feat(kc): Metadata Editor — schema-driven form with dirty tracking and save-back #39

Closed
opened 2026-02-18 21:15:54 +00:00 by forbes · 0 comments
Owner

Summary

Implement the Metadata Editor viewer — a schema-driven editable form that surfaces silo/metadata.json fields (custom schema fields, lifecycle state, tags) in an MDI viewport tab. This is the most complex viewer widget and introduces the dirty tracking and save-back pipeline used by all editable viewers.

Background

silo/metadata.json contains user-customizable fields defined by a Silo schema (e.g., mechanical-part-v2). The editor renders fields dynamically based on the schema descriptor from GET /api/schemas/{name}/form. When offline, it falls back to a flat key-value display.

The existing mods/silo/freecad/schema_form.py already implements dynamic form building with collapsible groups — this should be reused or adapted.

Widget specification

┌──────────────────────────────────────────────────┐
│  Part Metadata                     [lifecycle ▾]  │
├──────────────────────────────────────────────────┤
│  Schema: mechanical-part-v2                      │
│  Tags:   [structural] [aluminum] [+]             │
│                                                  │
│  ── Identity ──────────────────────────────────  │
│  Category     [bracket         ▾]                │
│  Project      [PRJ-042         ▾]                │
│                                                  │
│  ── Material ──────────────────────────────────  │
│  Material     [6061-T6          ]                │
│  Finish       [anodized         ]                │
│  Weight (kg)  [0.34             ]                │
│                                                  │
│                           [Save]  [Reset]        │
└──────────────────────────────────────────────────┘

Layout

  • QScrollArea containing QGroupBox sections
  • Lifecycle state: QComboBox in header bar
  • Tags: horizontal flow of removable chip widgets with add button
  • Field sections driven by schema descriptor; flat key-value fallback if unavailable

Field type mapping

Schema field type Qt widget
string QLineEdit
enum QComboBox
number QDoubleSpinBox
boolean QCheckBox
text (multiline) QPlainTextEdit

Form dimensions

  • Input fields: 32px height, 8px internal padding, 12px vertical gap
  • Matches Silo web UI settings panel proportions

Dirty tracking and save-back

This phase introduces the modification tracking system:

  1. Per-object dirty flag: SiloViewerObject gets a _dirty attribute set by the viewer widget on any edit.
  2. Save button: Writes changes to obj.RawContent and clears the dirty flag.
  3. Reset button: Reverts to last-saved obj.RawContent state.
  4. Document save integration: In the pre_reinject callback (added in Phase 1), dirty objects have their RawContent serialized back into the silo/ cache dict before ZIP write.
  5. Unsaved changes warning: If the MDI subwindow is closed with unsaved changes, prompt the user.

Offline behavior

  • Schema descriptor unavailable → fall back to flat key-value QLineEdit display for all fields
  • Field edits are permitted offline per KC_SPECIFICATION.md
  • Lifecycle transitions require server confirmation — QComboBox is disabled when offline
  • Changes reconciled on next commit

Files to modify

File Change
src/Mod/Create/silo_viewers.py Add SiloMetadataEditor widget class
src/Mod/Create/silo_objects.py Add _dirty flag, mark_dirty(), is_dirty() methods
src/Mod/Create/kc_format.py Ensure pre_reinject callback writes dirty metadata back to ZIP cache

Acceptance criteria

  • Double-clicking "Metadata" opens an editable form in a viewport tab
  • Fields render correctly based on schema descriptor (when connected)
  • Fields fall back to flat key-value QLineEdit display when offline
  • Lifecycle QComboBox is disabled when offline
  • Tags display as removable chips with an add button
  • Editing any field marks the object as dirty
  • Save button writes to obj.RawContent and clears dirty flag
  • Reset button reverts all fields to last-saved state
  • On document save, dirty metadata is written back to silo/metadata.json in the ZIP
  • Close-with-unsaved-changes prompts the user
  • Round-trip: edit metadata → save document → reopen → metadata reflects edits

Dependencies

  • Phase 1 foundation (#37)
  • Phase 2 Manifest Viewer (#38) — for the create_viewer_widget() factory and MDI pipeline

References

  • docs/KC_SPECIFICATION.md §4.2 (silo/metadata.json schema)
  • docs/SILO_VIEWPORT_PLAN.md Phase 3
  • Silo Metadata Viewport Specification §5.2 (Metadata Editor)
  • mods/silo/freecad/schema_form.py — existing dynamic form builder
## Summary Implement the Metadata Editor viewer — a schema-driven editable form that surfaces `silo/metadata.json` fields (custom schema fields, lifecycle state, tags) in an MDI viewport tab. This is the most complex viewer widget and introduces the dirty tracking and save-back pipeline used by all editable viewers. ## Background `silo/metadata.json` contains user-customizable fields defined by a Silo schema (e.g., `mechanical-part-v2`). The editor renders fields dynamically based on the schema descriptor from `GET /api/schemas/{name}/form`. When offline, it falls back to a flat key-value display. The existing `mods/silo/freecad/schema_form.py` already implements dynamic form building with collapsible groups — this should be reused or adapted. ## Widget specification ``` ┌──────────────────────────────────────────────────┐ │ Part Metadata [lifecycle ▾] │ ├──────────────────────────────────────────────────┤ │ Schema: mechanical-part-v2 │ │ Tags: [structural] [aluminum] [+] │ │ │ │ ── Identity ────────────────────────────────── │ │ Category [bracket ▾] │ │ Project [PRJ-042 ▾] │ │ │ │ ── Material ────────────────────────────────── │ │ Material [6061-T6 ] │ │ Finish [anodized ] │ │ Weight (kg) [0.34 ] │ │ │ │ [Save] [Reset] │ └──────────────────────────────────────────────────┘ ``` ### Layout - `QScrollArea` containing `QGroupBox` sections - Lifecycle state: `QComboBox` in header bar - Tags: horizontal flow of removable chip widgets with add button - Field sections driven by schema descriptor; flat key-value fallback if unavailable ### Field type mapping | Schema field type | Qt widget | |-------------------|-----------| | `string` | `QLineEdit` | | `enum` | `QComboBox` | | `number` | `QDoubleSpinBox` | | `boolean` | `QCheckBox` | | `text` (multiline) | `QPlainTextEdit` | ### Form dimensions - Input fields: 32px height, 8px internal padding, 12px vertical gap - Matches Silo web UI settings panel proportions ## Dirty tracking and save-back This phase introduces the modification tracking system: 1. **Per-object dirty flag**: `SiloViewerObject` gets a `_dirty` attribute set by the viewer widget on any edit. 2. **Save button**: Writes changes to `obj.RawContent` and clears the dirty flag. 3. **Reset button**: Reverts to last-saved `obj.RawContent` state. 4. **Document save integration**: In the `pre_reinject` callback (added in Phase 1), dirty objects have their `RawContent` serialized back into the `silo/` cache dict before ZIP write. 5. **Unsaved changes warning**: If the MDI subwindow is closed with unsaved changes, prompt the user. ## Offline behavior - Schema descriptor unavailable → fall back to flat key-value `QLineEdit` display for all fields - Field edits are permitted offline per `KC_SPECIFICATION.md` - Lifecycle transitions require server confirmation — `QComboBox` is disabled when offline - Changes reconciled on next commit ## Files to modify | File | Change | |------|--------| | `src/Mod/Create/silo_viewers.py` | Add `SiloMetadataEditor` widget class | | `src/Mod/Create/silo_objects.py` | Add `_dirty` flag, `mark_dirty()`, `is_dirty()` methods | | `src/Mod/Create/kc_format.py` | Ensure `pre_reinject` callback writes dirty metadata back to ZIP cache | ## Acceptance criteria - [ ] Double-clicking "Metadata" opens an editable form in a viewport tab - [ ] Fields render correctly based on schema descriptor (when connected) - [ ] Fields fall back to flat key-value `QLineEdit` display when offline - [ ] Lifecycle `QComboBox` is disabled when offline - [ ] Tags display as removable chips with an add button - [ ] Editing any field marks the object as dirty - [ ] Save button writes to `obj.RawContent` and clears dirty flag - [ ] Reset button reverts all fields to last-saved state - [ ] On document save, dirty metadata is written back to `silo/metadata.json` in the ZIP - [ ] Close-with-unsaved-changes prompts the user - [ ] Round-trip: edit metadata → save document → reopen → metadata reflects edits ## Dependencies - Phase 1 foundation (#37) - Phase 2 Manifest Viewer (#38) — for the `create_viewer_widget()` factory and MDI pipeline ## References - `docs/KC_SPECIFICATION.md` §4.2 (`silo/metadata.json` schema) - `docs/SILO_VIEWPORT_PLAN.md` Phase 3 - Silo Metadata Viewport Specification §5.2 (Metadata Editor) - `mods/silo/freecad/schema_form.py` — existing dynamic form builder
forbes added the enhancement label 2026-02-18 21:15:54 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kindred/silo-mod#39