Skip to content
sysout.dev

asciitor 1.4 Refinements

Planned features and UX refinements for asciitor 1.4.

1. Core refinements

1.1. Automatic Title and Name Generation

New documents currently use Untitled as both the app bar title and the initial name offered during the first save.

Before the document is saved for the first time, use its first level-one heading as the app bar title and as the suggested filename in the system create-document dialog. Continue to use Untitled when no level-one heading is available. Sanitize characters that are not valid in filenames and add the extension for the active document format.

The suggested name applies only to the first save. After the document has been saved, later title changes must not rename the file. asciitor does not need to perform a separate duplicate-name check: its storage access may not include enough directory visibility, and the selected Android document provider remains responsible for resolving or rejecting the chosen name.

1.2. Recently Opened and External Documents

Decision

Do not add externally launched documents to Recently Opened.

Documents selected through asciitor’s Open picker are recorded in Recently Opened. The Android document picker can provide durable URI access, allowing asciitor to reopen those entries in a later session.

Documents launched externally from Downloads or another file manager, through an app association or the Android Open with chooser, may instead use a private FileProvider URI with only temporary access. asciitor can read such a document during the original launch but cannot reliably retain permission after that activity finishes. Persisting the temporary URI produces a Recent entry that may fail with a permission error when reopened.

A Recent entry should be a reliable promise that the document can be opened again without asking the user to locate it. Therefore, do not record externally launched documents in Recently Opened. Users who want durable Recents access should select the document through asciitor’s Open action. Do not work around this limitation by storing a private copy, because that copy would no longer represent the original document or its save destination.

1.3. External App Navigation

When a document is opened from Downloads or another file manager, pressing the app bar back arrow or performing the Android back gesture currently navigates to the asciitor home screen.

When asciitor was launched to view an external document, Back should finish that document task and return the user to the originating app. Normal in-app document navigation should continue to return to the asciitor home screen.

1.4. Keep the Keyboard and Cursor in Sync

When the user taps near the bottom of the document in Edit mode, the keyboard can cover the cursor. Sometimes the editor instead scrolls to the top, making the editing position difficult to locate.

When the keyboard opens or changes size, keep the active cursor and its current line visible above the keyboard without unexpectedly resetting the editor’s scroll position.

1.5. Autosave

Autosave dirty changes in documents that already have a writable destination. A new document must still be saved manually the first time so the user can choose its destination and filename.

Use a trailing debounce: save two seconds after the most recent edit rather than writing after every keystroke. If more edits arrive, restart the timer. Also request an immediate save when asciitor leaves the foreground with dirty changes, such as when the user presses Home, switches apps, locks the device, or opens another activity. This lifecycle save is a safety flush for a pending debounce, not the primary autosave mechanism.

Autosave must have the same persistence semantics as manual save. Keep the dirty indicator visible while a write is pending, and clear it only after persistence is confirmed. Serialize saves so that an older write cannot complete after and overwrite a newer one.

Use the same persistence and recovery pipeline for autosave and manual Save, while allowing the warning prefix to identify which action failed. Retry a transient document-provider failure once before warning the user. Do not automatically retry permission loss, a missing destination, full storage, or an unknown failure.

If autosave fails, retain the dirty state and show a clear warning that the changes are not saved. Offer manual Save to retry the current destination and Save As where choosing a new destination can recover the document. Do not repeatedly display the same warning after every edit while the same unresolved failure remains.

Expected failure categories include:

  • A temporarily unavailable document provider, cloud connection, removable volume, or transient I/O error. These may succeed when retried.

  • Full storage or a provider quota limit. The user can free space or select another destination.

  • Revoked permission, a read-only destination, or a provider that no longer permits writing. The user may be able to restore access; otherwise Save As is required.

  • A destination that was moved or deleted, or an invalid document URI. The existing destination cannot be repaired by asciitor; use Save As to preserve the current content.

  • Provider authentication or account errors. The user must resolve them in the provider before retrying, or save elsewhere.

Manual Save may encounter the same underlying error as autosave. Error messages should therefore identify the likely category and, where possible, tell the user whether to retry, restore access, free space, reconnect the provider, or use Save As.

1.6. Clean Up Stale Recently Opened Entries

A document previously selected through asciitor’s Open picker remains in Recently Opened if it is later renamed, moved, or deleted outside asciitor. Its stored URI may then be stale, and opening it currently produces an overly technical error.

Show a user-friendly message explaining that the document is no longer available at its previous location, then remove the stale entry from Recently Opened. Do not treat a temporary provider, network, authentication, or removable-storage failure as proof that an entry is stale; retain the entry when availability may be restored.

1.7. Open the App Drawer with a Content Swipe

A left-to-right swipe beginning within the main content area opens the app drawer on non-document screens and while editing a document.

Keep this drawer gesture separate from Android’s system Back gesture at the screen edge. Disable it in Preview mode because the native WebView can misclassify vertical document scrolling as a drawer swipe. The gesture remains available in Edit mode.

2. Document Toolbar and Focus Mode

Replace the separate Preview and Edit tabs with a single mode action whose icon communicates the destination:

  • In Preview mode, show an Edit icon that switches to Edit mode.

  • In Edit mode, show a Preview icon that switches to Preview mode.

Use the space freed by the tabs for a document toolbar containing the mode action, Save, Export, and a Focus mode toggle.

The Focus mode action should use a square or four-corner full-screen icon similar to the convention used by Material React Table. Activating it should switch to an inverse exit-full-screen icon; activating that icon should return to normal mode. The exact Material icon and Android behavior need to be explored and validated.

In Focus mode, hide the normal app chrome and Android system bars while keeping the document toolbar available. Extend the current Edit or Preview background edge to edge so the stronger app chrome color does not distract from the document. An edge swipe may reveal transient system bars over the content. The keyboard remains available, Android Back exits Focus mode before closing the document, and Focus mode survives orientation and configuration changes. Restore the normal app and system chrome when Focus mode ends or the document closes.

3. Later explorations

The following features can be considered after the core refinements and document-toolbar work.

3.1. Synchronize Edit and Preview Positions

Keep Edit and Preview near the same part of the document when switching modes. Opening Preview scrolls to the rendered block nearest the edit cursor’s source line. Returning to Edit places the cursor at the source line of the first visible rendered block and aligns the editor viewport with it.

The mapping uses the renderer’s block-level source locations. When rendered content has no source location, preserve the approximate position using the document’s proportional scroll position.

3.2. Syntax Highlighting

Highlight AsciiDoc and Markdown syntax in Edit mode using Compose’s state-based output styling. Detect the syntax from the saved or opened filename; treat new and unknown-format documents as AsciiDoc, and leave plain-text files unstyled. This does not change Preview-mode source-block rendering, which remains handled by Asciidoctor and highlight.js.

Use a registry of dependency-free, format-specific line scanners for headings, structural markers, links and macros, inline and block literals, and comments. Styling must not modify the stored text, cursor offsets, selection, IME composition, or accessibility text. Use contrast-checked colors in light and dark themes. Skip highlighting when a document exceeds 200,000 characters so large-document editing remains responsive. Additional formats can be added by registering their extensions and scanner without changing the editor UI.

3.3. HTML Export

Provide PDF and HTML under one Export as dialog. PDF continues through Android’s print mechanism. HTML uses Android’s document creator and exports the current in-memory document as static, already-rendered HTML using the exact Preview theme active at export time.

The HTML file contains no Asciidoctor runtime dependency. Inline the Preview stylesheet, active colors and typography settings, selected Preview font, and code font. Preserve document-linked images as authored: data-URI images remain embedded, web images retain their URLs, and relative or local images retain their paths. The app does not request broader folder access to collect sibling assets.