Skip to content

feat(documents): Create-from entry points, including a highlighted selection (#14) - #125

Merged
Adron merged 1 commit into
parity/queuefrom
issue/14-materialize-docs-entry
Sep 16, 2026
Merged

Adron merged 1 commit into
parity/queuefrom
issue/14-materialize-docs-entry

Conversation

@Adron

@Adron Adron commented Sep 16, 2026

Copy link
Copy Markdown
Member

Closes #14. Part of epic #10. Opens #15's shared window.

What changed

  • + Create on every document browser row and in the editor — a CreateFromMenu offering the
    web's own four labels (To List / To Doc / To List & Doc / To Message), opening Create from…: the preview / edit / confirm window #15's
    MaterializeWindow with a MaterializeSource.Document(documentId).
  • Selection action in the editor — the body field now edits a TextFieldValue, so a highlighted
    range is known. While something is highlighted, a "Selection · + Create" bar opens the same window
    with MaterializeSource.DocumentSelection(documentId, markdown) (wire kind docElements).
  • Document → list and document → message are pure functions in domain/DocumentElements.kt:
    documentListRows() (one row per heading and bullet, falling back to every parsed block when a
    document has neither) and markdownToPlainText() (paragraphs kept, bullets marked , inline
    markdown flattened, code blocks dropped — fenced and indented).

The element-identity caveat resolved — and the open question from #15 answered

create-from.txt states docElements carries documentId + markdown: string: a selection has no
id, the markdown is its identity, which is exactly how #11 already models it. Nothing was invented.

Corroborated against the live web client — the public JS bundle contains the real Create-from window,
which supplied what neither the help centre nor the OpenAPI spec states:

  • default columns for document/docElements are Section / Text / Type, with sourceKey
    section / text / type
    — so no source key here is a guess. (This is the question Create from…: the preview / edit / confirm window #15 left
    open for the entry-point issues; it is now answered for documents.)
  • rows = elements filtered to heading|list-item, falling back to all elements;
  • the markdown→plain-text rules, ported directly;
  • title / relativePath defaults (Copy of X, slug-<id8>.md, X (selection)).

Nothing was written to the account — the authed probe was blocked in that environment.

:core:materialize — three additive, defaulted changes

MaterializePreview.documentMarkdown (a source that already is a document is previewed verbatim
rather than re-rendered as bullets-from-rows), MaterializePreview.draftBody, and
MaterializeWindowViewModel.reset()without reset(), dismissing and reopening the same launch
resurrected the previous success pane
, because start() deliberately keeps edits across
recomposition.

Deviations worth knowing

  • Indented code blocks: the web parser only handles fenced; the issue asked for both, so this
    drops indented ones too (CommonMark-ish — it must start a block, so nested bullets and list
    continuations are unaffected).
  • Create from…: the preview / edit / confirm window #15's window has one title field where the web has separate list/doc titles, so the suggestion
    varies by the destination the menu picked (To Doc → "Copy of …").
  • The draft body is display-only: ToMessageDraft still sends no content, so the server builds and
    sizes the real body.

Verification

./gradlew :app:assembleDebug testDebugUnitTest → BUILD SUCCESSFUL, 1391 tests, 0 failures
(:feature:documents 218, :core:materialize 69, both re-run with --rerun-tasks).

New: DocumentElementsTest (18 — heading/bullet rows including nested bullets, mixed content, a
document with neither; plain text preserving paragraphs, dropping fenced/tilde/unterminated/indented
code, bullets, inline flattening), DocumentMaterializeLaunchTest (9), SelectionPinTest (6 — a
tap that blurs the field must not steal the selection out from under the action)
, plus editor and
browser ViewModel cases. Compose entry-point tests compile, not executed (no emulator).

Follow-ups

  • Opening a created list from a document is a no-op: DocumentsRoute/DocumentEditorRoute expose
    onOpenList: (String) -> Unit = {}, but wiring it needs the app NavHost — out of scope here.
  • The editor previews unsaved text while the server materializes the saved document (documented in
    KDoc). A selection is unaffected, since its markdown is sent.
  • Column sourceKeys match the web client exactly but were not confirmed against a live
    POST /api/materialize response; a server-side rename would show empty cells rather than an error.

…lection

+ Create on every document browser row and in the editor opens the shared
preview/edit/confirm window on a `document` source, and highlighting markdown
in the editor offers the same four destinations for a `docElements` source —
the document id plus the selected passage, which is the only identity a
selection has.

Both entry points only ever send ids: the server re-fetches and re-authorizes
the document and derives the result from its own copy. Everything the window
shows is seeded locally so the mapping is visible before anything is created:

- Document → list: each heading and bullet point becomes its own row, previewed
  through the Section / Text / Type columns the server derives them into. A
  document with neither falls back to the blocks it does have rather than
  previewing an empty list.
- Document → message: markdown flattened to plain text with paragraphs kept and
  code blocks — fenced and indented — dropped. Shown as a preview only; the body
  the composer receives is still built and size-checked server-side.

Both conversions are pure functions (`documentListRows`, `markdownToPlainText`)
so the rules are pinned by unit tests rather than through the UI.

Additive changes in :core:materialize, kept to what the document entry points
need: `MaterializePreview` gained `documentMarkdown` (a source that already is a
document is previewed verbatim instead of re-rendered from rows) and `draftBody`
(the message destination shows the draft before it is asked for), and the window
ViewModel gained `reset()` so closing the window ends the flow — it keeps the
edits across recomposition, so only the host can say when a flow is over.

Closes #14
@Adron
Adron merged commit 27fd7a7 into parity/queue Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant