diff --git a/docs/IMPLEMENTATION_PLAN.md b/docs/IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..5291b0a --- /dev/null +++ b/docs/IMPLEMENTATION_PLAN.md @@ -0,0 +1,2041 @@ +# RPG Companion v2.0 - Implementation Plan + +**Target Version:** 2.0.0 +**Architecture:** Widget Dashboard + Schema System +**Start Date:** 2025-10-23 +**Estimated Duration:** 8-12 weeks + +--- + +## Implementation Strategy + +### Principles +1. **Each task builds on the previous** - Dependencies clearly marked +2. **Incremental deployment** - Each epic delivers working functionality +3. **Backward compatibility** - Existing features keep working throughout +4. **Test as you go** - Manual testing after each task completion +5. **Progressive enhancement** - Start simple, add complexity gradually + +### Progress Tracking +- Use checkboxes to mark completion: `- [ ]` → `- [x]` +- Update epic status in headers +- Document blockers and decisions in comments + +--- + +## Epic 1: Foundation - Dashboard Infrastructure + +**Status:** Not Started +**Dependencies:** None +**Estimated Duration:** 2 weeks +**Goal:** Build the core widget dashboard system without schema integration + +### Task 1.1: Grid Engine Core +**Dependencies:** None +**Estimated Time:** 3-4 days + +- [ ] Create `src/systems/dashboard/` directory structure +- [ ] Implement `GridEngine` class (`src/systems/dashboard/gridEngine.js`) + - [ ] `constructor(config)` - Initialize grid with columns, rowHeight, gap + - [ ] `getPixelPosition(widget)` - Convert grid coords to pixels + - [ ] `snapToCell(pixelX, pixelY)` - Snap pixel position to grid + - [ ] `detectCollision(widget, widgets)` - Check for widget overlaps + - [ ] `reflow(widgets)` - Auto-reflow on collision +- [ ] Add unit tests for grid calculations + - [ ] Test snap-to-grid accuracy + - [ ] Test collision detection edge cases + - [ ] Test reflow algorithm + +**Acceptance Criteria:** +- Grid engine can convert between pixel and grid coordinates +- Collision detection works for all widget sizes +- Reflow pushes widgets down correctly when overlapping + +--- + +### Task 1.2: Widget Registry System +**Dependencies:** Task 1.1 +**Estimated Time:** 2-3 days + +- [ ] Create `WidgetRegistry` class (`src/systems/dashboard/widgetRegistry.js`) + - [ ] `register(type, definition)` - Register widget type + - [ ] `get(type)` - Retrieve widget definition + - [ ] `getAvailable(hasSchema)` - List available widgets + - [ ] `unregister(type)` - Remove widget type +- [ ] Define widget definition interface (JSDoc types) +- [ ] Create base widget template with lifecycle hooks +- [ ] Add widget metadata (name, icon, description, minSize, defaultSize, requiresSchema) + +**Acceptance Criteria:** +- Can register/retrieve widgets from registry +- Widget definitions include all required metadata +- Can filter widgets by schema requirement + +--- + +### Task 1.3: Dashboard Data Structure +**Dependencies:** Task 1.2 +**Estimated Time:** 1-2 days + +- [ ] Define dashboard config structure in `src/core/state.js` + - [ ] Add `extensionSettings.dashboard` object + - [ ] Add `gridConfig` (columns, rowHeight, gap, snapToGrid, showGrid) + - [ ] Add `tabs` array structure + - [ ] Add `defaultTab` string +- [ ] Create default layout generator + - [ ] Generate "Status" tab with userStats, infoBox, presentCharacters + - [ ] Generate "Inventory" tab with inventory widget +- [ ] Add dashboard config to settings save/load +- [ ] Create dashboard config migration from current structure + +**Acceptance Criteria:** +- Dashboard config persists in extensionSettings +- Default layout generates on first load +- Existing users see their current layout as default + +--- + +### Task 1.4: Tab Management System +**Dependencies:** Task 1.3 +**Estimated Time:** 3-4 days + +- [ ] Create `TabManager` class (`src/systems/dashboard/tabManager.js`) + - [ ] `createTab(name, icon)` - Add new tab + - [ ] `renameTab(tabId, newName)` - Rename existing tab + - [ ] `deleteTab(tabId)` - Remove tab (with confirmation) + - [ ] `reorderTabs(tabIds)` - Change tab order + - [ ] `duplicateTab(tabId)` - Copy tab with all widgets + - [ ] `setActiveTab(tabId)` - Switch active tab +- [ ] Implement tab navigation UI + - [ ] Tab buttons with icons and names + - [ ] Active tab highlighting + - [ ] Tab overflow handling (scroll or dropdown) + - [ ] "+" button to add new tab +- [ ] Add keyboard shortcuts for tab switching (Ctrl+1-9) +- [ ] Add tab context menu (right-click: rename, delete, duplicate) + +**Acceptance Criteria:** +- Can create, rename, delete, reorder tabs via UI +- Tab changes persist across sessions +- Keyboard shortcuts work correctly +- Context menu appears on right-click + +--- + +### Task 1.5: Drag-and-Drop Implementation +**Dependencies:** Task 1.1, Task 1.4 +**Estimated Time:** 4-5 days + +- [ ] Create `DragDropHandler` class (`src/systems/dashboard/dragDrop.js`) + - [ ] `initWidget(element, widget)` - Attach drag listeners + - [ ] `startDrag(e, element, widget)` - Begin drag operation + - [ ] `onMouseMove(e)` - Update widget position during drag + - [ ] `onMouseUp(e)` - Complete drag and snap to grid + - [ ] Support touch events for mobile +- [ ] Add visual drag feedback + - [ ] Ghost/preview of widget during drag + - [ ] Grid cells highlight on hover + - [ ] Collision zones shown in red +- [ ] Implement drag from widget library + - [ ] Sidebar with available widgets + - [ ] Drag widget type onto grid to instantiate + - [ ] Show widget preview before drop +- [ ] Add drag constraints + - [ ] Prevent dragging outside grid bounds + - [ ] Snap to grid on drop + - [ ] Cancel drag on Escape key + +**Acceptance Criteria:** +- Can drag existing widgets to new positions +- Can drag new widgets from library onto grid +- Grid snapping works accurately +- Touch events work on mobile devices +- Visual feedback is smooth and clear + +--- + +### Task 1.6: Widget Resize Handles +**Dependencies:** Task 1.5 +**Estimated Time:** 2-3 days + +- [ ] Add resize handles to widget corners (edit mode only) +- [ ] Implement resize logic + - [ ] Track mouse position relative to widget + - [ ] Update widget width/height in grid units + - [ ] Respect minSize constraints from widget definition + - [ ] Snap resize to grid cells +- [ ] Add visual feedback during resize + - [ ] Show new dimensions in overlay + - [ ] Highlight affected grid cells + - [ ] Show collision warnings +- [ ] Handle resize collisions + - [ ] Push other widgets down if needed + - [ ] Prevent resize if would overlap and can't push + +**Acceptance Criteria:** +- Resize handles appear in edit mode +- Can resize widgets by dragging corners +- Respects minimum size constraints +- Grid snapping works during resize +- Collisions handled gracefully + +--- + +### Task 1.7: Edit Mode UI +**Dependencies:** Task 1.4, Task 1.5, Task 1.6 +**Estimated Time:** 3-4 days + +- [ ] Create edit mode state management + - [ ] Add `isEditMode` flag to state + - [ ] Toggle edit mode with button in panel header + - [ ] Show/hide edit controls based on mode +- [ ] Build edit mode UI elements + - [ ] "Edit Layout" button in panel header + - [ ] "Save" and "Cancel" buttons when in edit mode + - [ ] Grid overlay visualization (dotted lines) + - [ ] Widget library sidebar +- [ ] Implement widget controls (edit mode only) + - [ ] Drag handle in widget header + - [ ] Delete button (×) in widget header + - [ ] Settings button (⚙) in widget header + - [ ] Resize handles on widget corners +- [ ] Add confirmation dialogs + - [ ] Confirm before deleting widget + - [ ] Confirm before canceling unsaved changes + - [ ] Confirm before resetting to default layout + +**Acceptance Criteria:** +- Edit mode toggle works smoothly +- All edit controls visible only in edit mode +- Grid overlay appears when editing +- Confirmation dialogs prevent accidental changes +- Changes saved on "Save", reverted on "Cancel" + +--- + +### Task 1.8: Layout Persistence +**Dependencies:** Task 1.7 +**Estimated Time:** 2-3 days + +- [ ] Create `LayoutPersistence` class (`src/systems/dashboard/layoutPersistence.js`) + - [ ] `saveLayout(dashboard)` - Save to extensionSettings + - [ ] `loadLayout()` - Load from extensionSettings + - [ ] `exportLayout()` - Export as JSON file + - [ ] `importLayout(file)` - Import from JSON file + - [ ] `resetToDefault()` - Restore default layout +- [ ] Add debounced auto-save + - [ ] Save 500ms after widget position change + - [ ] Save immediately on tab create/delete/rename + - [ ] Show save indicator in UI +- [ ] Implement import/export UI + - [ ] "Export Layout" button in settings + - [ ] "Import Layout" button in settings + - [ ] File picker for import + - [ ] Download JSON file for export + +**Acceptance Criteria:** +- Layout changes persist across page refreshes +- Auto-save works reliably without lag +- Export creates valid JSON file +- Import correctly restores layout +- Reset button restores default layout + +--- + +**Epic 1 Complete When:** +- [x] Grid engine works with accurate positioning +- [x] Widget registry can register/retrieve widgets +- [x] Dashboard config persists correctly +- [x] Tab management fully functional +- [x] Drag-and-drop works on desktop and mobile +- [x] Resize handles work smoothly +- [x] Edit mode toggle and UI complete +- [x] Layout persistence reliable + +--- + +## Epic 2: Widget Conversion + +**Status:** Not Started +**Dependencies:** Epic 1 complete +**Estimated Duration:** 2-3 weeks +**Goal:** Convert existing hardcoded sections into draggable widgets + +### Task 2.1: User Stats Widget +**Dependencies:** Epic 1 +**Estimated Time:** 3-4 days + +- [ ] Register `userStats` widget in registry + - [ ] Define widget metadata (name, icon, minSize, defaultSize) + - [ ] Set `requiresSchema: false` +- [ ] Create widget render function + - [ ] Reuse existing `renderUserStats()` logic + - [ ] Wrap in widget container with header + - [ ] Add widget-specific CSS classes +- [ ] Add widget configuration options + - [ ] Toggle classic stats display + - [ ] Choose stat bar style (solid/gradient) + - [ ] Select which stats to show +- [ ] Implement configuration UI + - [ ] Settings icon opens config modal + - [ ] Config changes update widget immediately + - [ ] Save config to widget instance + +**Acceptance Criteria:** +- User Stats widget appears in widget library +- Can drag onto grid and resize +- Displays all current stats correctly +- Configuration options work +- Editable fields still functional + +--- + +### Task 2.2: Info Box Widget +**Dependencies:** Task 2.1 +**Estimated Time:** 2-3 days + +- [ ] Register `infoBox` widget in registry +- [ ] Create widget render function + - [ ] Reuse existing `renderInfoBox()` logic + - [ ] Maintain dashboard widget styling + - [ ] Keep editable fields functional +- [ ] Add widget configuration options + - [ ] Toggle individual widgets (calendar, weather, temp, clock, location) + - [ ] Choose widget layout (horizontal/vertical) + - [ ] Customize colors +- [ ] Test all info box interactions + - [ ] Editing date/weather/time/location + - [ ] Field focus/blur behavior + - [ ] Data persistence + +**Acceptance Criteria:** +- Info Box widget draggable and resizable +- All dashboard widgets render correctly +- Editing functionality preserved +- Configuration options work +- Responsive on mobile + +--- + +### Task 2.3: Present Characters Widget +**Dependencies:** Task 2.2 +**Estimated Time:** 3-4 days + +- [ ] Register `presentCharacters` widget in registry +- [ ] Create widget render function + - [ ] Reuse existing `renderThoughts()` logic + - [ ] Display character cards with avatars + - [ ] Show relationship badges + - [ ] Render traits and thoughts +- [ ] Add widget configuration options + - [ ] Choose card layout (list/grid) + - [ ] Filter by relationship type + - [ ] Toggle thought bubbles in chat + - [ ] Customize card styling +- [ ] Test character card interactions + - [ ] Editing character fields + - [ ] Avatar loading + - [ ] Thought bubble overlay in chat + +**Acceptance Criteria:** +- Present Characters widget functional +- Character cards display correctly +- Editing works as before +- Thought bubbles still appear in chat +- Configuration options work + +--- + +### Task 2.4: Inventory Widget +**Dependencies:** Task 2.3 +**Estimated Time:** 4-5 days + +- [ ] Register `inventory` widget in registry +- [ ] Create widget render function + - [ ] Reuse existing `renderInventory()` logic + - [ ] Show sub-tabs (On Person, Stored, Assets) + - [ ] Maintain list/grid view toggles + - [ ] Keep collapsible locations +- [ ] Add widget configuration options + - [ ] Set default sub-tab + - [ ] Choose default view mode (list/grid) + - [ ] Customize location order + - [ ] Toggle item counts +- [ ] Test all inventory interactions + - [ ] Adding/removing items + - [ ] Creating/deleting storage locations + - [ ] Editing item names + - [ ] Switching view modes + - [ ] Collapsing/expanding locations + +**Acceptance Criteria:** +- Inventory widget fully functional +- All sub-tabs work correctly +- View mode toggles work +- Storage locations editable +- Item editing preserved +- Configuration options functional + +--- + +### Task 2.5: Classic Stats Widget +**Dependencies:** Task 2.4 +**Estimated Time:** 2-3 days + +- [ ] Register `classicStats` widget in registry +- [ ] Extract classic stats from User Stats + - [ ] Create separate rendering function + - [ ] Show STR/DEX/CON/INT/WIS/CHA grid + - [ ] Display +/- buttons for each stat +- [ ] Add widget configuration options + - [ ] Choose stat layout (2x3, 3x2, 1x6) + - [ ] Toggle stat modifiers display + - [ ] Show/hide attribute abbreviations + - [ ] Customize stat ranges (min/max) +- [ ] Test stat modification + - [ ] +/- buttons increment/decrement + - [ ] Values persist correctly + - [ ] Modifiers calculate correctly + +**Acceptance Criteria:** +- Classic Stats widget standalone functional +- Can be added independently of User Stats +- +/- buttons work correctly +- Configuration options work +- Values persist across sessions + +--- + +### Task 2.6: Dice Roller Widget +**Dependencies:** Task 2.5 +**Estimated Time:** 3-4 days + +- [ ] Register `diceRoller` widget in registry +- [ ] Create interactive dice roller UI + - [ ] Formula input field (e.g., "2d6+3") + - [ ] Quick roll buttons (d4, d6, d8, d10, d12, d20, d100) + - [ ] Roll button + - [ ] Results display area +- [ ] Implement dice rolling logic + - [ ] Parse dice formula + - [ ] Generate random rolls + - [ ] Calculate total with modifiers + - [ ] Show individual die results +- [ ] Add widget configuration options + - [ ] Save favorite roll formulas + - [ ] Choose result display style + - [ ] Toggle roll history + - [ ] Set default formula +- [ ] Integrate with classic stats + - [ ] Add stat modifiers to rolls + - [ ] Show success/failure based on DC + +**Acceptance Criteria:** +- Dice roller widget fully functional +- Can parse complex formulas +- Roll results accurate +- Quick buttons work +- Configuration options work +- Integration with stats functional + +--- + +### Task 2.7: Last Roll Display Widget +**Dependencies:** Task 2.6 +**Estimated Time:** 1-2 days + +- [ ] Register `lastRoll` widget in registry +- [ ] Create compact roll display + - [ ] Show last roll formula + - [ ] Display total result prominently + - [ ] Show individual die results + - [ ] Add timestamp +- [ ] Add widget configuration options + - [ ] Choose display format (compact/detailed) + - [ ] Toggle individual dice display + - [ ] Customize result colors +- [ ] Sync with dice roller + - [ ] Update when new roll made + - [ ] Link to dice roller widget + +**Acceptance Criteria:** +- Last Roll widget displays correctly +- Updates automatically on new rolls +- Configuration options work +- Compact enough for small spaces + +--- + +**Epic 2 Complete When:** +- [x] All core widgets converted and functional +- [x] Each widget draggable and resizable +- [x] All existing functionality preserved +- [x] Configuration options work for each widget +- [x] No regressions in data persistence +- [x] Mobile responsive behavior maintained + +--- + +## Epic 3: Schema Infrastructure + +**Status:** Not Started +**Dependencies:** Epic 1 complete (Epic 2 can happen in parallel) +**Estimated Duration:** 3-4 weeks +**Goal:** Build the schema system foundation + +### Task 3.1: YAML Parser Integration +**Dependencies:** None +**Estimated Time:** 2-3 days + +- [ ] Add js-yaml library to project + - [ ] Install via npm: `npm install js-yaml` + - [ ] Or use CDN for no-build setup +- [ ] Create YAML utilities (`src/systems/schema/yamlUtils.js`) + - [ ] `parseYAML(string)` - Parse YAML to object + - [ ] `toYAML(object)` - Convert object to YAML string + - [ ] `validateYAMLSyntax(string)` - Check for syntax errors +- [ ] Add error handling for YAML parsing + - [ ] Catch parsing errors + - [ ] Show user-friendly error messages + - [ ] Highlight problematic lines +- [ ] Test with sample schemas + - [ ] Load D&D 5e schema YAML + - [ ] Verify correct parsing + - [ ] Test malformed YAML handling + +**Acceptance Criteria:** +- YAML parser correctly loads schema files +- Syntax errors caught and reported clearly +- Can convert between YAML and JSON +- Sample schemas parse without errors + +--- + +### Task 3.2: Schema Data Structure +**Dependencies:** Task 3.1 +**Estimated Time:** 2-3 days + +- [ ] Define schema structure in JSDoc types (`src/types/schema.js`) + - [ ] `SchemaMetadata` type (name, version, author, description, tags) + - [ ] `ComponentDefinition` type (type, label, icon, properties) + - [ ] `PropertyDefinition` type (type, label, min, max, default, formula) + - [ ] `PromptTemplate` type (section name, template string) + - [ ] `LayoutSuggestion` type (tabs, widgets) +- [ ] Create schema builder utilities + - [ ] `createEmptySchema()` - Generate blank schema + - [ ] `validateSchemaStructure(schema)` - Check required fields + - [ ] `mergeSchemas(base, override)` - Combine schemas +- [ ] Define component type enums + - [ ] object, list, resource, formula, number, text, boolean +- [ ] Create default D&D 5e schema as reference + +**Acceptance Criteria:** +- Schema structure well-documented with types +- Utility functions work correctly +- D&D 5e reference schema complete +- All component types defined + +--- + +### Task 3.3: Formula Engine +**Dependencies:** Task 3.2 +**Estimated Time:** 4-5 days + +- [ ] Create `FormulaEngine` class (`src/systems/schema/formulaEngine.js`) + - [ ] `constructor(characterData)` - Initialize with character instance + - [ ] `evaluate(formula)` - Calculate formula result + - [ ] `resolveReferences(formula)` - Replace @ references with values + - [ ] `getValueByPath(path)` - Navigate nested object by path + - [ ] `safeEval(expression)` - Evaluate with whitelist functions + - [ ] `invalidateCache()` - Clear memoized results +- [ ] Implement safe evaluation + - [ ] Whitelist math functions (floor, ceil, round, min, max, abs) + - [ ] Use Function constructor for sandboxing + - [ ] Prevent infinite loops with timeout + - [ ] Block access to globals +- [ ] Add formula caching + - [ ] Memoize calculated values + - [ ] Invalidate cache on data changes + - [ ] Show cache hit rate in debug +- [ ] Create formula testing suite + - [ ] Test basic math operations + - [ ] Test @ reference resolution + - [ ] Test nested references + - [ ] Test invalid formulas + +**Acceptance Criteria:** +- Formula engine evaluates expressions correctly +- @ references resolve to character data +- Whitelisted functions work +- Dangerous code blocked +- Cache improves performance +- All tests pass + +--- + +### Task 3.4: Schema Validation +**Dependencies:** Task 3.2 +**Estimated Time:** 3-4 days + +- [ ] Add JSON Schema validation library + - [ ] Install Ajv: `npm install ajv` + - [ ] Or use CDN for no-build setup +- [ ] Create `SchemaValidator` class (`src/systems/schema/validator.js`) + - [ ] `compileSchema(yamlSchema)` - Convert to JSON Schema + - [ ] `convertComponent(component)` - Map component to JSON Schema + - [ ] `convertProperties(properties)` - Map properties to JSON Schema + - [ ] `validate(characterInstance, schema)` - Check instance validity +- [ ] Implement component type conversions + - [ ] object → JSON Schema object + - [ ] list → JSON Schema array + - [ ] resource → JSON Schema object with current/max + - [ ] formula → JSON Schema number + - [ ] number/text/boolean → corresponding JSON Schema types +- [ ] Add validation error reporting + - [ ] Collect all validation errors + - [ ] Format errors for display + - [ ] Highlight invalid fields in UI +- [ ] Create validation test suite + +**Acceptance Criteria:** +- Schema validation catches invalid data +- Error messages clear and helpful +- All component types validate correctly +- Test suite covers edge cases + +--- + +### Task 3.5: IndexedDB Storage Layer +**Dependencies:** Task 3.4 +**Estimated Time:** 4-5 days + +- [ ] Create `SchemaStorage` class (`src/systems/schema/storage.js`) + - [ ] `init()` - Initialize IndexedDB + - [ ] `saveSchema(schema)` - Save to schemas store + - [ ] `loadSchema(schemaId)` - Retrieve schema by ID + - [ ] `listSchemas()` - Get all schemas + - [ ] `deleteSchema(schemaId)` - Remove schema + - [ ] `saveCharacter(instance)` - Save character instance + - [ ] `loadCharacter(charId)` - Retrieve character + - [ ] `listCharacters()` - Get all characters + - [ ] `deleteCharacter(charId)` - Remove character +- [ ] Set up IndexedDB schema + - [ ] Create `schemas` object store (keyPath: 'id') + - [ ] Create indexes on name, version + - [ ] Create `characters` object store (keyPath: 'id') + - [ ] Create indexes on schemaId, name +- [ ] Implement error handling + - [ ] Handle DB initialization failures + - [ ] Catch transaction errors + - [ ] Provide fallback to localStorage +- [ ] Add storage quota management + - [ ] Check available space + - [ ] Warn if running low + - [ ] Implement cleanup strategy + +**Acceptance Criteria:** +- IndexedDB initializes correctly +- Can save/load schemas reliably +- Can save/load characters reliably +- Indexes speed up queries +- Error handling robust +- Fallback works if IndexedDB unavailable + +--- + +### Task 3.6: File System Access API Integration +**Dependencies:** Task 3.5 +**Estimated Time:** 3-4 days + +- [ ] Add File System Access API support to SchemaStorage + - [ ] `exportSchema(schemaId)` - Export to YAML file + - [ ] `importSchema()` - Import from YAML file + - [ ] `exportCharacter(charId)` - Export to JSON file + - [ ] `importCharacter()` - Import from JSON file +- [ ] Implement browser detection + - [ ] Check for File System Access API support + - [ ] Fallback to download/upload if unavailable +- [ ] Add file picker UI + - [ ] Use native file picker when available + - [ ] File extension filters (.yaml for schemas, .json for characters) + - [ ] Suggested filenames +- [ ] Implement fallback for older browsers + - [ ] Use blob download for export + - [ ] Use file input for import + - [ ] Show appropriate UI based on support +- [ ] Add import validation + - [ ] Validate YAML/JSON syntax + - [ ] Validate schema structure + - [ ] Validate character instance against schema + +**Acceptance Criteria:** +- Export creates downloadable files +- Import loads files correctly +- File pickers work on supported browsers +- Fallback works on older browsers +- Validation prevents bad imports + +--- + +### Task 3.7: Character Instance Manager +**Dependencies:** Task 3.3, Task 3.5 +**Estimated Time:** 3-4 days + +- [ ] Create `CharacterManager` class (`src/systems/schema/characterManager.js`) + - [ ] `createCharacter(schemaId, name)` - New character instance + - [ ] `loadCharacter(charId)` - Load existing character + - [ ] `saveCharacter(instance)` - Persist changes + - [ ] `deleteCharacter(charId)` - Remove character + - [ ] `updateProperty(path, value)` - Change character data + - [ ] `recalculateFormulas()` - Update all derived values +- [ ] Implement character lifecycle + - [ ] Initialize with default values from schema + - [ ] Validate changes against schema + - [ ] Update timestamps on changes + - [ ] Auto-save after modifications +- [ ] Add formula recalculation + - [ ] Detect which formulas depend on changed property + - [ ] Recalculate in dependency order + - [ ] Update UI after recalculation +- [ ] Create character selection UI + - [ ] List all characters + - [ ] Switch between characters + - [ ] Create new character button + - [ ] Delete character button (with confirmation) + +**Acceptance Criteria:** +- Can create/load/save/delete characters +- Property updates validated +- Formulas recalculate automatically +- Character selection UI functional +- Auto-save works reliably + +--- + +**Epic 3 Complete When:** +- [x] YAML parser integrated and working +- [x] Schema data structure defined +- [x] Formula engine evaluates correctly +- [x] Schema validation catches errors +- [x] IndexedDB storage reliable +- [x] File export/import works +- [x] Character manager fully functional + +--- + +## Epic 4: Schema-Driven Widgets + +**Status:** Not Started +**Dependencies:** Epic 1, Epic 3 complete +**Estimated Duration:** 3-4 weeks +**Goal:** Create widgets that render based on schema definitions + +### Task 4.1: Schema Widget Renderer +**Dependencies:** Epic 1, Epic 3 +**Estimated Time:** 4-5 days + +- [ ] Create `SchemaWidgetRenderer` class (`src/systems/dashboard/schemaWidgets.js`) + - [ ] `constructor(schema, instance, formulaEngine)` - Initialize + - [ ] `renderComponent(name, container, config)` - Render any component + - [ ] `renderObject(component, data, container)` - Render object type + - [ ] `renderList(component, data, container, config)` - Render list type + - [ ] `renderResource(component, data, container)` - Render resource type +- [ ] Implement object component rendering + - [ ] Display properties in labeled grid + - [ ] Show formulas as read-only values + - [ ] Editable inputs for non-formula properties + - [ ] Apply min/max constraints +- [ ] Implement list component rendering + - [ ] Display items in table or list + - [ ] Add/remove list items + - [ ] Edit item properties inline + - [ ] Filter/sort options +- [ ] Implement resource component rendering + - [ ] Show current/max values + - [ ] Display as progress bar or dots + - [ ] Editable current value + - [ ] Calculate max from formula if defined +- [ ] Add update handlers + - [ ] Update character instance on input change + - [ ] Trigger formula recalculation + - [ ] Re-render affected widgets + - [ ] Save changes automatically + +**Acceptance Criteria:** +- Schema components render correctly +- All component types supported +- Editing works for non-formula fields +- Formulas calculate and display correctly +- Changes persist automatically + +--- + +### Task 4.2: Custom Stats Widget +**Dependencies:** Task 4.1 +**Estimated Time:** 3-4 days + +- [ ] Register `customStats` widget in registry + - [ ] Set `requiresSchema: true` + - [ ] Define metadata +- [ ] Create widget render function + - [ ] Use SchemaWidgetRenderer for component + - [ ] Display stats based on schema definition + - [ ] Show formulas and derived values + - [ ] Allow editing base stats +- [ ] Add widget configuration options + - [ ] Choose which stats to display + - [ ] Select display format (bars, numbers, both) + - [ ] Customize bar colors + - [ ] Toggle formula visibility +- [ ] Test with D&D 5e schema + - [ ] Ability scores (STR, DEX, etc.) + - [ ] Ability modifiers (calculated) + - [ ] Editing and persistence + +**Acceptance Criteria:** +- Custom Stats widget only appears when schema active +- Renders stats from schema definition +- Formulas calculate correctly +- Editing works and persists +- Configuration options functional + +--- + +### Task 4.3: Skills Widget +**Dependencies:** Task 4.1 +**Estimated Time:** 3-4 days + +- [ ] Register `skills` widget in registry + - [ ] Set `requiresSchema: true` +- [ ] Create widget render function + - [ ] Use SchemaWidgetRenderer for list component + - [ ] Display skills in table or list + - [ ] Show skill values and modifiers + - [ ] Indicate proficiency status +- [ ] Add widget configuration options + - [ ] Filter by skill category + - [ ] Sort by name or value + - [ ] Choose display format (table, list, grid) + - [ ] Toggle modifier display +- [ ] Add skill interaction features + - [ ] Click skill to roll check + - [ ] Add/remove custom skills + - [ ] Edit skill values + - [ ] Toggle proficiency +- [ ] Integrate with dice roller + - [ ] Roll skill check with modifiers + - [ ] Show DC comparison + - [ ] Display result + +**Acceptance Criteria:** +- Skills widget renders schema-defined skills +- Can edit skill values +- Filtering and sorting work +- Dice integration functional +- Configuration options work + +--- + +### Task 4.4: Relationships Widget +**Dependencies:** Task 4.1 +**Estimated Time:** 4-5 days + +- [ ] Register `relationships` widget in registry + - [ ] Set `requiresSchema: true` +- [ ] Define relationship component in schema + - [ ] Character name + - [ ] Relationship type (Enemy/Neutral/Friend/Lover or custom) + - [ ] Affection/Trust values + - [ ] Notes/history +- [ ] Create widget render function + - [ ] Display character list with relationship info + - [ ] Show affection/trust meters + - [ ] Display relationship type badge + - [ ] Show recent interactions +- [ ] Add widget configuration options + - [ ] Filter by relationship type + - [ ] Sort by affection or name + - [ ] Choose display format (list, cards, graph) + - [ ] Toggle notes display +- [ ] Implement relationship management + - [ ] Add new relationship + - [ ] Edit affection/trust values + - [ ] Change relationship type + - [ ] Add notes/history entries + - [ ] Remove relationship + +**Acceptance Criteria:** +- Relationships widget functional with schema +- Can add/edit/remove relationships +- Affection/Trust values display correctly +- Filtering and sorting work +- Configuration options work + +--- + +### Task 4.5: Quests Widget +**Dependencies:** Task 4.1 +**Estimated Time:** 4-5 days + +- [ ] Register `quests` widget in registry + - [ ] Set `requiresSchema: true` +- [ ] Define quest component in schema + - [ ] Quest name + - [ ] Description + - [ ] Status (Active/Completed/Failed) + - [ ] Objectives (checklist) + - [ ] Rewards +- [ ] Create widget render function + - [ ] Display active quests + - [ ] Show objectives as checklist + - [ ] Indicate quest status + - [ ] Show rewards +- [ ] Add widget configuration options + - [ ] Filter by status + - [ ] Sort by name or priority + - [ ] Toggle completed quests + - [ ] Choose compact or detailed view +- [ ] Implement quest management + - [ ] Add new quest + - [ ] Edit quest details + - [ ] Check off objectives + - [ ] Mark quest as complete/failed + - [ ] Delete quest + +**Acceptance Criteria:** +- Quests widget renders schema-defined quests +- Can manage quests (add/edit/delete) +- Objectives checklist functional +- Status filtering works +- Configuration options work + +--- + +### Task 4.6: Status Effects Widget +**Dependencies:** Task 4.1 +**Estimated Time:** 3-4 days + +- [ ] Register `statusEffects` widget in registry + - [ ] Set `requiresSchema: true` +- [ ] Define status effect component in schema + - [ ] Effect name + - [ ] Duration (turns/time) + - [ ] Effect description + - [ ] Intensity/stacks +- [ ] Create widget render function + - [ ] Display active effects as badges + - [ ] Show duration countdown + - [ ] Indicate effect type (buff/debuff) + - [ ] Display effect description on hover +- [ ] Add widget configuration options + - [ ] Filter by effect type + - [ ] Sort by duration or name + - [ ] Choose display format (badges, list, icons) + - [ ] Toggle expired effects +- [ ] Implement effect management + - [ ] Add new effect + - [ ] Edit effect details + - [ ] Update duration + - [ ] Remove effect + - [ ] Auto-decrement duration on time passage + +**Acceptance Criteria:** +- Status Effects widget shows active effects +- Can manage effects (add/edit/remove) +- Duration tracking works +- Filtering works +- Configuration options work + +--- + +### Task 4.7: Resources Widget +**Dependencies:** Task 4.1 +**Estimated Time:** 2-3 days + +- [ ] Register `resources` widget in registry + - [ ] Set `requiresSchema: true` +- [ ] Create widget render function + - [ ] Use SchemaWidgetRenderer for resource components + - [ ] Display multiple resources + - [ ] Show as progress bars or dots + - [ ] Calculate max from formulas +- [ ] Add widget configuration options + - [ ] Choose which resources to display + - [ ] Select display format (bars, dots, numbers) + - [ ] Customize colors + - [ ] Toggle max value display +- [ ] Test with D&D 5e schema + - [ ] Hit Points + - [ ] Spell Slots + - [ ] Any custom resources + +**Acceptance Criteria:** +- Resources widget renders schema-defined resources +- Progress bars/dots display correctly +- Formula-based max values calculate +- Current value editing works +- Configuration options work + +--- + +**Epic 4 Complete When:** +- [x] Schema widget renderer works for all component types +- [x] All schema-driven widgets implemented +- [x] Widgets only appear when schema active +- [x] Editing and persistence works +- [x] Configuration options functional +- [x] D&D 5e schema fully supported + +--- + +## Epic 5: Schema Editor UI + +**Status:** Not Started +**Dependencies:** Epic 3 complete +**Estimated Duration:** 2-3 weeks +**Goal:** Build GUI for creating/editing schemas + +### Task 5.1: Schema Editor Modal +**Dependencies:** Epic 3 +**Estimated Time:** 3-4 days + +- [ ] Create schema editor modal UI + - [ ] Full-screen or large modal overlay + - [ ] Header with title and action buttons + - [ ] Two-panel layout (sidebar + editor) + - [ ] Close button with unsaved changes warning +- [ ] Build sidebar navigation + - [ ] List of schema components + - [ ] "Add Component" button + - [ ] Component templates section + - [ ] Import/export buttons +- [ ] Create main editor area + - [ ] YAML editor with syntax highlighting + - [ ] Visual builder toggle + - [ ] Preview pane + - [ ] Validation feedback +- [ ] Add action buttons + - [ ] Save button + - [ ] Cancel button + - [ ] Validate button + - [ ] Export button + - [ ] Import button + +**Acceptance Criteria:** +- Schema editor modal opens from settings +- Layout is intuitive and spacious +- Can switch between YAML and visual editor +- Action buttons work correctly + +--- + +### Task 5.2: YAML Editor Component +**Dependencies:** Task 5.1 +**Estimated Time:** 3-4 days + +- [ ] Integrate syntax highlighting library + - [ ] Use CodeMirror or Ace Editor + - [ ] Configure YAML syntax mode + - [ ] Set theme to match extension theme +- [ ] Add editor features + - [ ] Line numbers + - [ ] Auto-indentation + - [ ] Code folding + - [ ] Find/replace + - [ ] Undo/redo +- [ ] Implement real-time validation + - [ ] Parse YAML on change (debounced) + - [ ] Show syntax errors inline + - [ ] Highlight invalid lines + - [ ] Display error messages +- [ ] Add YAML assistance + - [ ] Auto-complete for component types + - [ ] Snippets for common patterns + - [ ] Inline documentation on hover + +**Acceptance Criteria:** +- YAML editor has syntax highlighting +- Validation catches errors in real-time +- Editor features work smoothly +- Assistance features helpful + +--- + +### Task 5.3: Visual Schema Builder +**Dependencies:** Task 5.1 +**Estimated Time:** 5-6 days + +- [ ] Create visual builder UI + - [ ] Component list on left + - [ ] Component editor on right + - [ ] Drag-and-drop to reorder components +- [ ] Build component editor form + - [ ] Component name input + - [ ] Component type selector + - [ ] Label and icon inputs + - [ ] Properties list + - [ ] Add/remove property buttons +- [ ] Create property editor + - [ ] Property name input + - [ ] Property type selector + - [ ] Min/max/default inputs (conditional on type) + - [ ] Formula input (for formula type) + - [ ] Required checkbox +- [ ] Implement component templates + - [ ] Pre-made component definitions + - [ ] D&D 5e ability scores template + - [ ] Resource template + - [ ] Skills list template + - [ ] One-click insert +- [ ] Add validation feedback + - [ ] Highlight invalid fields + - [ ] Show validation errors + - [ ] Prevent saving invalid schema + +**Acceptance Criteria:** +- Visual builder creates valid YAML +- Can add/edit/remove components and properties +- Templates speed up schema creation +- Validation prevents invalid schemas +- Syncs with YAML editor + +--- + +### Task 5.4: Schema Preview Pane +**Dependencies:** Task 5.3 +**Estimated Time:** 3-4 days + +- [ ] Create preview pane UI + - [ ] Live preview of schema widgets + - [ ] Mock character data + - [ ] Refresh button +- [ ] Implement live preview + - [ ] Render widgets based on current schema + - [ ] Use sample data to populate + - [ ] Update on schema changes (debounced) +- [ ] Add preview controls + - [ ] Select which component to preview + - [ ] Toggle between widget types + - [ ] Adjust preview size + - [ ] Reset to default view +- [ ] Show formula calculations + - [ ] Evaluate formulas with sample data + - [ ] Display calculated values + - [ ] Highlight formula errors + +**Acceptance Criteria:** +- Preview pane shows widgets as they'll appear +- Updates when schema changes +- Formulas calculate with sample data +- Preview helps validate schema design + +--- + +### Task 5.5: Schema Templates Library +**Dependencies:** Task 5.4 +**Estimated Time:** 4-5 days + +- [ ] Create official schema templates + - [ ] D&D 5th Edition (complete) + - [ ] Pathfinder 2e (basic) + - [ ] Cyberpunk RED (basic) + - [ ] World of Darkness (basic) + - [ ] Blank template +- [ ] Build template selector UI + - [ ] Grid or list of templates + - [ ] Template preview cards + - [ ] Description and tags + - [ ] "Use Template" button +- [ ] Implement template loading + - [ ] Load template YAML + - [ ] Populate editor with template + - [ ] Show confirmation before overwriting current schema +- [ ] Add template customization + - [ ] Start from template + - [ ] Modify as needed + - [ ] Save as custom schema +- [ ] Create template documentation + - [ ] README for each template + - [ ] Usage examples + - [ ] Customization guide + +**Acceptance Criteria:** +- At least 4 complete templates available +- Template selector easy to use +- Loading templates works smoothly +- Templates well-documented +- Users can customize templates + +--- + +### Task 5.6: Schema Import/Export UI +**Dependencies:** Task 5.1 +**Estimated Time:** 2-3 days + +- [ ] Build import UI + - [ ] File picker button + - [ ] Drag-and-drop area + - [ ] URL import (fetch from GitHub, etc.) + - [ ] Import from clipboard +- [ ] Build export UI + - [ ] Export as YAML file + - [ ] Export as JSON file + - [ ] Copy to clipboard + - [ ] Share URL (if hosted) +- [ ] Add import validation + - [ ] Check file format + - [ ] Validate schema structure + - [ ] Show preview before import + - [ ] Confirm overwrite +- [ ] Implement export options + - [ ] Include metadata (author, version) + - [ ] Minify or format YAML + - [ ] Bundle with character data + - [ ] Generate shareable link + +**Acceptance Criteria:** +- Import supports files, URLs, clipboard +- Export creates valid YAML files +- Validation prevents bad imports +- Export options work correctly + +--- + +**Epic 5 Complete When:** +- [x] Schema editor modal fully functional +- [x] YAML editor with syntax highlighting +- [x] Visual builder creates valid schemas +- [x] Preview pane shows live updates +- [x] Template library available +- [x] Import/export works reliably + +--- + +## Epic 6: AI Integration + +**Status:** Not Started +**Dependencies:** Epic 3, Epic 4 complete +**Estimated Duration:** 2-3 weeks +**Goal:** Integrate schema system with AI prompt generation and parsing + +### Task 6.1: Schema-Based Prompt Builder +**Dependencies:** Epic 3, Epic 4 +**Estimated Time:** 4-5 days + +- [ ] Create `SchemaPromptBuilder` class (`src/systems/generation/schemaPromptBuilder.js`) + - [ ] `generatePrompt(schema, instance)` - Create full prompt + - [ ] `resolveTemplate(template, data)` - Replace [@references] + - [ ] `generateComponentPrompt(component, data)` - Auto-generate component prompt +- [ ] Implement prompt template resolution + - [ ] Parse [@component.property] syntax + - [ ] Resolve references to character data + - [ ] Handle missing values gracefully +- [ ] Add prompt customization + - [ ] Use schema's prompt templates if defined + - [ ] Auto-generate from components if no template + - [ ] Allow users to override prompts +- [ ] Test with D&D 5e schema + - [ ] Generate stats prompt + - [ ] Generate skills prompt + - [ ] Generate resources prompt + - [ ] Verify all references resolve + +**Acceptance Criteria:** +- Prompt builder generates valid prompts +- References resolve correctly +- Custom templates work +- Auto-generation fallback works +- D&D 5e prompts accurate + +--- + +### Task 6.2: Schema-Based Response Parser +**Dependencies:** Task 6.1 +**Estimated Time:** 5-6 days + +- [ ] Create `SchemaParser` class (`src/systems/generation/schemaParser.js`) + - [ ] `parseResponse(response, schema)` - Extract all components + - [ ] `parseComponent(text, component)` - Parse specific component + - [ ] `updateInstance(instance, parsedData)` - Apply parsed data +- [ ] Implement component-specific parsing + - [ ] Parse object components (key: value format) + - [ ] Parse list components (table or bullet format) + - [ ] Parse resource components (current/max format) + - [ ] Parse numbers with units +- [ ] Add flexible pattern matching + - [ ] Support multiple formats for same data + - [ ] Handle typos and variations + - [ ] Use fuzzy matching for component names +- [ ] Implement validation + - [ ] Validate parsed values against schema + - [ ] Type coercion (string to number, etc.) + - [ ] Range checking (min/max) + - [ ] Show parsing errors in debug + +**Acceptance Criteria:** +- Parser extracts data from AI responses +- Handles multiple formats gracefully +- Validates against schema +- Updates character instance correctly +- Debug logs show parsing details + +--- + +### Task 6.3: Prompt Injection System +**Dependencies:** Task 6.1 +**Estimated Time:** 3-4 days + +- [ ] Update `src/systems/generation/injector.js` for schema support + - [ ] Detect if schema is active + - [ ] Use SchemaPromptBuilder instead of hardcoded builder + - [ ] Fall back to hardcoded if no schema +- [ ] Implement context injection + - [ ] Include schema name and version in prompt + - [ ] Add instructions for schema format + - [ ] Include example output format +- [ ] Add Together mode support + - [ ] Inject schema instructions into main prompt + - [ ] Parse and extract schema data from response + - [ ] Clean response text before display +- [ ] Add Separate mode support + - [ ] Use schema for separate tracker generation + - [ ] Inject schema context summary + - [ ] Parse schema data from separate response +- [ ] Test with both modes + - [ ] Together mode with D&D schema + - [ ] Separate mode with D&D schema + - [ ] Verify extraction and parsing + +**Acceptance Criteria:** +- Schema prompts inject correctly +- Both Together and Separate modes work +- AI responses parse successfully +- Fallback to hardcoded works +- No regressions for non-schema users + +--- + +### Task 6.4: Parser Debug UI +**Dependencies:** Task 6.2 +**Estimated Time:** 2-3 days + +- [ ] Enhance debug console widget + - [ ] Show raw AI response + - [ ] Show extracted code blocks + - [ ] Show parsed component data + - [ ] Show validation errors +- [ ] Add parser statistics + - [ ] Success/failure rate + - [ ] Parse time + - [ ] Matched patterns + - [ ] Unrecognized content +- [ ] Implement parser testing tools + - [ ] Paste AI response to test parsing + - [ ] Show step-by-step parsing + - [ ] Highlight matched sections + - [ ] Show what wasn't matched +- [ ] Add debugging controls + - [ ] Toggle verbose logging + - [ ] Export debug logs + - [ ] Clear logs + - [ ] Copy AI response + +**Acceptance Criteria:** +- Debug console shows parser activity +- Can test parsing with sample responses +- Statistics help identify issues +- Debugging tools useful for troubleshooting + +--- + +**Epic 6 Complete When:** +- [x] Schema-based prompts generate correctly +- [x] Parser extracts schema data from responses +- [x] Together and Separate modes both work +- [x] Prompt injection integrated +- [x] Debug UI helps troubleshoot parsing + +--- + +## Epic 7: Polish & Mobile + +**Status:** Not Started +**Dependencies:** All previous epics +**Estimated Duration:** 2-3 weeks +**Goal:** Mobile responsiveness, animations, settings integration + +### Task 7.1: Mobile Responsive Layout +**Dependencies:** Epic 1, Epic 2 +**Estimated Time:** 4-5 days + +- [ ] Implement mobile breakpoint (≤1000px) + - [ ] Force single-column widget layout + - [ ] Stack widgets vertically + - [ ] Maintain user's widget order + - [ ] Disable resize handles +- [ ] Add mobile-specific UI + - [ ] Tab dropdown instead of horizontal tabs + - [ ] Larger touch targets + - [ ] Swipe gestures for tabs + - [ ] Collapsible widget headers +- [ ] Implement mobile edit mode + - [ ] Vertical drag handles for reordering + - [ ] Simplified widget library (bottom sheet) + - [ ] Touch-friendly controls + - [ ] Prevent horizontal scrolling +- [ ] Test on mobile devices + - [ ] iOS Safari + - [ ] Android Chrome + - [ ] Different screen sizes + - [ ] Portrait and landscape + +**Acceptance Criteria:** +- Mobile layout forces single column +- All widgets accessible on mobile +- Touch interactions work smoothly +- Edit mode functional on mobile +- No horizontal scrolling +- Performance acceptable on mobile + +--- + +### Task 7.2: Animation System +**Dependencies:** Epic 1, Epic 2 +**Estimated Time:** 3-4 days + +- [ ] Create animation utilities (`src/systems/ui/animations.js`) + - [ ] `fadeIn(element, duration)` - Fade element in + - [ ] `fadeOut(element, duration)` - Fade element out + - [ ] `slideIn(element, direction, duration)` - Slide in from edge + - [ ] `slideOut(element, direction, duration)` - Slide out + - [ ] `pulse(element)` - Pulse effect + - [ ] `shake(element)` - Shake effect +- [ ] Add widget animations + - [ ] Fade in when added to grid + - [ ] Smooth position changes when dragging + - [ ] Bounce when dropped + - [ ] Pulse when updated with new data +- [ ] Add UI transition animations + - [ ] Tab switching transitions + - [ ] Modal open/close animations + - [ ] Panel expand/collapse + - [ ] Button hover effects +- [ ] Implement animation controls + - [ ] Toggle animations in settings + - [ ] Respect prefers-reduced-motion + - [ ] Adjust animation speed + - [ ] Disable for performance + +**Acceptance Criteria:** +- Animations smooth and natural +- Can be disabled in settings +- Respects accessibility preferences +- No performance impact +- Enhances user experience + +--- + +### Task 7.3: Settings Panel Integration +**Dependencies:** Epic 1, Epic 5 +**Estimated Time:** 3-4 days + +- [ ] Update settings modal with dashboard section + - [ ] Grid configuration (columns, row height, gap) + - [ ] Snap to grid toggle + - [ ] Show grid in edit mode toggle + - [ ] Animation settings +- [ ] Add widget availability toggles + - [ ] List all registered widgets + - [ ] Checkboxes to enable/disable + - [ ] Show which require schema + - [ ] Disable schema widgets if no schema +- [ ] Add schema management section + - [ ] Current schema selector + - [ ] "Create New Schema" button + - [ ] "Import Schema" button + - [ ] "Export Schema" button + - [ ] Schema templates button +- [ ] Add layout management buttons + - [ ] "Edit Layout" button + - [ ] "Reset to Default" button + - [ ] "Export Layout" button + - [ ] "Import Layout" button +- [ ] Add character management section + - [ ] Current character selector + - [ ] "Create New Character" button + - [ ] "Switch Character" button + - [ ] "Delete Character" button + +**Acceptance Criteria:** +- All dashboard settings in settings modal +- Widget toggles control availability +- Schema management accessible +- Layout management functional +- Character management works + +--- + +### Task 7.4: Keyboard Shortcuts +**Dependencies:** Epic 1 +**Estimated Time:** 2-3 days + +- [ ] Create keyboard shortcut system (`src/systems/ui/shortcuts.js`) + - [ ] Register keyboard event listeners + - [ ] Map shortcuts to actions + - [ ] Handle modifier keys (Ctrl, Alt, Shift) + - [ ] Prevent conflicts with ST shortcuts +- [ ] Implement tab shortcuts + - [ ] Ctrl+1-9 to switch tabs + - [ ] Ctrl+Tab to next tab + - [ ] Ctrl+Shift+Tab to previous tab +- [ ] Implement edit mode shortcuts + - [ ] E to toggle edit mode + - [ ] Delete to remove focused widget + - [ ] Escape to cancel drag/resize + - [ ] Ctrl+S to save layout + - [ ] Ctrl+Z to undo (if implemented) +- [ ] Add shortcut hints + - [ ] Tooltip on buttons showing shortcut + - [ ] "Keyboard Shortcuts" help modal + - [ ] Visual indicators for active shortcuts +- [ ] Make shortcuts configurable + - [ ] Allow users to remap shortcuts + - [ ] Save custom shortcuts to settings + - [ ] Validate no conflicts + +**Acceptance Criteria:** +- Keyboard shortcuts work correctly +- Don't conflict with SillyTavern shortcuts +- Hints visible in UI +- Shortcuts configurable +- Help modal lists all shortcuts + +--- + +### Task 7.5: Accessibility Improvements +**Dependencies:** Epic 1, Epic 2 +**Estimated Time:** 3-4 days + +- [ ] Add ARIA labels to all interactive elements + - [ ] Buttons have aria-label + - [ ] Widgets have aria-role + - [ ] Tab navigation has aria-selected + - [ ] Edit controls have aria-pressed +- [ ] Implement keyboard navigation + - [ ] Tab through widgets + - [ ] Arrow keys to move between widgets + - [ ] Enter to activate/edit widget + - [ ] Space to toggle checkboxes/buttons +- [ ] Add focus indicators + - [ ] Visible focus ring on all interactive elements + - [ ] Focus stays visible during keyboard navigation + - [ ] Focus returns to logical place after modal closes +- [ ] Implement screen reader support + - [ ] Descriptive labels for all widgets + - [ ] Announce state changes + - [ ] Live regions for dynamic content + - [ ] Skip links for navigation +- [ ] Test with accessibility tools + - [ ] axe DevTools + - [ ] Lighthouse accessibility audit + - [ ] Screen reader testing (NVDA/JAWS) + - [ ] Keyboard-only navigation testing + +**Acceptance Criteria:** +- All interactive elements keyboard accessible +- Screen readers can navigate interface +- Focus indicators clear and visible +- Passes accessibility audits +- No critical accessibility issues + +--- + +### Task 7.6: Performance Optimization +**Dependencies:** All previous tasks +**Estimated Time:** 3-4 days + +- [ ] Profile rendering performance + - [ ] Use Chrome DevTools Performance tab + - [ ] Identify slow operations + - [ ] Measure render times + - [ ] Find memory leaks +- [ ] Optimize widget rendering + - [ ] Implement virtual scrolling for long lists + - [ ] Debounce expensive operations + - [ ] Use requestAnimationFrame for animations + - [ ] Cache rendered content when possible +- [ ] Optimize drag-and-drop + - [ ] Throttle position updates + - [ ] Use CSS transforms for positioning + - [ ] Optimize collision detection + - [ ] Reduce DOM manipulations +- [ ] Optimize formula calculations + - [ ] Memoize formula results + - [ ] Calculate only changed formulas + - [ ] Batch recalculations + - [ ] Use Web Workers for complex formulas +- [ ] Reduce bundle size + - [ ] Lazy load widgets + - [ ] Code split by feature + - [ ] Minify production builds + - [ ] Remove unused dependencies + +**Acceptance Criteria:** +- Widgets render in <100ms +- Drag-and-drop feels smooth (60fps) +- No memory leaks after extended use +- Bundle size reasonable (<500KB) +- Performance acceptable on low-end devices + +--- + +### Task 7.7: Error Handling & Recovery +**Dependencies:** All previous tasks +**Estimated Time:** 2-3 days + +- [ ] Implement global error boundary + - [ ] Catch JavaScript errors + - [ ] Show user-friendly error message + - [ ] Log errors to console + - [ ] Offer recovery options +- [ ] Add error handling to critical operations + - [ ] Schema loading/parsing errors + - [ ] Formula evaluation errors + - [ ] Save/load failures + - [ ] Network errors +- [ ] Implement auto-recovery + - [ ] Retry failed operations + - [ ] Fall back to defaults on corruption + - [ ] Restore from backup + - [ ] Clear corrupted data +- [ ] Add error reporting + - [ ] Export error logs + - [ ] Copy error details to clipboard + - [ ] Generate diagnostic report + - [ ] Link to GitHub issues +- [ ] Create error messages + - [ ] Clear and actionable + - [ ] Avoid technical jargon + - [ ] Suggest solutions + - [ ] Link to documentation + +**Acceptance Criteria:** +- Errors don't crash extension +- User-friendly error messages +- Recovery options work +- Can export error logs for debugging +- Common errors documented + +--- + +**Epic 7 Complete When:** +- [x] Mobile responsive layout works +- [x] Animations smooth and toggleable +- [x] Settings panel integrated +- [x] Keyboard shortcuts functional +- [x] Accessibility requirements met +- [x] Performance optimized +- [x] Error handling robust + +--- + +## Epic 8: Documentation & Migration + +**Status:** Not Started +**Dependencies:** All previous epics +**Estimated Duration:** 1-2 weeks +**Goal:** User documentation, migration tools, testing + +### Task 8.1: User Documentation +**Dependencies:** All features complete +**Estimated Time:** 4-5 days + +- [ ] Write user guide (`docs/user-guide.md`) + - [ ] Getting started + - [ ] Dashboard basics + - [ ] Creating and managing tabs + - [ ] Adding and arranging widgets + - [ ] Edit mode guide + - [ ] Keyboard shortcuts reference +- [ ] Write schema guide (`docs/schema-guide.md`) + - [ ] What are schemas? + - [ ] Using schema templates + - [ ] Creating custom schemas + - [ ] YAML syntax guide + - [ ] Component types reference + - [ ] Formula syntax guide + - [ ] Importing/exporting schemas +- [ ] Write widget reference (`docs/widget-reference.md`) + - [ ] List of all widgets + - [ ] Widget descriptions + - [ ] Configuration options + - [ ] Usage examples + - [ ] Screenshots +- [ ] Create video tutorials + - [ ] Dashboard overview + - [ ] Creating a custom layout + - [ ] Using schema templates + - [ ] Building a custom schema +- [ ] Write troubleshooting guide + - [ ] Common issues and solutions + - [ ] Error messages explained + - [ ] Debug mode instructions + - [ ] How to report bugs + +**Acceptance Criteria:** +- All major features documented +- Guides include examples and screenshots +- Troubleshooting covers common issues +- Video tutorials available +- Documentation accessible and clear + +--- + +### Task 8.2: Migration Wizard +**Dependencies:** Epic 3, Epic 4 +**Estimated Time:** 3-4 days + +- [ ] Create migration wizard UI + - [ ] Welcome screen explaining migration + - [ ] Preview of new features + - [ ] Backup warning + - [ ] Step-by-step wizard +- [ ] Implement data migration + - [ ] Detect current data format + - [ ] Convert hardcoded stats to D&D schema + - [ ] Map classic stats to core abilities + - [ ] Convert inventory to schema format + - [ ] Preserve custom values +- [ ] Implement layout migration + - [ ] Convert current layout to dashboard format + - [ ] Create default tabs + - [ ] Position widgets based on current layout + - [ ] Preserve panel position and theme +- [ ] Add backup/restore + - [ ] Export current data before migration + - [ ] Save backup to file + - [ ] Restore from backup if migration fails + - [ ] Keep backup accessible +- [ ] Test migration scenarios + - [ ] Fresh install (no migration needed) + - [ ] v1.x user with data + - [ ] User with custom settings + - [ ] User with inventory v2 data + +**Acceptance Criteria:** +- Migration wizard guides users smoothly +- All existing data preserved +- No data loss during migration +- Backup created automatically +- Can restore from backup if needed +- Migration tested with various scenarios + +--- + +### Task 8.3: Schema Template Creation +**Dependencies:** Epic 5 complete +**Estimated Time:** 5-6 days + +- [ ] Create D&D 5e complete schema + - [ ] All ability scores + - [ ] Ability modifiers (formulas) + - [ ] Hit points and hit dice + - [ ] Armor class (formula) + - [ ] All skills with proficiency + - [ ] Saving throws + - [ ] Spell slots + - [ ] Features and traits + - [ ] Equipment and inventory + - [ ] Prompt templates +- [ ] Create Pathfinder 2e schema + - [ ] Ability scores + - [ ] Ancestry and heritage + - [ ] Class and level + - [ ] Skills with proficiency levels + - [ ] Action economy + - [ ] Conditions + - [ ] Bulk inventory system +- [ ] Create Cyberpunk RED schema + - [ ] Stats (INT, REF, TECH, etc.) + - [ ] Derived stats (HP, humanity) + - [ ] Skills + - [ ] Cyberware and humanity loss + - [ ] Gear and inventory + - [ ] Critical injuries +- [ ] Create World of Darkness schema + - [ ] Attributes (Physical, Social, Mental) + - [ ] Skills + - [ ] Health (Bashing, Lethal, Aggravated) + - [ ] Willpower + - [ ] Merits and flaws + - [ ] Experience points +- [ ] Create blank starter template + - [ ] Basic structure example + - [ ] Comments explaining each section + - [ ] Sample component definitions + - [ ] Instructions for customization +- [ ] Document each template + - [ ] README for each system + - [ ] Usage instructions + - [ ] Customization examples + - [ ] Known limitations + +**Acceptance Criteria:** +- At least 4 complete schema templates +- Each template accurate to system rules +- Templates well-documented +- Users can start from templates +- Templates showcase different schema features + +--- + +### Task 8.4: Testing & QA +**Dependencies:** All features complete +**Estimated Time:** 5-6 days + +- [ ] Create testing checklist + - [ ] All features and workflows + - [ ] Different browsers + - [ ] Desktop and mobile + - [ ] Various screen sizes +- [ ] Test dashboard system + - [ ] Create/rename/delete tabs + - [ ] Drag-and-drop widgets + - [ ] Resize widgets + - [ ] Edit mode toggle + - [ ] Layout persistence + - [ ] Export/import layouts +- [ ] Test all widgets + - [ ] Each core widget + - [ ] Each schema widget + - [ ] Widget configuration + - [ ] Data editing + - [ ] Data persistence +- [ ] Test schema system + - [ ] Create custom schema + - [ ] Import/export schemas + - [ ] YAML editor + - [ ] Visual builder + - [ ] Formula evaluation + - [ ] Validation +- [ ] Test AI integration + - [ ] Together mode with schema + - [ ] Separate mode with schema + - [ ] Prompt generation + - [ ] Response parsing + - [ ] Fallback to hardcoded +- [ ] Test mobile responsiveness + - [ ] Layout on mobile + - [ ] Touch interactions + - [ ] Edit mode on mobile + - [ ] Performance on mobile +- [ ] Test migration + - [ ] Migrate from v1.x + - [ ] Data preservation + - [ ] Backup/restore +- [ ] Test edge cases + - [ ] Very long character names + - [ ] Large schemas + - [ ] Many widgets on grid + - [ ] Complex formulas + - [ ] Rapid interactions + - [ ] Network failures +- [ ] Performance testing + - [ ] Load time + - [ ] Render time + - [ ] Memory usage + - [ ] Battery impact (mobile) +- [ ] Accessibility testing + - [ ] Screen reader + - [ ] Keyboard navigation + - [ ] Focus indicators + - [ ] Color contrast + +**Acceptance Criteria:** +- All features tested thoroughly +- Critical bugs fixed +- Performance acceptable +- Accessibility requirements met +- Mobile experience good +- No data loss scenarios +- Migration works reliably + +--- + +### Task 8.5: Release Preparation +**Dependencies:** Task 8.4 complete +**Estimated Time:** 2-3 days + +- [ ] Update version number + - [ ] Bump to 2.0.0 in manifest.json + - [ ] Update package.json if present + - [ ] Update documentation versions +- [ ] Write changelog + - [ ] List all new features + - [ ] Document breaking changes + - [ ] Note migration requirements + - [ ] Thank contributors +- [ ] Create release notes + - [ ] Highlight major features + - [ ] Include screenshots/GIFs + - [ ] Link to documentation + - [ ] Mention migration wizard +- [ ] Update README + - [ ] Add v2.0 features + - [ ] Update screenshots + - [ ] Add schema system section + - [ ] Update installation instructions +- [ ] Prepare demo content + - [ ] Sample schemas + - [ ] Example layouts + - [ ] Tutorial data + - [ ] Video walkthrough +- [ ] Tag release + - [ ] Create v2.0.0 git tag + - [ ] Push to repository + - [ ] Create GitHub release + - [ ] Upload assets + +**Acceptance Criteria:** +- Version bumped correctly +- Changelog comprehensive +- Release notes engaging +- README up to date +- Demo content helpful +- Release tagged and published + +--- + +**Epic 8 Complete When:** +- [x] User documentation complete +- [x] Migration wizard tested +- [x] Schema templates created +- [x] All testing completed +- [x] Release prepared and published + +--- + +## Success Criteria (v2.0 Complete) + +### Core Functionality +- [ ] Dashboard system with draggable widgets works perfectly +- [ ] Users can create/manage unlimited tabs +- [ ] Widget library contains all planned widgets +- [ ] Edit mode intuitive and bug-free +- [ ] Layout persists reliably across sessions + +### Schema System +- [ ] Schema system fully functional +- [ ] Formula engine evaluates correctly +- [ ] YAML import/export works +- [ ] Schema editor (YAML + visual) complete +- [ ] At least 4 schema templates available + +### Integration +- [ ] AI integration works with schemas +- [ ] Both Together and Separate modes supported +- [ ] Parser extracts schema data correctly +- [ ] Existing functionality preserved for non-schema users + +### Polish +- [ ] Mobile responsive and functional +- [ ] Animations smooth (and toggleable) +- [ ] Keyboard shortcuts work +- [ ] Accessibility standards met +- [ ] Performance acceptable + +### Documentation +- [ ] User guide comprehensive +- [ ] Schema guide clear +- [ ] Troubleshooting covers common issues +- [ ] Migration wizard reliable +- [ ] All features documented + +--- + +## Risk Mitigation + +### High-Risk Areas + +**Risk 1: Grid System Complexity** +- **Mitigation:** Use established grid layout algorithms, test extensively +- **Contingency:** Consider using gridstack.js library if custom implementation fails + +**Risk 2: Formula Engine Security** +- **Mitigation:** Whitelist functions, sandbox execution, timeout limits +- **Contingency:** Limit formula complexity, provide safe alternatives + +**Risk 3: Mobile Performance** +- **Mitigation:** Profile early, optimize rendering, virtualize long lists +- **Contingency:** Simplify mobile layout, disable animations on mobile + +**Risk 4: Migration Data Loss** +- **Mitigation:** Create automatic backups, test migration extensively +- **Contingency:** Manual data recovery tools, rollback mechanism + +**Risk 5: Backward Compatibility** +- **Mitigation:** Keep hardcoded mode functional, fallbacks everywhere +- **Contingency:** Maintain v1.x branch, provide downgrade instructions + +--- + +## Timeline Estimate + +| Epic | Duration | Dependencies | Start After | +|------|----------|--------------|-------------| +| Epic 1: Dashboard Infrastructure | 2 weeks | None | Immediately | +| Epic 2: Widget Conversion | 2-3 weeks | Epic 1 | Week 3 | +| Epic 3: Schema Infrastructure | 3-4 weeks | None (parallel) | Week 1 | +| Epic 4: Schema Widgets | 3-4 weeks | Epic 1, 3 | Week 5 | +| Epic 5: Schema Editor | 2-3 weeks | Epic 3 | Week 5 | +| Epic 6: AI Integration | 2-3 weeks | Epic 3, 4 | Week 8 | +| Epic 7: Polish & Mobile | 2-3 weeks | All | Week 10 | +| Epic 8: Documentation | 1-2 weeks | All | Week 12 | + +**Total Estimated Duration:** 12-14 weeks (3-3.5 months) + +**Critical Path:** Epic 1 → Epic 2 → Epic 4 → Epic 7 → Epic 8 + +--- + +## Notes for Implementation + +### Daily Workflow +1. Check current epic status +2. Pick next task with no blockers +3. Mark task as in progress (update checkbox to `[~]` or add comment) +4. Work on task +5. Test task completion +6. Mark task complete (`[x]`) +7. Update epic progress +8. Commit changes with conventional commit message +9. Push to branch + +### When Stuck +- Check dependencies (are they really complete?) +- Review technical design docs +- Ask for help/clarification +- Consider breaking task into smaller subtasks +- Document blockers and move to next task + +### Testing Strategy +- Test each task after completion +- Manual testing in browser with SillyTavern +- Test on different screen sizes +- Test with different AI backends if possible +- Keep debug mode enabled during development +- Check console for errors/warnings + +### Code Quality +- Follow existing code style +- Use JSDoc for type hints +- Comment complex logic +- Extract reusable functions +- Keep functions focused and small +- Handle errors gracefully +- Add logging for debugging + +--- + +**Last Updated:** 2025-10-23 +**Next Review:** After each epic completion diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..37277e2 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,266 @@ +# RPG Companion Documentation + +This directory contains all design and implementation documentation for RPG Companion v2.0. + +--- + +## Documentation Index + +### Implementation + +- **[IMPLEMENTATION_PLAN.md](./IMPLEMENTATION_PLAN.md)** - Complete implementation roadmap + - 8 epics with detailed tasks and subtasks + - Checkboxes for progress tracking + - Dependencies and timeline estimates + - Each task builds on the previous one + +### Feature Design + +- **[Widget Dashboard System](./features/widget-dashboard-system.md)** - Dashboard architecture + - Dynamic tabs with create/rename/delete + - Widget grid system with drag-and-drop + - Edit mode and layout persistence + - Mobile responsive design + - Widget development guide + +- **[Schema System Architecture](./features/schema-system-architecture.md)** - Schema system design + - Entity-Component-System (ECS) pattern + - YAML-based system definitions + - Formula engine with @ references + - Character instance validation + - Storage layer (IndexedDB + File System API) + - AI prompt generation and parsing + +--- + +## Quick Start + +### For Developers + +1. **Start here:** Read [IMPLEMENTATION_PLAN.md](./IMPLEMENTATION_PLAN.md) +2. **Understand the dashboard:** Read [Widget Dashboard System](./features/widget-dashboard-system.md) +3. **Understand schemas:** Read [Schema System Architecture](./features/schema-system-architecture.md) +4. **Pick a task:** Find unchecked tasks in implementation plan +5. **Build incrementally:** Each task builds on previous ones + +### For Contributors + +- All major features documented in `/docs/features/` +- Implementation plan tracks progress with checkboxes +- Each epic is a major deliverable +- Commit messages should reference task numbers +- Example: `feat: implement grid engine core (Task 1.1)` + +--- + +## Architecture Overview + +``` +RPG Companion v2.0 Architecture + +┌─────────────────────────────────────────────────────────┐ +│ User Interface Layer │ +│ ┌───────────────┐ ┌───────────────┐ ┌──────────────┐│ +│ │ Tab Navigator │ │ Widget Grid │ │ Edit Mode UI ││ +│ └───────────────┘ └───────────────┘ └──────────────┘│ +└─────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────┐ +│ Widget System Layer │ +│ ┌───────────────┐ ┌───────────────┐ ┌──────────────┐│ +│ │ Widget │ │ Grid Engine │ │ Drag & Drop ││ +│ │ Registry │ │ │ │ Handler ││ +│ └───────────────┘ └───────────────┘ └──────────────┘│ +└─────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────┐ +│ Schema System Layer │ +│ ┌───────────────┐ ┌───────────────┐ ┌──────────────┐│ +│ │ Schema │ │ Formula │ │ Character ││ +│ │ Validator │ │ Engine │ │ Manager ││ +│ └───────────────┘ └───────────────┘ └──────────────┘│ +└─────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────┐ +│ Storage Layer │ +│ ┌───────────────┐ ┌───────────────┐ ┌──────────────┐│ +│ │ IndexedDB │ │ File System │ │ Extension ││ +│ │ │ │ Access API │ │ Settings ││ +│ └───────────────┘ └───────────────┘ └──────────────┘│ +└─────────────────────────────────────────────────────────┘ +``` + +--- + +## Key Concepts + +### Widget Dashboard +- **Dynamic Tabs:** Users create unlimited tabs with custom names +- **Widget Grid:** 12-column responsive grid with drag-and-drop +- **Edit Mode:** Visual editor for arranging widgets +- **Persistence:** Layouts save automatically + +### Schema System +- **System Definition:** YAML files define RPG system rules +- **Character Instance:** JSON data validated against schema +- **Formula Engine:** Calculate derived stats with @ references +- **AI Integration:** Dynamic prompts and parsing based on schema + +### Progressive Enhancement +- **No Modes:** Single flexible system with toggles +- **Backward Compatible:** Existing features work without schemas +- **Opt-In Complexity:** Users enable advanced features when ready + +--- + +## Epics Overview + +| # | Epic | Status | Duration | Description | +|---|------|--------|----------|-------------| +| 1 | Dashboard Infrastructure | Not Started | 2 weeks | Core grid engine, tabs, drag-and-drop | +| 2 | Widget Conversion | Not Started | 2-3 weeks | Convert existing sections to widgets | +| 3 | Schema Infrastructure | Not Started | 3-4 weeks | YAML parser, formula engine, validation | +| 4 | Schema-Driven Widgets | Not Started | 3-4 weeks | Widgets that render from schemas | +| 5 | Schema Editor UI | Not Started | 2-3 weeks | YAML editor and visual builder | +| 6 | AI Integration | Not Started | 2-3 weeks | Schema-based prompts and parsing | +| 7 | Polish & Mobile | Not Started | 2-3 weeks | Responsive, animations, accessibility | +| 8 | Documentation | Not Started | 1-2 weeks | User docs, migration, templates | + +**Total Estimated Time:** 12-14 weeks (3-3.5 months) + +--- + +## Design Principles + +### KISS (Keep It Simple, Stupid) +- Vanilla JavaScript, no frameworks +- Progressive enhancement over feature flags +- Clear APIs over clever abstractions + +### User Freedom +> "This is SillyTavern - users should be able to do whatever the fuck they want" + +- No arbitrary limitations +- Everything customizable +- Full GUI editing +- Import/export everything + +### Backward Compatibility +- Existing features must keep working +- Graceful fallbacks everywhere +- Migration wizard for v1.x users +- No data loss scenarios + +### Performance First +- Widgets lazy-load +- Formulas memoized +- Drag-and-drop throttled +- Mobile optimized + +--- + +## Contributing + +### Before Starting a Task + +1. Read the task description in [IMPLEMENTATION_PLAN.md](./IMPLEMENTATION_PLAN.md) +2. Check dependencies are complete +3. Review relevant design docs +4. Understand acceptance criteria + +### While Working + +1. Mark task in progress (comment or `[~]`) +2. Follow code style in CLAUDE.md +3. Test incrementally +4. Check console for errors +5. Add debug logging + +### After Completing + +1. Test acceptance criteria +2. Mark task complete (`[x]`) +3. Commit with conventional commit message +4. Update epic progress +5. Document any blockers or deviations + +### Commit Message Format + +``` +type(scope): description + +Examples: +feat(dashboard): implement grid engine core (Task 1.1) +fix(widgets): resolve user stats rendering bug +docs(schema): add formula engine examples +refactor(storage): optimize IndexedDB queries +``` + +--- + +## Testing Strategy + +### Manual Testing +- Test in SillyTavern with extension enabled +- Check console for errors +- Test on different screen sizes +- Verify data persistence +- Test edge cases + +### Browser Compatibility +- Chrome/Chromium (primary) +- Firefox +- Safari (if possible) +- Mobile browsers + +### Accessibility +- Keyboard navigation +- Screen reader support +- Focus indicators +- Color contrast + +--- + +## Support + +### Getting Help + +- Check [CLAUDE.md](../CLAUDE.md) for development guidelines +- Review relevant design docs in `/docs/features/` +- Check implementation plan for dependencies +- Ask questions in Discord + +### Reporting Issues + +When stuck or blocked: +- Document the blocker in implementation plan +- Include error messages and logs +- Describe what you tried +- Note which task is blocked + +--- + +## Future Enhancements + +Ideas for post-v2.0: + +- Widget marketplace for community widgets +- Layout templates for different RPG systems +- Widget linking (skills affect stats, etc.) +- Conditional widget visibility +- Real-time collaboration +- Cloud sync +- Advanced formula functions +- Visual node-based formula editor +- Drag-and-drop formula builder + +--- + +## License + +See [LICENSE](../LICENSE) for details (AGPL-3.0). + +--- + +**Last Updated:** 2025-10-23 +**Version:** 2.0.0-dev diff --git a/docs/features/schema-system-architecture.md b/docs/features/schema-system-architecture.md new file mode 100644 index 0000000..ada12a4 --- /dev/null +++ b/docs/features/schema-system-architecture.md @@ -0,0 +1,1318 @@ +# Schema System Architecture + +**Status:** Design Phase +**Priority:** Critical (Tier 1 Feature - 16% vote priority) +**Target Version:** 2.0.0 + +--- + +## Overview + +The Schema System allows users to define custom RPG systems using human-readable YAML files instead of being locked into hardcoded stats. Inspired by Gemini Deep Research recommendations and Entity-Component-System (ECS) patterns. + +### Vision +> Transform RPG Companion from a fixed D&D-style tracker into a universal RPG system that adapts to ANY tabletop game: Pathfinder, Cyberpunk RED, World of Darkness, homebrew systems, etc. + +--- + +## Architecture Overview + +### Three-Layer System + +``` +┌─────────────────────────────────────────┐ +│ System Definition (YAML) │ ← Design Time +│ Rules, structure, formulas │ +└─────────────────────────────────────────┘ + ↓ validates +┌─────────────────────────────────────────┐ +│ Character Instance (JSON) │ ← Run Time +│ Actual character data │ +└─────────────────────────────────────────┘ + ↓ renders via +┌─────────────────────────────────────────┐ +│ Widget Dashboard (UI) │ ← User Interface +│ Dynamic widget rendering │ +└─────────────────────────────────────────┘ +``` + +--- + +## System Definition Layer (YAML) + +### Schema Structure + +```yaml +# dnd5e.yaml - Example D&D 5th Edition Schema + +meta: + name: "D&D 5th Edition" + version: "1.0.0" + author: "RPG Companion Community" + description: "Official D&D 5e ruleset" + tags: ["fantasy", "d20", "official"] + +components: + + # Core Abilities (STR, DEX, CON, etc.) + coreAbilities: + type: object + label: "Ability Scores" + icon: "🎲" + properties: + strength: + type: number + label: "Strength" + abbr: "STR" + min: 1 + max: 30 + default: 10 + + dexterity: + type: number + label: "Dexterity" + abbr: "DEX" + min: 1 + max: 30 + default: 10 + + constitution: + type: number + label: "Constitution" + abbr: "CON" + min: 1 + max: 30 + default: 10 + + intelligence: + type: number + label: "Intelligence" + abbr: "INT" + min: 1 + max: 30 + default: 10 + + wisdom: + type: number + label: "Wisdom" + abbr: "WIS" + min: 1 + max: 30 + default: 10 + + charisma: + type: number + label: "Charisma" + abbr: "CHA" + min: 1 + max: 30 + default: 10 + + # Derived Stats (calculated from abilities) + abilityModifiers: + type: object + label: "Ability Modifiers" + properties: + str_mod: + type: formula + formula: "floor((@coreAbilities.strength - 10) / 2)" + + dex_mod: + type: formula + formula: "floor((@coreAbilities.dexterity - 10) / 2)" + + con_mod: + type: formula + formula: "floor((@coreAbilities.constitution - 10) / 2)" + + int_mod: + type: formula + formula: "floor((@coreAbilities.intelligence - 10) / 2)" + + wis_mod: + type: formula + formula: "floor((@coreAbilities.wisdom - 10) / 2)" + + cha_mod: + type: formula + formula: "floor((@coreAbilities.charisma - 10) / 2)" + + # Resources (pools that track usage) + resources: + type: list + label: "Resources" + icon: "⚡" + items: + hitPoints: + type: resource + label: "Hit Points" + abbr: "HP" + current: 0 + max: + type: formula + formula: "10 + @abilityModifiers.con_mod" + color: "#cc3333" + display: "bar" + + spellSlots: + type: resource + label: "Spell Slots" + abbr: "Spells" + current: 0 + max: 0 + color: "#3366cc" + display: "dots" + + # Skills + skills: + type: list + label: "Skills" + icon: "⚔️" + items: + acrobatics: + type: number + label: "Acrobatics" + baseAbility: "dexterity" + proficient: false + expertise: false + + animalHandling: + type: number + label: "Animal Handling" + baseAbility: "wisdom" + proficient: false + expertise: false + + arcana: + type: number + label: "Arcana" + baseAbility: "intelligence" + proficient: false + expertise: false + + # ... more skills + + # Conditions/Status Effects + statusEffects: + type: list + label: "Conditions" + icon: "✨" + items: + name: + type: text + label: "Condition Name" + + duration: + type: number + label: "Rounds Remaining" + min: 0 + + effect: + type: text + label: "Effect Description" + + # Inventory (simplified) + inventory: + type: object + label: "Equipment" + icon: "🎒" + properties: + carried: + type: list + label: "Carried Items" + + worn: + type: list + label: "Worn Armor" + + gold: + type: number + label: "Gold Pieces" + abbr: "GP" + default: 0 + + # Character Identity + identity: + type: object + label: "Character Info" + properties: + name: + type: text + label: "Name" + required: true + + race: + type: text + label: "Race" + + class: + type: text + label: "Class" + + level: + type: number + label: "Level" + min: 1 + max: 20 + default: 1 + + background: + type: text + label: "Background" + +# Prompt template for AI generation +prompts: + stats: | + Character Stats + --- + HP: [@resources.hitPoints.current/@resources.hitPoints.max] + Spell Slots: [@resources.spellSlots.current/@resources.spellSlots.max] + Conditions: [List active conditions or "None"] + + skills: | + Skills + --- + [Skill Name]: [Modifier] | [Proficiency Status] + (List all relevant skills for the current scene) + +# Widget layout suggestions +layout: + defaultTabs: + - name: "Combat" + widgets: + - type: "resources" + component: "resources" + x: 0 + y: 0 + w: 4 + h: 3 + + - type: "skills" + component: "skills" + filter: ["acrobatics", "athletics", "stealth"] + x: 4 + y: 0 + w: 4 + h: 4 + + - type: "statusEffects" + component: "statusEffects" + x: 8 + y: 0 + w: 4 + h: 2 + + - name: "Character" + widgets: + - type: "coreAbilities" + component: "coreAbilities" + x: 0 + y: 0 + w: 6 + h: 3 + + - type: "identity" + component: "identity" + x: 6 + y: 0 + w: 6 + h: 3 +``` + +--- + +## Component Types + +### 1. Object Components +Group related properties together. + +```yaml +identity: + type: object + properties: + name: + type: text + age: + type: number +``` + +**Rendered as:** Card with labeled fields + +### 2. List Components +Collections of items. + +```yaml +skills: + type: list + items: + name: + type: text + value: + type: number +``` + +**Rendered as:** Vertical list, table, or grid + +### 3. Resource Components +Tracked pools with current/max values. + +```yaml +hitPoints: + type: resource + current: 10 + max: 20 + display: "bar" +``` + +**Rendered as:** Progress bar or numeric display + +### 4. Formula Components +Derived values calculated from other components. + +```yaml +armorClass: + type: formula + formula: "10 + @abilityModifiers.dex_mod + @equipment.armor.bonus" +``` + +**Rendered as:** Read-only calculated value + +--- + +## Character Instance Layer (JSON) + +### Instance Structure + +Character data stored in `extensionSettings.characterInstance`: + +```javascript +extensionSettings.characterInstance = { + schemaId: "dnd5e-v1.0.0", // Which schema this uses + schemaVersion: "1.0.0", // Schema version + + data: { + // Component data matching schema structure + coreAbilities: { + strength: 16, + dexterity: 14, + constitution: 15, + intelligence: 10, + wisdom: 12, + charisma: 8 + }, + + abilityModifiers: { + // Calculated automatically via formula + str_mod: 3, + dex_mod: 2, + con_mod: 2, + int_mod: 0, + wis_mod: 1, + cha_mod: -1 + }, + + resources: { + hitPoints: { + current: 12, + max: 22 + }, + spellSlots: { + current: 3, + max: 4 + } + }, + + skills: [ + { name: "Acrobatics", value: 2, proficient: false }, + { name: "Athletics", value: 5, proficient: true }, + { name: "Stealth", value: 4, proficient: true } + // ... more skills + ], + + statusEffects: [ + { name: "Blessed", duration: 10, effect: "+1d4 to attacks" } + ], + + inventory: { + carried: ["Longsword", "Shield", "Healing Potion x2"], + worn: ["Chain Mail"], + gold: 47 + }, + + identity: { + name: "Ragnar", + race: "Human", + class: "Fighter", + level: 3, + background: "Soldier" + } + }, + + // Metadata + createdAt: "2025-10-23T12:00:00Z", + updatedAt: "2025-10-23T14:30:00Z" +}; +``` + +--- + +## Formula Engine + +### Formula Syntax + +```javascript +// @ references components in character instance +@coreAbilities.strength // → 16 +@abilityModifiers.str_mod // → 3 +@resources.hitPoints.max // → 22 + +// Math operators +floor((@coreAbilities.strength - 10) / 2) // → 3 +@coreAbilities.strength + 5 // → 21 +(@level * 2) + @abilityModifiers.con_mod // → 8 + +// Conditional (future) +@coreAbilities.strength > 15 ? "Strong" : "Weak" +``` + +### Safe Expression Parser + +```javascript +// src/systems/schema/formulaEngine.js + +export class FormulaEngine { + constructor(characterData) { + this.data = characterData; + this.cache = new Map(); // Memoize calculated values + } + + // Evaluate formula string + evaluate(formula) { + // Check cache first + if (this.cache.has(formula)) { + return this.cache.get(formula); + } + + // Replace @ references with actual values + const resolved = this.resolveReferences(formula); + + // Safe eval using Function constructor (sandboxed) + try { + const result = this.safeEval(resolved); + this.cache.set(formula, result); + return result; + } catch (error) { + console.error('[Formula Engine] Error evaluating:', formula, error); + return 0; // Fallback + } + } + + // Replace @component.path with actual values + resolveReferences(formula) { + const refRegex = /@([a-zA-Z0-9_.]+)/g; + + return formula.replace(refRegex, (match, path) => { + const value = this.getValueByPath(path); + return value !== undefined ? value : 0; + }); + } + + // Get nested value from character data + getValueByPath(path) { + const parts = path.split('.'); + let value = this.data; + + for (const part of parts) { + if (value && typeof value === 'object') { + value = value[part]; + } else { + return undefined; + } + } + + return value; + } + + // Safe evaluation (whitelist functions) + safeEval(expression) { + const allowedFunctions = { + floor: Math.floor, + ceil: Math.ceil, + round: Math.round, + abs: Math.abs, + min: Math.min, + max: Math.max + }; + + // Create sandboxed function + const func = new Function(...Object.keys(allowedFunctions), `return ${expression}`); + + // Execute with whitelisted functions + return func(...Object.values(allowedFunctions)); + } + + // Clear cache (call when character data changes) + invalidateCache() { + this.cache.clear(); + } +} + +// Usage: +const engine = new FormulaEngine(characterInstance.data); +const strMod = engine.evaluate("floor((@coreAbilities.strength - 10) / 2)"); +console.log('STR Modifier:', strMod); // → 3 +``` + +--- + +## Schema Validation + +### JSON Schema Integration + +Use JSON Schema to validate character instances: + +```javascript +// src/systems/schema/validator.js + +import Ajv from 'ajv'; // Lightweight JSON Schema validator + +export class SchemaValidator { + constructor() { + this.ajv = new Ajv({ allErrors: true }); + } + + // Convert YAML schema to JSON Schema + compileSchema(yamlSchema) { + const jsonSchema = { + type: 'object', + properties: {}, + required: [] + }; + + // Convert each component to JSON Schema property + for (const [componentName, component] of Object.entries(yamlSchema.components)) { + jsonSchema.properties[componentName] = this.convertComponent(component); + + if (component.required) { + jsonSchema.required.push(componentName); + } + } + + return this.ajv.compile(jsonSchema); + } + + // Convert component definition to JSON Schema + convertComponent(component) { + switch (component.type) { + case 'object': + return { + type: 'object', + properties: this.convertProperties(component.properties) + }; + + case 'list': + return { + type: 'array', + items: this.convertComponent(component.items) + }; + + case 'resource': + return { + type: 'object', + properties: { + current: { type: 'number' }, + max: { type: 'number' } + }, + required: ['current', 'max'] + }; + + case 'number': + return { + type: 'number', + minimum: component.min, + maximum: component.max, + default: component.default + }; + + case 'text': + return { + type: 'string', + minLength: component.minLength, + maxLength: component.maxLength + }; + + case 'formula': + // Formulas are always numbers + return { type: 'number' }; + + default: + return { type: 'string' }; + } + } + + // Validate character instance against schema + validate(characterInstance, schema) { + const compiled = this.compileSchema(schema); + const valid = compiled(characterInstance.data); + + if (!valid) { + return { + valid: false, + errors: compiled.errors + }; + } + + return { valid: true }; + } +} +``` + +--- + +## Storage Layer + +### Hybrid Storage Strategy (Gemini Recommendation) + +**IndexedDB** for internal operations: +- Fast local access +- Query capabilities +- No size limits (within reason) + +**File System Access API** for import/export: +- User-friendly YAML files +- Version control compatible +- Shareable with community + +```javascript +// src/systems/schema/storage.js + +export class SchemaStorage { + constructor() { + this.db = null; + this.init(); + } + + async init() { + // Initialize IndexedDB + const request = indexedDB.open('RPGCompanionSchemas', 1); + + request.onupgradeneeded = (event) => { + const db = event.target.result; + + // Schemas store + if (!db.objectStoreNames.contains('schemas')) { + const schemaStore = db.createObjectStore('schemas', { keyPath: 'id' }); + schemaStore.createIndex('name', 'meta.name'); + schemaStore.createIndex('version', 'meta.version'); + } + + // Character instances store + if (!db.objectStoreNames.contains('characters')) { + const charStore = db.createObjectStore('characters', { keyPath: 'id' }); + charStore.createIndex('schemaId', 'schemaId'); + charStore.createIndex('name', 'data.identity.name'); + } + }; + + request.onsuccess = (event) => { + this.db = event.target.result; + console.log('[Schema Storage] IndexedDB initialized'); + }; + } + + // Save schema to IndexedDB + async saveSchema(schema) { + const transaction = this.db.transaction(['schemas'], 'readwrite'); + const store = transaction.objectStore('schemas'); + + const schemaWithId = { + id: `${schema.meta.name}-v${schema.meta.version}`, + ...schema, + savedAt: new Date().toISOString() + }; + + await store.put(schemaWithId); + return schemaWithId.id; + } + + // Load schema from IndexedDB + async loadSchema(schemaId) { + const transaction = this.db.transaction(['schemas'], 'readonly'); + const store = transaction.objectStore('schemas'); + + return new Promise((resolve, reject) => { + const request = store.get(schemaId); + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); + } + + // List all schemas + async listSchemas() { + const transaction = this.db.transaction(['schemas'], 'readonly'); + const store = transaction.objectStore('schemas'); + + return new Promise((resolve, reject) => { + const request = store.getAll(); + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); + } + + // Export schema to YAML file + async exportSchema(schemaId) { + const schema = await this.loadSchema(schemaId); + + // Convert to YAML + const yaml = this.toYAML(schema); + + // Use File System Access API (if available) + if ('showSaveFilePicker' in window) { + const handle = await window.showSaveFilePicker({ + suggestedName: `${schema.meta.name}.yaml`, + types: [{ + description: 'YAML Schema', + accept: { 'text/yaml': ['.yaml', '.yml'] } + }] + }); + + const writable = await handle.createWritable(); + await writable.write(yaml); + await writable.close(); + } else { + // Fallback: download blob + const blob = new Blob([yaml], { type: 'text/yaml' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${schema.meta.name}.yaml`; + a.click(); + URL.revokeObjectURL(url); + } + } + + // Import schema from YAML file + async importSchema() { + // Use File System Access API (if available) + if ('showOpenFilePicker' in window) { + const [handle] = await window.showOpenFilePicker({ + types: [{ + description: 'YAML Schema', + accept: { 'text/yaml': ['.yaml', '.yml'] } + }] + }); + + const file = await handle.getFile(); + const yaml = await file.text(); + const schema = this.fromYAML(yaml); + + // Validate and save + await this.saveSchema(schema); + return schema; + } else { + // Fallback: file input + return new Promise((resolve, reject) => { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.yaml,.yml'; + + input.onchange = async (e) => { + const file = e.target.files[0]; + const yaml = await file.text(); + const schema = this.fromYAML(yaml); + await this.saveSchema(schema); + resolve(schema); + }; + + input.click(); + }); + } + } + + // Convert schema object to YAML string + toYAML(schema) { + // Use js-yaml library + return jsyaml.dump(schema, { + indent: 2, + lineWidth: 80, + noRefs: true + }); + } + + // Parse YAML string to schema object + fromYAML(yaml) { + // Use js-yaml library + return jsyaml.load(yaml); + } +} +``` + +--- + +## Widget Integration + +### Schema-Driven Widget Rendering + +```javascript +// src/systems/dashboard/schemaWidgets.js + +export class SchemaWidgetRenderer { + constructor(schema, characterInstance, formulaEngine) { + this.schema = schema; + this.instance = characterInstance; + this.formulaEngine = formulaEngine; + } + + // Render component as widget + renderComponent(componentName, container, config = {}) { + const component = this.schema.components[componentName]; + const data = this.instance.data[componentName]; + + switch (component.type) { + case 'object': + this.renderObject(component, data, container); + break; + + case 'list': + this.renderList(component, data, container, config); + break; + + case 'resource': + this.renderResource(component, data, container); + break; + + default: + console.warn('Unknown component type:', component.type); + } + } + + // Render object component (e.g., coreAbilities) + renderObject(component, data, container) { + const html = ` +
+

${component.label || 'Component'}

+
+ ${Object.entries(component.properties).map(([key, prop]) => { + const value = data?.[key] ?? prop.default ?? ''; + const displayValue = prop.type === 'formula' + ? this.formulaEngine.evaluate(prop.formula) + : value; + + return ` +
+ + ${prop.type === 'formula' + ? `${displayValue}` + : `` + } + ${prop.abbr ? `(${prop.abbr})` : ''} +
+ `; + }).join('')} +
+
+ `; + + container.innerHTML = html; + + // Add event listeners for editable fields + container.querySelectorAll('input').forEach(input => { + input.addEventListener('change', (e) => { + this.updateProperty( + e.target.dataset.component, + e.target.dataset.property, + e.target.value + ); + }); + }); + } + + // Render list component (e.g., skills) + renderList(component, data, container, config) { + const filter = config.filter; // Optional filter for specific items + + const items = Array.isArray(data) ? data : []; + const filteredItems = filter + ? items.filter(item => filter.includes(item.name)) + : items; + + const html = ` +
+

${component.icon || ''} ${component.label || 'List'}

+
+ ${filteredItems.map((item, index) => ` +
+ ${Object.entries(component.items).map(([key, itemProp]) => { + const value = item[key] ?? ''; + return ` + + ${itemProp.label ? `` : ''} + ${itemProp.type === 'number' + ? `` + : `${value}` + } + + `; + }).join('')} +
+ `).join('')} +
+ +
+ `; + + container.innerHTML = html; + + // Event listeners for list item editing + container.querySelectorAll('input').forEach(input => { + input.addEventListener('change', (e) => { + this.updateListItem( + component.label, + parseInt(e.target.dataset.index), + e.target.dataset.key, + e.target.value + ); + }); + }); + + // Add item button + container.querySelector('.schema-add-item').addEventListener('click', () => { + this.addListItem(component.label); + }); + } + + // Render resource component (e.g., HP) + renderResource(component, data, container) { + const current = data?.current ?? 0; + const max = typeof component.max === 'object' && component.max.type === 'formula' + ? this.formulaEngine.evaluate(component.max.formula) + : (data?.max ?? 0); + + const percentage = max > 0 ? (current / max) * 100 : 0; + const color = component.color || '#3366cc'; + + const html = ` +
+
+

${component.label || 'Resource'}

+ + + / ${max} + +
+ ${component.display === 'bar' + ? `
+
+
` + : `
+ ${Array(max).fill('').map((_, i) => ` + + `).join('')} +
` + } +
+ `; + + container.innerHTML = html; + + // Update current value + container.querySelector('.schema-current').addEventListener('change', (e) => { + this.updateResource(component.label, 'current', parseInt(e.target.value)); + }); + } + + // Update character property + updateProperty(componentName, propertyName, value) { + if (!this.instance.data[componentName]) { + this.instance.data[componentName] = {}; + } + + this.instance.data[componentName][propertyName] = value; + + // Invalidate formula cache + this.formulaEngine.invalidateCache(); + + // Save character instance + this.saveInstance(); + } + + // Update list item + updateListItem(componentName, index, key, value) { + if (!Array.isArray(this.instance.data[componentName])) { + this.instance.data[componentName] = []; + } + + if (!this.instance.data[componentName][index]) { + this.instance.data[componentName][index] = {}; + } + + this.instance.data[componentName][index][key] = value; + + this.saveInstance(); + } + + // Add new list item + addListItem(componentName) { + if (!Array.isArray(this.instance.data[componentName])) { + this.instance.data[componentName] = []; + } + + // Create empty item based on component definition + const component = this.schema.components[componentName]; + const newItem = {}; + + for (const [key, prop] of Object.entries(component.items)) { + newItem[key] = prop.default ?? ''; + } + + this.instance.data[componentName].push(newItem); + + this.saveInstance(); + + // Re-render component + this.renderComponent(componentName, container); + } + + // Update resource value + updateResource(componentName, field, value) { + if (!this.instance.data[componentName]) { + this.instance.data[componentName] = {}; + } + + this.instance.data[componentName][field] = value; + + this.saveInstance(); + } + + // Save character instance + saveInstance() { + // Update timestamp + this.instance.updatedAt = new Date().toISOString(); + + // Save to extension settings + updateExtensionSettings({ characterInstance: this.instance }); + + // Persist to storage + saveSettings(); + } +} +``` + +--- + +## AI Prompt Generation + +### Dynamic Prompt Builder + +```javascript +// src/systems/generation/schemaPromptBuilder.js + +export function generateSchemaPrompt(schema, characterInstance) { + let prompt = ''; + + // Use schema's prompt templates + if (schema.prompts) { + for (const [section, template] of Object.entries(schema.prompts)) { + // Replace [@component.path] with actual values + const resolved = resolvePromptTemplate(template, characterInstance.data); + prompt += resolved + '\n\n'; + } + } else { + // Fallback: auto-generate from components + for (const [name, component] of Object.entries(schema.components)) { + prompt += generateComponentPrompt(name, component, characterInstance.data[name]); + prompt += '\n\n'; + } + } + + return prompt.trim(); +} + +// Resolve [@reference] syntax in prompt templates +function resolvePromptTemplate(template, data) { + const refRegex = /\[@([a-zA-Z0-9_.]+)\]/g; + + return template.replace(refRegex, (match, path) => { + const value = getValueByPath(data, path); + return value !== undefined ? value : '[Unknown]'; + }); +} + +// Auto-generate prompt for a component +function generateComponentPrompt(name, component, data) { + let prompt = `${component.label || name}\n---\n`; + + switch (component.type) { + case 'object': + for (const [key, prop] of Object.entries(component.properties)) { + const value = data?.[key] ?? prop.default ?? ''; + prompt += `${prop.label || key}: ${value}\n`; + } + break; + + case 'list': + if (Array.isArray(data)) { + data.forEach(item => { + const values = Object.entries(component.items) + .map(([key, prop]) => `${prop.label || key}: ${item[key]}`) + .join(' | '); + prompt += `${values}\n`; + }); + } + break; + + case 'resource': + prompt += `${component.label}: ${data?.current ?? 0}/${data?.max ?? 0}\n`; + break; + } + + return prompt; +} +``` + +--- + +## Migration from Hardcoded to Schema + +### Backward Compatibility Strategy + +1. **Keep existing hardcoded mode** as fallback +2. **Detect schema presence** to switch modes +3. **Provide migration wizard** to convert existing characters + +```javascript +// src/systems/schema/migration.js + +export async function migrateToSchema() { + const currentStats = extensionSettings.userStats; + const currentClassicStats = extensionSettings.classicStats; + const currentLevel = extensionSettings.level; + + // Load D&D 5e schema as default + const dnd5eSchema = await schemaStorage.loadSchema('dnd5e-v1.0.0'); + + // Map existing data to schema + const characterInstance = { + schemaId: 'dnd5e-v1.0.0', + schemaVersion: '1.0.0', + data: { + coreAbilities: { + strength: currentClassicStats.str, + dexterity: currentClassicStats.dex, + constitution: currentClassicStats.con, + intelligence: currentClassicStats.int, + wisdom: currentClassicStats.wis, + charisma: currentClassicStats.cha + }, + + resources: { + hitPoints: { + current: Math.round(currentStats.health), + max: 100 // Default, user can change + } + }, + + identity: { + name: getContext().name1, + level: currentLevel + }, + + inventory: currentStats.inventory + }, + + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + }; + + // Save migrated instance + await schemaStorage.saveCharacterInstance(characterInstance); + + // Enable schema mode + updateExtensionSettings({ + schemaMode: true, + activeSchemaId: 'dnd5e-v1.0.0', + characterInstance + }); + + console.log('[Schema Migration] Successfully migrated to D&D 5e schema'); +} +``` + +--- + +## Schema Editor UI + +### Visual Builder (Future) + +``` +┌─────────────────────────────────────────────────────────┐ +│ Schema Editor: D&D 5e [Save] [×]│ +├─────────────────────────────────────────────────────────┤ +│ ┌─ Components ──────┐ ┌─ Editor ───────────────────────┐│ +│ │ + Core Abilities │ │ Component: Core Abilities ││ +│ │ + Ability Mods │ │ ││ +│ │ + Resources │ │ Type: [Object ▼] ││ +│ │ + Skills │ │ Label: [Core Abilities] ││ +│ │ + Status Effects │ │ Icon: [🎲] ││ +│ │ + Inventory │ │ ││ +│ │ + Identity │ │ Properties: ││ +│ │ │ │ ┌──────────────────────────────┐││ +│ │ [+ Add Component] │ │ │ strength │││ +│ └───────────────────┘ │ │ Type: number │││ +│ │ │ Label: "Strength" │││ +│ │ │ Min: 1, Max: 30 │││ +│ │ │ Default: 10 │││ +│ │ │ [Edit] [Delete] │││ +│ │ │ │││ +│ │ │ dexterity │││ +│ │ │ Type: number │││ +│ │ │ ... │││ +│ │ └──────────────────────────────┘││ +│ │ [+ Add Property] ││ +│ └────────────────────────────────┘│ +│ │ +│ [YAML View] [Visual Builder] │ +└─────────────────────────────────────────────────────────┘ +``` + +--- + +## Success Criteria + +- ✅ Users can import D&D 5e schema YAML +- ✅ Character instance validates against schema +- ✅ Formula engine calculates derived stats correctly +- ✅ Schema-driven widgets render dynamically +- ✅ Users can edit character data through widgets +- ✅ AI prompts generate based on schema +- ✅ Export/import workflows work reliably +- ✅ Backward compatibility maintained (hardcoded mode still works) +- ✅ Migration wizard converts existing characters to schema + +--- + +## Open Questions + +1. **Schema Marketplace:** Should we host community schemas on GitHub? +2. **Version Compatibility:** How to handle schema version upgrades? +3. **Formula Complexity:** Limit formula depth to prevent infinite loops? +4. **Multi-Character:** Support multiple character instances with different schemas? +5. **Real-Time Sync:** Should formulas recalculate on every input change or debounced? + +--- + +## Next Steps + +1. Implement YAML parser and validator +2. Build formula engine with safe evaluation +3. Create IndexedDB storage layer +4. Develop schema-driven widget renderer +5. Design schema editor UI (YAML + visual builder) +6. Create D&D 5e reference schema +7. Build migration wizard +8. Write documentation and tutorials diff --git a/docs/features/widget-dashboard-system.md b/docs/features/widget-dashboard-system.md new file mode 100644 index 0000000..e3a641d --- /dev/null +++ b/docs/features/widget-dashboard-system.md @@ -0,0 +1,869 @@ +# Widget Dashboard System + +**Status:** Design Phase +**Priority:** Critical (Foundation for Schema System) +**Target Version:** 2.0.0 + +--- + +## Overview + +Transform RPG Companion from a static, hardcoded panel into a fully customizable widget-based dashboard where users can create tabs, drag-and-drop widgets, and arrange their perfect RPG tracking interface. + +### Core Philosophy +> "This is SillyTavern - users should be able to do whatever the fuck they want" + +No "modes", no training wheels, no limitations. Just pure customization. + +--- + +## Key Features + +### 1. Dynamic Tabs +- **User-created tabs**: Create unlimited tabs with custom names +- **Tab management**: Rename, delete, reorder, duplicate tabs +- **Default tabs**: Ships with "Status" and "Inventory" (user can modify/delete) +- **Tab icons**: Optional emoji/icon per tab +- **Tab context**: Each tab has independent widget layout + +### 2. Widget Grid System +- **12-column responsive grid** (like Bootstrap) +- **Variable row height** (default: 80px, user-configurable) +- **Drag-and-drop** with smooth animations +- **Auto-snap to grid** positions (toggleable) +- **Resize handles** on widget corners +- **Collision detection** and auto-reflow + +### 3. Widget Library + +#### Core Widgets (Always Available) +```javascript +{ + userStats: { + name: 'User Stats', + icon: '❤️', + description: 'Health, energy, satiety, hygiene, arousal bars', + minSize: { w: 2, h: 2 }, + defaultSize: { w: 4, h: 3 }, + requiresSchema: false + }, + + infoBox: { + name: 'Info Box', + icon: '📅', + description: 'Date, weather, temperature, time, location dashboard', + minSize: { w: 3, h: 2 }, + defaultSize: { w: 6, h: 2 }, + requiresSchema: false + }, + + presentCharacters: { + name: 'Present Characters', + icon: '👥', + description: 'Character cards with avatars and traits', + minSize: { w: 2, h: 2 }, + defaultSize: { w: 6, h: 3 }, + requiresSchema: false + }, + + inventory: { + name: 'Inventory', + icon: '🎒', + description: 'On Person, Stored, Assets with list/grid views', + minSize: { w: 3, h: 3 }, + defaultSize: { w: 6, h: 4 }, + requiresSchema: false + }, + + classicStats: { + name: 'Classic Stats', + icon: '🎲', + description: 'D&D-style STR/DEX/CON/INT/WIS/CHA with +/- buttons', + minSize: { w: 2, h: 2 }, + defaultSize: { w: 3, h: 3 }, + requiresSchema: false + }, + + diceRoller: { + name: 'Dice Roller', + icon: '🎲', + description: 'Interactive dice roller with formula input', + minSize: { w: 2, h: 1 }, + defaultSize: { w: 3, h: 2 }, + requiresSchema: false + }, + + lastRoll: { + name: 'Last Roll', + icon: '🎯', + description: 'Display of most recent dice roll result', + minSize: { w: 1, h: 1 }, + defaultSize: { w: 2, h: 1 }, + requiresSchema: false + } +} +``` + +#### Schema-Driven Widgets (Require Active Schema) +```javascript +{ + customStats: { + name: 'Custom Stats', + icon: '📊', + description: 'Schema-defined stats with formula support', + minSize: { w: 2, h: 2 }, + defaultSize: { w: 4, h: 3 }, + requiresSchema: true + }, + + skills: { + name: 'Skills', + icon: '⚔️', + description: 'Schema-defined skills with progression', + minSize: { w: 2, h: 3 }, + defaultSize: { w: 4, h: 4 }, + requiresSchema: true + }, + + relationships: { + name: 'Relationships', + icon: '💕', + description: 'Character relationship tracker with affection values', + minSize: { w: 3, h: 2 }, + defaultSize: { w: 6, h: 3 }, + requiresSchema: true + }, + + quests: { + name: 'Quest Log', + icon: '📜', + description: 'Active/completed quests with objectives', + minSize: { w: 3, h: 3 }, + defaultSize: { w: 6, h: 4 }, + requiresSchema: true + }, + + statusEffects: { + name: 'Status Effects', + icon: '✨', + description: 'Active buffs/debuffs with duration tracking', + minSize: { w: 2, h: 2 }, + defaultSize: { w: 4, h: 2 }, + requiresSchema: true + }, + + resources: { + name: 'Resources', + icon: '⚡', + description: 'Schema-defined resource pools (mana, stamina, etc.)', + minSize: { w: 2, h: 2 }, + defaultSize: { w: 3, h: 2 }, + requiresSchema: true + } +} +``` + +#### Meta Widgets +```javascript +{ + schemaEditor: { + name: 'Schema Editor', + icon: '⚙️', + description: 'Inline YAML/visual editor for system schema', + minSize: { w: 4, h: 4 }, + defaultSize: { w: 8, h: 6 }, + requiresSchema: false + }, + + debugConsole: { + name: 'Debug Console', + icon: '🐛', + description: 'Parser logs and debug output (mobile-friendly)', + minSize: { w: 3, h: 2 }, + defaultSize: { w: 6, h: 3 }, + requiresSchema: false + }, + + quickSettings: { + name: 'Quick Settings', + icon: '⚙️', + description: 'Most-used settings without opening modal', + minSize: { w: 2, h: 2 }, + defaultSize: { w: 3, h: 3 }, + requiresSchema: false + } +} +``` + +--- + +## User Interface Design + +### Edit Mode Toggle + +**View Mode** (Default): +``` +┌──────────────────────────────────────────────────────────┐ +│ RPG Companion [⚙️] [Edit] [×] │ +├──────────────────────────────────────────────────────────┤ +│ Combat │ Social │ Inventory │ Lore │ + │ +└──────────────────────────────────────────────────────────┘ +│ │ +│ [Widgets render here in locked positions] │ +│ │ +└──────────────────────────────────────────────────────────┘ +``` + +**Edit Mode** (Active): +``` +┌──────────────────────────────────────────────────────────┐ +│ RPG Companion [Save] [Cancel] [Reset] │ +├──────────────────────────────────────────────────────────┤ +│ Combat │ Social │ + │ [Rename] [Delete] │ +└──────────────────────────────────────────────────────────┘ +│ ┌─ Widget Library ────────────┐ │ +│ │ Core Widgets: │ ┌──────────────┐ │ +│ │ [+ User Stats] │ │ Widget │ [×] [↔] │ +│ │ [+ Info Box] │ │ (draggable) │ │ +│ │ [+ Present Characters] │ └──────────────┘ │ +│ │ [+ Inventory] │ │ +│ │ [+ Classic Stats] │ [Drop widgets here] │ +│ │ │ [12-column grid visible] │ +│ │ Schema Widgets: │ │ +│ │ [+ Skills] (need schema) │ │ +│ │ [+ Relationships] │ │ +│ │ [+ Quests] │ │ +│ └────────────────────────────┘ │ +└──────────────────────────────────────────────────────────┘ +``` + +### Widget Header (Edit Mode) + +Each widget shows controls when in edit mode: + +``` +┌─────────────────────────────────────┐ +│ User Stats [↔] [×] [⚙]│ ← Drag, Delete, Settings +├─────────────────────────────────────┤ +│ │ +│ [Widget content] │ +│ │ +└─────────────────────────────────────┘ + ↖ Resize handle +``` + +### Grid Visualization + +When in edit mode, show semi-transparent grid lines: + +``` +┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ ← 12 columns +│ │ │ │ │ │ │ │ │ │ │ │ │ +├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤ +│ │ ← Rows (80px each) +├───────────────────────┤ +│ │ +└───────────────────────┘ +``` + +--- + +## Mobile Behavior + +### Responsive Strategy + +**Mobile (≤1000px width):** +- Force single-column layout (widgets stack vertically) +- Maintain user's widget order from desktop +- Allow drag-to-reorder within column +- No resize handles (fixed width = 100%) +- Tabs become horizontal scrollable + +**Example Mobile View:** +``` +┌──────────────────────┐ +│ Combat ▼ │ ← Dropdown for tabs +└──────────────────────┘ +│ User Stats │ +│ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │ +├──────────────────────┤ +│ Skills │ +│ - Lockpicking: 75 │ +│ - Stealth: 60 │ +├──────────────────────┤ +│ Inventory │ +│ On Person: 3 items │ +└──────────────────────┘ + [drag handles for reorder] +``` + +--- + +## Data Structure + +### Dashboard Configuration + +Stored in `extensionSettings.dashboard`: + +```javascript +extensionSettings.dashboard = { + version: 2, // Dashboard config version + + gridConfig: { + columns: 12, // Grid columns + rowHeight: 80, // Pixels per row + gap: 12, // Gap between widgets (px) + snapToGrid: true, // Auto-snap enabled + showGrid: true // Show grid lines in edit mode + }, + + tabs: [ + { + id: 'tab-combat', // Unique ID (generated) + name: 'Combat', // User-editable name + icon: '⚔️', // Optional emoji/icon + order: 0, // Tab order + widgets: [ + { + id: 'widget-1', // Unique widget instance ID + type: 'userStats', // Widget type from registry + x: 0, // Grid column (0-11) + y: 0, // Grid row (0-infinity) + w: 4, // Width in columns + h: 3, // Height in rows + config: { // Widget-specific config + showClassicStats: true, + statBarStyle: 'gradient' + } + }, + { + id: 'widget-2', + type: 'skills', + x: 4, + y: 0, + w: 4, + h: 4, + config: { + category: 'Combat', + sortBy: 'value' + } + } + // ... more widgets + ] + }, + { + id: 'tab-social', + name: 'Social', + icon: '💬', + order: 1, + widgets: [ + // ... widgets for this tab + ] + } + ], + + defaultTab: 'tab-combat' // Which tab to show on load +}; +``` + +### Default Layout + +First-time users get this default layout: + +```javascript +const DEFAULT_DASHBOARD = { + tabs: [ + { + id: 'tab-status', + name: 'Status', + icon: '📊', + widgets: [ + { type: 'userStats', x: 0, y: 0, w: 6, h: 3 }, + { type: 'infoBox', x: 6, y: 0, w: 6, h: 2 }, + { type: 'presentCharacters', x: 0, y: 3, w: 12, h: 3 } + ] + }, + { + id: 'tab-inventory', + name: 'Inventory', + icon: '🎒', + widgets: [ + { type: 'inventory', x: 0, y: 0, w: 12, h: 6 } + ] + } + ] +}; +``` + +--- + +## Implementation Architecture + +### Module Structure + +``` +src/systems/dashboard/ +├── gridEngine.js # Core grid layout engine +├── widgetRegistry.js # Widget type definitions +├── dragDrop.js # Drag-and-drop logic +├── tabManager.js # Tab CRUD operations +├── layoutPersistence.js # Save/load layouts +└── editMode.js # Edit mode UI state +``` + +### Widget Registry System + +```javascript +// src/systems/dashboard/widgetRegistry.js + +export class WidgetRegistry { + constructor() { + this.widgets = new Map(); + } + + register(type, definition) { + this.widgets.set(type, { + ...definition, + render: definition.render.bind(definition) + }); + } + + get(type) { + return this.widgets.get(type); + } + + getAvailable(hasSchema = false) { + return Array.from(this.widgets.values()) + .filter(w => !w.requiresSchema || hasSchema); + } +} + +// Usage: +const registry = new WidgetRegistry(); + +registry.register('userStats', { + name: 'User Stats', + icon: '❤️', + minSize: { w: 2, h: 2 }, + defaultSize: { w: 4, h: 3 }, + requiresSchema: false, + + render(container, config) { + // Reuse existing renderUserStats() logic + renderUserStats(container, config); + }, + + getConfig() { + // Return editable config options for settings + return { + showClassicStats: { type: 'boolean', default: true }, + statBarStyle: { type: 'select', options: ['solid', 'gradient'] } + }; + } +}); +``` + +### Grid Engine + +```javascript +// src/systems/dashboard/gridEngine.js + +export class GridEngine { + constructor(config) { + this.columns = config.columns || 12; + this.rowHeight = config.rowHeight || 80; + this.gap = config.gap || 12; + this.snapToGrid = config.snapToGrid !== false; + } + + // Calculate widget pixel position from grid coordinates + getPixelPosition(widget) { + const colWidth = (this.containerWidth - (this.gap * (this.columns + 1))) / this.columns; + + return { + left: widget.x * (colWidth + this.gap) + this.gap, + top: widget.y * (this.rowHeight + this.gap) + this.gap, + width: widget.w * colWidth + (widget.w - 1) * this.gap, + height: widget.h * this.rowHeight + (widget.h - 1) * this.gap + }; + } + + // Snap pixel position to nearest grid cell + snapToCell(pixelX, pixelY) { + const colWidth = (this.containerWidth - (this.gap * (this.columns + 1))) / this.columns; + const x = Math.round((pixelX - this.gap) / (colWidth + this.gap)); + const y = Math.round((pixelY - this.gap) / (this.rowHeight + this.gap)); + + return { + x: Math.max(0, Math.min(x, this.columns - 1)), + y: Math.max(0, y) + }; + } + + // Check for collisions with other widgets + detectCollision(widget, widgets) { + return widgets.some(other => { + if (other.id === widget.id) return false; + + return !( + widget.x + widget.w <= other.x || + widget.x >= other.x + other.w || + widget.y + widget.h <= other.y || + widget.y >= other.y + other.h + ); + }); + } + + // Reflow widgets after position change + reflow(widgets) { + // Sort by y position, then x + const sorted = [...widgets].sort((a, b) => { + if (a.y !== b.y) return a.y - b.y; + return a.x - b.x; + }); + + // Push down any overlapping widgets + for (let i = 0; i < sorted.length; i++) { + const widget = sorted[i]; + + while (this.detectCollision(widget, sorted.slice(0, i))) { + widget.y++; + } + } + + return sorted; + } +} +``` + +### Drag-and-Drop Handler + +```javascript +// src/systems/dashboard/dragDrop.js + +export class DragDropHandler { + constructor(gridEngine, onDrop) { + this.gridEngine = gridEngine; + this.onDrop = onDrop; + this.draggedWidget = null; + this.dragOffset = { x: 0, y: 0 }; + } + + initWidget(widgetElement, widgetData) { + const handle = widgetElement.querySelector('.widget-drag-handle'); + + handle.addEventListener('mousedown', (e) => { + this.startDrag(e, widgetElement, widgetData); + }); + } + + startDrag(e, element, widget) { + e.preventDefault(); + + this.draggedWidget = widget; + const rect = element.getBoundingClientRect(); + this.dragOffset = { + x: e.clientX - rect.left, + y: e.clientY - rect.top + }; + + element.classList.add('dragging'); + + document.addEventListener('mousemove', this.onMouseMove); + document.addEventListener('mouseup', this.onMouseUp); + } + + onMouseMove = (e) => { + if (!this.draggedWidget) return; + + const pixelX = e.clientX - this.dragOffset.x; + const pixelY = e.clientY - this.dragOffset.y; + + if (this.gridEngine.snapToGrid) { + const gridPos = this.gridEngine.snapToCell(pixelX, pixelY); + this.draggedWidget.x = gridPos.x; + this.draggedWidget.y = gridPos.y; + } else { + // Free-form positioning (convert to grid on drop) + this.draggedWidget.pixelX = pixelX; + this.draggedWidget.pixelY = pixelY; + } + + this.onDrop(this.draggedWidget); + } + + onMouseUp = (e) => { + if (!this.draggedWidget) return; + + document.querySelector('.dragging')?.classList.remove('dragging'); + + // Final snap to grid + if (this.draggedWidget.pixelX !== undefined) { + const gridPos = this.gridEngine.snapToCell( + this.draggedWidget.pixelX, + this.draggedWidget.pixelY + ); + this.draggedWidget.x = gridPos.x; + this.draggedWidget.y = gridPos.y; + delete this.draggedWidget.pixelX; + delete this.draggedWidget.pixelY; + } + + this.onDrop(this.draggedWidget, true); // true = drop complete + this.draggedWidget = null; + + document.removeEventListener('mousemove', this.onMouseMove); + document.removeEventListener('mouseup', this.onMouseUp); + } +} +``` + +--- + +## Widget Development Guide + +### Creating a New Widget + +```javascript +// 1. Define widget in registry +registry.register('myCustomWidget', { + name: 'My Custom Widget', + icon: '🎨', + description: 'Does something cool', + minSize: { w: 2, h: 2 }, + defaultSize: { w: 4, h: 3 }, + requiresSchema: false, + + // Render function receives container and config + render(container, config) { + const html = ` +
+

${config.title || 'My Widget'}

+
+ +
+
+ `; + + container.innerHTML = html; + + // Set up event listeners + container.querySelector('.my-widget').addEventListener('click', () => { + console.log('Widget clicked!'); + }); + }, + + // Define configurable options + getConfig() { + return { + title: { + type: 'text', + label: 'Widget Title', + default: 'My Widget' + }, + color: { + type: 'color', + label: 'Accent Color', + default: '#e94560' + }, + showBorder: { + type: 'boolean', + label: 'Show Border', + default: true + } + }; + }, + + // Called when widget config changes + onConfigChange(newConfig, container) { + this.render(container, newConfig); + } +}); + +// 2. Widget automatically available in dashboard +``` + +### Widget Lifecycle + +```javascript +// Widget instance lifecycle: +1. User adds widget to tab + → registry.get(type) returns definition + → Generate unique widget ID + → Assign default size and position + +2. Dashboard renders widget + → Create container element + → Call widget.render(container, config) + → Apply positioning/sizing CSS + +3. User enters edit mode + → Show drag handle and resize controls + → Enable drag/drop handlers + +4. User changes widget config + → Call widget.onConfigChange(newConfig) + → Widget re-renders with new config + +5. User removes widget + → Clean up event listeners + → Remove from layout array + → Reflow remaining widgets +``` + +--- + +## Settings Integration + +### Widget Management Section + +Add to existing Settings modal: + +```html +
+
+ Dashboard Layout +
+
+ + +

Available Widgets

+
+ + + + + + + + + +
+ + +

Grid Settings

+ + + + + + + +

Layout Actions

+ + + + + +
+
+``` + +--- + +## Technical Considerations + +### Performance + +- **Virtualization**: Only render visible widgets (especially on mobile) +- **Throttle drag updates**: Use RAF (requestAnimationFrame) for smooth dragging +- **Debounce saves**: Don't save layout on every drag - wait 500ms after drop +- **Lazy load widgets**: Only load widget code when first used + +### Browser Compatibility + +- **CSS Grid**: Fallback to flexbox for older browsers +- **Drag API**: Use mouse events instead of HTML5 Drag API (better cross-browser) +- **Touch events**: Support both mouse and touch for mobile +- **LocalStorage**: Store layout in extensionSettings, backed up to localStorage + +### Accessibility + +- **Keyboard navigation**: Tab through widgets, Enter to edit +- **Screen readers**: Proper ARIA labels on all controls +- **Focus indicators**: Clear visual focus states +- **Skip links**: "Skip to widget X" for keyboard users + +--- + +## Migration Strategy + +### Phase 1: Infrastructure +- Implement grid engine and widget registry +- Add dashboard config to extensionSettings +- Create default layout from current structure + +### Phase 2: Edit Mode +- Build edit mode toggle and UI +- Implement drag-and-drop for existing widgets +- Add tab management (create/rename/delete) + +### Phase 3: Widget Conversion +- Convert existing sections to widgets: + - userStats widget (reuse renderUserStats) + - infoBox widget (reuse renderInfoBox) + - presentCharacters widget (reuse renderThoughts) + - inventory widget (reuse renderInventory) + - classicStats widget (extract from userStats) + +### Phase 4: New Widgets +- Implement schema-driven widgets: + - skills widget + - relationships widget + - quests widget + - statusEffects widget + +### Phase 5: Polish +- Mobile responsive refinements +- Animation polish +- Settings integration +- Documentation and tutorials + +--- + +## Future Enhancements + +- **Widget marketplace**: Share custom widgets with community +- **Layout templates**: Pre-made layouts for different RPG systems +- **Widget linking**: Connect widgets (e.g., skills affect stats) +- **Conditional visibility**: Show/hide widgets based on conditions +- **Widget themes**: Per-widget color/style overrides +- **Nested tabs**: Tabs within widgets for complex UIs + +--- + +## Open Questions + +1. **Grid Library**: Use existing library (Gridstack.js) or build custom? + - **Pro Gridstack**: Battle-tested, feature-rich, responsive + - **Pro Custom**: No dependencies, lighter weight, full control + +2. **Schema Editor Widget**: Should it be a widget or always-modal? + - **Widget**: More flexible positioning, can be in dedicated tab + - **Modal**: Cleaner separation, larger working area + +3. **Mobile Tab Limit**: Should we limit tabs on mobile? + - **Unlimited**: Let users manage, use dropdown/scroll + - **Limited**: Force max 5 tabs, rest in "More" menu + +4. **Widget State**: Where to store widget-specific state (not config)? + - **Per-widget**: Each widget manages its own state + - **Global**: Dashboard state manager for all widgets + - **Hybrid**: Widgets can opt into global state management + +5. **Undo/Redo**: Should layout changes support undo/redo? + - **Yes**: Better UX, prevents accidental deletions + - **No**: Adds complexity, users can import previous layout + +--- + +## Success Metrics + +- ✅ Users can create/delete/rename tabs without code +- ✅ Users can drag-and-drop widgets to any position +- ✅ Layout persists across sessions +- ✅ Mobile users get functional (even if stacked) layout +- ✅ Existing functionality works as widgets (no regressions) +- ✅ Schema-driven widgets only appear when schema active +- ✅ Export/import layouts works reliably +- ✅ Edit mode is intuitive (no tutorial needed)