16 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
54e1148b39 |
feat(doc): round-trip tabular clipboard payloads with RFC-4180 quoting
Some checks failed
repo hygiene / hygiene (push) Has been cancelled
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
doc-engine / engine (push) Successful in 16s
doc-engine / consumer (push) Successful in 3m58s
Cells holding tabs, newlines, CRs, or quotes re-distributed across the
grid on a copy/paste cycle — the documented caveat of every tabular
clipboard milestone. This closes it on both sides:
- table_grid_tsv quotes such fields on the way out (wrapped in double
quotes, inner quotes doubled) via a new quote_tabular_field helper;
plain and empty fields stay raw, so payloads stay byte-compatible
with spreadsheets and plain text editors. The cell-range payload and
the block-span document payload share the one builder, so both
inherit the quoting at once.
- paste_table_payload replaces its split('\n')/split('\t') walk with
split_tabular_payload, an RFC-4180-style tokenizer: quotes open only
at field start (mid-field quotes are literal), doubled quotes read
as one, tabs/newlines/CRs inside quotes are literal field text,
CRLF rows outside quotes keep their tolerance, an unterminated
quote reads to the end as best effort, and a single trailing
newline adds no phantom row (a deliberate empty row survives).
- Caret parking, no-op skipping, empty-field clears, and the one-undo
grouped write semantics of the raw paste milestone are unchanged;
the caret offset in a multi-line value counts the newline too.
Tests: unit coverage for the writer and the tokenizer (every quoting
rule plus the quote/split round-trip property), runtime coverage of
copy quoting, paste restoring embedded tab/newline values verbatim
with one-undo and redo, and an end-to-end copy-cut-paste cycle; a
doc-engine materialize test pins special-character cell text
surviving peer sync and undo/redo verbatim. README caveats updated
and the milestone documented.
|
||
|
|
4df2193859 |
docs(doc): retire stale in-cell paste and migration-status claims
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
doc-engine / engine (push) Successful in 15s
doc-engine / consumer (push) Has been cancelled
Tabular paste superseded the clipboard section's "in-cell pastes always stay a single whole-cell write, newlines included", and the doc-engine README still framed the controller migration as "next" even though the workspace navigation already runs on CrdtDocEditor (the classic editor stays registered as a fallback). Found during the phase audit. |
||
|
|
c5c1e0a3e9 |
feat(doc): paste tabular payloads across table cells
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
doc-engine / engine (push) Successful in 15s
doc-engine / consumer (push) Successful in 3m55s
A TextInput carrying tabs or newlines while the caret lives in a table now pastes the spreadsheet way — one cell per tab stop, one row per line — starting at the caret cell, or at the armed range's normalized top-left (consuming the range like any paste-over- selection). The distribution rides the grouped set_table_cells from the previous milestone so the rectangle un-pastes in one undo step, and the caret parks at the last cell the payload touched. Rows or columns past the table edge clip, CRLF strips per line like text-block pastes, empty fields clear their targets, and unchanged cells are skipped so a paste lands neither redundant LWW ops nor dead group members. Plain payloads keep the whole-cell char splice, and cells still never spawn blocks. The pre-existing in-cell paste test asserting the old "newline stays embedded in the cell" behavior is rewritten to the distribution semantics; new runtime tests cover the 2x2 distribution with caret parking and the one-undo/redo round trip, edge clipping without wrap-around, backward-spanned ranges pasting from their top-left, CRLF with empty-field clears, and the mobile menu's Paste action distributing from the pressed cell. Engine integration coverage replays a grouped multi-cell write into a peer and asserts the projections converge. |
||
|
|
08c3eed18d |
feat(doc): copy and clear cell ranges through the clipboard
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
doc-engine / engine (push) Has been cancelled
doc-engine / consumer (push) Has been cancelled
An armed table cell range now copies as tab/newline text (rows top to bottom, cells left to right, the spreadsheet convention) through the same copyable_selection_text the TextCopy/TextCut hits answer with, so the long-press cell menu floats the full action set instead of a paste-only one. Cut and Backspace/Delete clear every non-empty spanned cell via the engine's new set_table_cells, which pops each sub-write's compensation into one Compensation::Group — the span un-clears in a single undo step where Backspace previously dropped the range and edited only the caret cell. Single writes keep the leaf compensation and empty write lists record nothing. Grouping cell writes surfaced a real undo asymmetry: an undo after a redo re-applied the redone cell text, because redo pushed the write's own text as its undo compensation (inverse() only swaps the verb, keeping the stale text) while undo's leaf special-case captured live cell state only in one direction. undo()/redo() now resolve cell-text inverses per compensation member against the projected text at both transition boundaries (redo_compensation/ undo_compensation), so leaves and groups round-trip through arbitrary cycles and the "cells stay out of groups" restriction is gone. |
||
|
|
920827a440 |
feat(doc): split multi-line paste into blocks with one-step undo
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
doc-engine / engine (push) Has been cancelled
doc-engine / consumer (push) Has been cancelled
A pasted TextInput payload containing newlines used to land as a single run of text with literal line feed characters. It now splits block-per-line like a desktop editor: line 0 splices into the caret block, middle lines become sibling blocks inheriting the caret block's kind, and the trailing SplitBlock carries the caret block's suffix onto the last pasted line (ab|XY pasted with "l0\nl1\nl2" yields abl0 / l1 / l2XY). The caret parks after the last pasted atom. The whole paste folds into one Compensation::Group (undo applies group members in reverse, redo applies the inverse group in authored order), so N lines retract in a single Ctrl+Z. The caret anchor may be tombstoned (the caret a selection delete leaves behind): the new CrdtDocument::live_offset_of resolves it to the same live-text offset RGA splicing uses, so pasting over a deleted selection lands exactly where a single-line paste would. CRLF payloads strip their \r. Surfaced and fixed at the source while building this: the synthetic block a SplitBlock opens was a text dead end -- atoms and style ops addressed to a split op id landed in the op log but evaporated from the projection, and the child always spliced directly behind its parent. Split children are now first-class materialization targets: suffix atoms re-link head-to-tail (keeping ids and inherited style runs), child-addressed atoms splice in RGA-wise, and the child's splice skips the parent's real-chain descendants, keeping the middle paste lines ahead of it. Runtime tests cover the split/suffix/caret/undo/redo cycle, paste-over-selection as two undo steps, and the in-cell newline guard. Engine tests cover grouped-undo chronology, tombstone anchors, empty middle lines, CRLF, table refusal, peer convergence, and the synthetic-child text/style/order regressions underneath. |
||
|
|
7e0012b866 |
feat(doc): undo cross-block cuts losslessly via range tombstones
Some checks failed
repo hygiene / hygiene (push) Has been cancelled
doc-engine / engine (push) Has been cancelled
doc-engine / consumer (push) Has been cancelled
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
ReplaceBlockRange pushed no undo compensation, so a multi-block cut or selection delete could never be undone: the range op rewrites only the projection (drained blocks and every atom beneath the span stay in the op log untouched), and no op existed to make it stop applying. Add the CancelBlockRange/RestoreBlockRange op+compensation pair, resolved chronologically per target like every other tombstone pair (merge splits, cell styles, blocks, text, rows, columns). Undo of a cross-block delete tombstones the range op itself, which is lossless: splices, middle blocks and their texts all re-materialize exactly from the untouched log, in a single undo step. Redo clears the tombstone. The controller also refuses spans whose endpoints do not resolve against the projection in order, so a no-op range never strands an entry on the undo stack. Coverage: engine tests for the one-step undo/redo cycle (with a double undo proving the chronological tombstone), text typed beneath a cancelled range surviving, and unresolved-span refusal growing no undo stack. Widget runtime test cuts across three blocks, asserts the joined payload, then Ctrl+Z restores all three blocks and Ctrl+Shift+Z re-cuts. |
||
|
|
a3ff59e512 |
feat(doc): clipboard copy/cut/paste across blocks and table cells
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
doc-engine / engine (push) Has been cancelled
doc-engine / consumer (push) Has been cancelled
The CRDT editor had no clipboard surface at all. It now answers the platform clipboard queries exactly like makepad's TextInput: - Hit::TextCopy / Hit::TextCut fill the response cell with the selection payload: the text-block selection joined with newlines, or the in-cell character span when the caret lives in a table. Nothing selected leaves the response empty, so the platform keeps the clipboard alone. - Cut removes the payload through the shared selection-deletion path and paste flows through the existing TextInput insert path (replace- selection semantics for both blocks and cells). Three latent production bugs uncovered and fixed at the source: 1. delete_selection's applied flag was wired to the empty replacement's missing trailing atom, so same-block Backspace-over-selection over-deleted by one char and left live anchors behind. 2. engine delete_text pushed no undo compensation, making every selection deletion (cut, type-over, Backspace-over) unrecoverable. It now pushes the symmetric RestoreText pair. 3. All six engine tombstone pairs (text, blocks, rows, columns, merge splits, cell style clears) resolved as union-minus-union: once a restore op existed, delete→undo→redo could never re-delete. They now resolve chronologically per target in operation order. 13 new tests: 5 clipboard runtime (payload, cut+undo, cell span, no-payload, newline join), 2 widget regressions (over-delete, delete-key), 6 engine (delete undo/redo, empty-replace single-step, block/row/merge/cell-style chronology cycles). nigig-build 704 -> 711, doc-engine 62 -> 68. |
||
|
|
df2471105b |
feat(doc): CRDT-native table cell style runs
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
doc-engine / engine (push) Has been cancelled
doc-engine / consumer (push) Has been cancelled
Cell text was plain LWW strings, so style toggles were silently disabled whenever the caret sat in a table. Cells now carry styled runs end to end: - Engine: new `SetTableCellStyle` / `ClearTableCellStyle` / `RestoreTableCellStyle` ops. Ranges are char-offset snapshots replayed over the settled LWW cell text in operation order (newer patches win on overlap, spans clamp when text shrinks but do not stretch when it grows). Undo tombstones the style op exactly like a merge split, keeping overlapping later styles intact and undo/redo convergent across peers. `ProjectedTable::cell_runs` mirrors `cells` — every projected cell has at least one default-styled run — built by the new `apply_style_offset_range` helper, with `TableCellRef` addressing cells in the controller API. - Layout: `ProjectedTableCellLayout.runs` mirrors the projection runs for the renderer. - Renderer: `draw_table_projection` draws cell text run by run through the same regular/bold/italic/bold-italic pens as block text, advancing on the shared fixed char grid. - Widget: `toggle_selection_style` routes by session — a parked cell cursor styles the whole cell (cell text has no selection ranges yet), a text selection styles its range — so Ctrl/Cmd+B/I/U and the toolbar buttons now work in tables. Undo/redo round-trips like any other op. 9 engine tests (split/merge/clamp/overlap/toggle/reject/undo/convergence), 5 widget tests (layout runs, Ctrl+B whole-cell, undo/redo, toolbar path, empty-cell no-op). doc-engine 53 -> 62, nigig-build 685 -> 690. The doc README's "cell text carries no style runs yet" gap is closed and the doc-engine projection invariants document the new semantics. |
||
|
|
27ff51ca47 |
fix(doc): hit_test yields to table/node hit tests; pointer and IME runtime coverage
Some checks failed
doc-engine / consumer (push) Has been cancelled
doc-engine / engine (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / supply-chain (push) Has been cancelled
ProjectionLayoutTree::hit_test's nearest-glyph fallback resolved any point to a glyph, so taps landing inside tables or advanced nodes never reached the widget's table_hit_test/node_hit_test branches — production cell taps silently moved the text caret instead of parking in cells. The fallback now returns None when the point is inside a table or node rect (those layers own their taps), while empty-space snapping beside text still places the caret. Close the "widget input, selection, mobile gestures" test gap with a draw-free Area::Rect stub harness (stub_hit_area): tests seed the input area a GPU frame normally fills and mirror the two platform pre-dispatch steps (first_mouse_button, committed key focus), so raw MouseDown/MouseMove and IME TextInput dispatch through event.hits against a real Cx — exactly like a running app. Covered end-to-end: hit-dispatch sanity, caret placement from a tap, shift-press selection extension, drag selection over glyphs, TextInput inserting at the caret and replacing an active selection, and a cell tap + TextInput whole-cell round trip. The harness also exposed the test fixture actor drifting from production's single "local" actor (cross-actor mid-run anchoring is deterministic but not chronological — now a documented doc-engine projection invariant); the runtime fixture now uses "local" for production fidelity. READMEs: doc runtime-tests section documents the stub harness and the two bugs it unearthed; roadmap item updated (only the renderer draw pass remains GPU/Studio-bound); doc-engine projection invariants gain the per-actor counter boundary bullet. 6 new tests, 675 -> 681. |
||
|
|
d303484c93 |
feat(doc): CRDT-native cell range selection and table merge/split
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
doc-engine / engine (push) Has been cancelled
doc-engine / consumer (push) Has been cancelled
Closes the roadmap's table merge command item on the CRDT editor surface with a rectangular cell-range selection model driven from the keyboard and the workspace toolbar: - ProjectionSession::cell_selection holds stable anchor/focus row and column ids; Shift+Arrow at a cell boundary starts the range and steps the focus cell on an active one, with table-edge clamping, caret follow, plain-action collapse, and undo-stale self-clearing. - table_cell_range normalizes the selection against the projected table; cell_selection_rects renders the highlight over visible cells only, so merged spans contribute one expanded anchor rect. - Ctrl/Cmd+M (merge_selected_cells, toolbar Merge) routes through the engine's MergeTableCells op; undo splits through the symmetric compensation. cell_range_mergeable rejects ranges overlapping an existing merge UI-side, where ambiguous nested spans belong. - Ctrl/Cmd+Shift+M (split_cell_at_cursor, toolbar Split) resolves the containing merge via merge_at_cell even from a covered cell and reveals the preserved hidden text; undo re-merges through RestoreTableMerge. - Workspace toolbar gains Merge/Split buttons with status-line guidance (the only split path on touch devices). Tests: 9 new (4 pure layout: range normalization/staleness, mergeable rules, merge_at_cell, covered-skip highlight rects; 5 runtime on a real Cx: shift-span + merge + undo, edge clamp, split + re-merge, plain collapse, overlap rejection) for 669 total in nigig-build. The doc-engine README gains the merge/split materialization invariant. |
||
|
|
9a167f4df3 |
fix(doc-engine): honor after anchors when ordering table rows and columns
InsertTableRow/InsertTableColumn carried an after anchor on the wire but materialization ignored it, ordering the row/column vectors by op id only; an 'insert below X' could land anywhere once ids diverged. Rows and columns now materialize over the anchor chain with RGA-style sibling order (counter descending, actor ascending), matching text atoms, so a newer insert below an existing row renders right after it and peers converge on the same grid order. |
||
|
|
24d9f9269e |
feat(doc): CRDT-native keyboard editing with block split/merge ops
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
doc-engine / engine (push) Has been cancelled
doc-engine / consumer (push) Has been cancelled
CrdtDocEditor now handles the desktop keyboard surface natively through doc-engine operations: Ctrl/Cmd+Z/Shift+Z undo-redo via CrdtHistory, Ctrl/Cmd+B/I/U selection style toggles, arrow-key caret moves with shift-extend across block boundaries via the step_glyph stream, selection-aware Backspace/Delete with block-boundary merges, Return splitting paragraphs or appending table rows, and TextInput that replaces the active selection at the session caret block. Engine additions: Operation::SplitBlock/MergeBlocks with symmetric Compensation pairs; materialization keeps a split parent's trailing runs as a synthetic child spliced after the parent so peers converge without a new InsertBlock op; merge_runs coalesces equal style runs. |
||
|
|
7bedc677a6 |
feat(doc): render CRDT-native advanced nodes in CrdtDocEditor
Some checks failed
doc-engine / engine (push) Has been cancelled
doc-engine / consumer (push) Has been cancelled
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
- layout_projection walks the unified DocumentProjection::order so advanced nodes interleave with paragraphs and tables at their anchor positions; block_origins are assigned by index for both paths, with a blocks-only fallback for projections assembled without the op log - projected_node_metrics mirrors legacy AdvancedLayout heights, labels and interactivity per kind (unknown kinds follow the bridge's EmbeddedWidget mapping: 'Widget: <kind>') - ProjectionRenderer::draw_node_projection reuses the legacy draw_advanced_blocks colors; dividers collapse to a centered 1px line; new draw_node_fill/draw_node_border live fields on CrdtDocEditor - fix(doc-engine): the after-chain was block-only, so any block anchored after an advanced node was unreachable during materialization and vanished from the projection; nodes now participate as chain connectors - tests: unified-order interleaving, node metrics, node hit test, mixed table/node stacking, order-less fallback (nigig-build); node-anchored block materialization and mixed sibling ordering (doc-engine) |
||
|
|
7143b3e798 |
feat(doc): render CRDT-native tables in CrdtDocEditor
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
doc-engine / engine (push) Has been cancelled
doc-engine / consumer (push) Has been cancelled
- projection_layout builds ProjectedTableLayout geometry (fixed 160x28 cells matching the legacy bridge width, merge spans with covered-cell flags) and per-block origins so blocks after a table clear it instead of using the fixed line step - ProjectionRenderer draws the table grid through a new draw_table_border live field and consumes layout block origins for text placement - table_hit_test maps points to merge-anchor cells for future cell editing - doc-engine: split the cramped style-patch line clippy flagged as possible_missing_else; the crate is clippy-clean again - README: tick verified CRDT bridge boxes (dependency, wiring, selected replacement/formatting/table/toolbar migration, font size/color migration) and document the table milestone - CI: add doc-engine workflow (engine tests + clippy -D warnings + nigig-build consumer check/test) and trigger nigig-build CI on doc changes |
||
|
|
b5471e32e3 |
fix(cad): make the crate buildable, testable and safe to ship
Some checks failed
nigig-build (CAD) / supply-chain (push) Has been cancelled
nigig-build (CAD) / cad-module (push) Has been cancelled
nigig-build (CAD) / full-crate-check (push) Has been cancelled
PDF engine / engine (push) Has been cancelled
PDF engine / makepad-integration (push) Has been cancelled
PDF engine / fuzz (push) Has been cancelled
Phases 0-2 of CAD_ASSESSMENT_AND_PLAN.md. The crate did not compile and no
test had ever run; it now builds clean with a green suite.
Build and CI (Phase 0)
- Pin all 33 git dependency manifests to an explicit rev. A branch
dependency re-resolves on every build and is a code-execution path into
CI if force-pushed.
- Commit Cargo.lock (540 packages). Producing it required fixing three
resolution failures the workspace had always had: a non-existent
makepad-widgets feature, two rusqlite versions both linking sqlite3, and
four missed CellId call sites in spreadsheet-ui.
- Add .forgejo/workflows/nigig-build.yml.
- Replace five stale CAD docs that contradicted the code with one
ARCHITECTURE.md; add PHASE0/1/2_STATUS.md and TEST_BASELINE.md.
Correctness (Phase 1)
- Rotation units: transform_point bound sin_cos() backwards, transposed X
and Z, and applied axes in reverse order, so every exported STL was wrong
even at zero rotation. It now shares the renderer's matrix helpers.
- GLB quaternions had norm 0.125 (half-angle applied to cos/sin, degrees
read as radians) - invalid per the glTF spec.
- PDF wall/door/window yaw fed degrees to cos/sin.
- Fix a TOCTOU unwrap in touch picking; viewport.rs now has no unwrap().
- CommandContext gains update_node/insert_node_at/node_index: resize and
modify were delete+create, silently moving nodes to the end of the scene.
- Wire MAX_UNDO_LEVELS (defined, exported, never read) and switch the undo
stack to VecDeque; this also made the existing drag-merge logic reachable.
- CadNode::size() returned a fake 1x1x1 for CSG and extruded solids, making
them unpickable outside a 1x1x1 box at their origin.
- Reject non-finite script input; makepad_csg clamps NaN rather than
propagating it, so bad input produced silently wrong geometry.
Test baseline: 0 -> 722 passing, 0 failing
- 17 pre-existing failures fixed: 10 real defects (dependency-cycle
detection, over-allocation of unassigned tasks, quote/backslash
corruption on save, default rooms lost for all but the first region,
RGA text ordering) and 7 tests that were themselves wrong, each checked
against its production caller first.
Security (Phase 2)
- env!("CARGO_MANIFEST_DIR") was used as a runtime path in three places,
including as the AI agent's working directory. All runtime data now goes
under app_data_dir().
- Remove the hardcoded LAN LLM endpoint. It is now opt-in via
NIGIG_CAD_LOCAL_OPENAI_URL/_MODEL and refuses plaintext HTTP to anything
but loopback.
- Bound and content-sniff AI image attachments (8 MB cap, magic bytes);
the MIME type came from the filename extension.
- Escape SVG/HTML output, and add SRI to the exported viewer's script tag.
The pinned model-viewer@3.5.1 does not exist, so every exported viewer
was silently broken; now 4.0.0 with a verified hash.
- Stop embedding $USER in exported PDFs and logging document content in
release builds.
- CI now rejects reintroducing the runtime-path and hardcoded-endpoint
classes; both gates were verified to fail on a reintroduced defect.
Add system_prompt.md and embed it with include_str!. The file was missing
from the repository, so the agent silently used a one-line fallback.
|
||
| cc05abdc71 | Initial commit |