fix: recognise Final Draft files by their content

load() chose the parser from the extension alone, so a .txt that is
really FDX was parsed as Fountain. The .fdx extension still wins, and
other names are sniffed for the FinalDraft root element.
This commit is contained in:
Kharec committed 2026-10-15 17:58:46 +02:00
1 parent 6622203506
commit 764c9d4dc3
2 files changed
+13 -1

No files matched your search

+2
View File
@@ -17,6 +17,8 @@ version is in the `VERSION` file.
- `plotzilla new-script.fountain` opens a new script that will be saved to that file, - `plotzilla new-script.fountain` opens a new script that will be saved to that file,
instead of an untitled document that forgot the name it was given. instead of an untitled document that forgot the name it was given.
- A Final Draft file named `.txt` (or anything but `.fdx`) is recognised by its
content and opened as FDX, not parsed as Fountain.
- The unsaved-changes marker is now updated atomically: an edit landing while a save - The unsaved-changes marker is now updated atomically: an edit landing while a save
finished could make the close confirmation unreliable. finished could make the close confirmation unreliable.
- Saving is now durable: the file is synced to disk before it replaces the old one (a - Saving is now durable: the file is synced to disk before it replaces the old one (a
+11 -1
View File
@@ -118,7 +118,7 @@ func (a *App) load(path string) (*OpenResult, error) {
return nil, err return nil, err
} }
var doc *screenplay.Document var doc *screenplay.Document
if strings.EqualFold(filepath.Ext(path), ".fdx") { if isFDX(path, data) {
if doc, err = fdx.Read(bytes.NewReader(data)); err != nil { if doc, err = fdx.Read(bytes.NewReader(data)); err != nil {
return nil, err return nil, err
} }
@@ -128,6 +128,16 @@ func (a *App) load(path string) (*OpenResult, error) {
return &OpenResult{Path: path, Document: doc}, nil return &OpenResult{Path: path, Document: doc}, nil
} }
// isFDX reports whether a file is a Final Draft one: the extension says
// so, or its content does — a ".txt" export of Final Draft is still FDX.
func isFDX(path string, data []byte) bool {
if strings.EqualFold(filepath.Ext(path), ".fdx") {
return true
}
head := strings.TrimPrefix(string(data[:min(len(data), 512)]), "\ufeff")
return strings.Contains(head, "<FinalDraft")
}
// Save writes the document to path, asking for one when empty. It returns // Save writes the document to path, asking for one when empty. It returns
// the path written, or "" if the user cancelled. // the path written, or "" if the user cancelled.
func (a *App) Save(doc screenplay.Document, path string) (string, error) { func (a *App) Save(doc screenplay.Document, path string) (string, error) {