diff --git a/docs/ABSTRACTIONS.md b/docs/ABSTRACTIONS.md index 058d6da5..87633c69 100644 --- a/docs/ABSTRACTIONS.md +++ b/docs/ABSTRACTIONS.md @@ -674,6 +674,7 @@ The Inspector panel (`src/components/Inspector.tsx`) is composed of sub-panels: 1. **DynamicPropertiesPanel** (`src/components/DynamicPropertiesPanel.tsx`): Renders frontmatter as editable key-value pairs: - **Editable properties** (top): Type badge, Status pill with dropdown, number fields, boolean toggles, array tag pills, text fields. Click-to-edit interaction. - **Property display modes**: `text`, `number`, `date`, `boolean`, `status`, `url`, `tags`, and `color`. Numeric frontmatter values auto-detect as `number`, and custom scalar keys can be explicitly switched to `Number` through the property-type control. + - **Present empty properties**: A top-level frontmatter key with a blank scalar value (for example `start date:`) is treated as present and renders as an editable empty row. Only absent keys are omitted. - **Info section** (bottom, separated by border): Read-only derived metadata — Modified, Created, Words, File Size. Uses muted styling with no interaction. - Keys in `SKIP_KEYS` (`type`, `aliases`, `notion_id`, `workspace`, `is_a`, `Is A`) are hidden from the editable section. diff --git a/src/components/DynamicPropertiesPanel.test.tsx b/src/components/DynamicPropertiesPanel.test.tsx index f5437c7b..df403c39 100644 --- a/src/components/DynamicPropertiesPanel.test.tsx +++ b/src/components/DynamicPropertiesPanel.test.tsx @@ -176,6 +176,18 @@ describe('DynamicPropertiesPanel', () => { expect(screen.getByText('\u2014').parentElement).toHaveClass('justify-start', 'text-left') }) + it('keeps present empty properties visible and editable', () => { + renderPanel({ frontmatter: { 'start date': '' }, onUpdateProperty }) + + expect(screen.getByText('Start date')).toBeInTheDocument() + fireEvent.click(screen.getByText('\u2014')) + const input = screen.getByDisplayValue('') + fireEvent.change(input, { target: { value: '2026-05-03' } }) + fireEvent.blur(input) + + expect(onUpdateProperty).toHaveBeenCalledWith('start date', '2026-05-03') + }) + it('hides Owner with wikilink value from Properties panel', () => { renderPanel({ frontmatter: { Owner: '[[person/luca]]' } }) // Owner with wikilink goes to RelationshipsPanel, not Properties diff --git a/src/utils/frontmatter.test.ts b/src/utils/frontmatter.test.ts index a595af53..1ec157e3 100644 --- a/src/utils/frontmatter.test.ts +++ b/src/utils/frontmatter.test.ts @@ -89,6 +89,13 @@ describe('parseFrontmatter', () => { expect(fm['status']).toBeUndefined() expect(fm['Status']).toBe('Evergreened') }) + + it('keeps top-level keys with blank scalar values', () => { + const fm = parseFrontmatter('---\ntype: Book\nstart date:\nrating: \n---\n# New Book') + + expect(fm['start date']).toBe('') + expect(fm['rating']).toBe('') + }) }) describe('detectFrontmatterState', () => { diff --git a/src/utils/frontmatter.ts b/src/utils/frontmatter.ts index ed54ea66..4845f129 100644 --- a/src/utils/frontmatter.ts +++ b/src/utils/frontmatter.ts @@ -31,7 +31,7 @@ function collapseList(items: FrontmatterText[]): FrontmatterValue { } function isBlockScalar(value: FrontmatterText): boolean { - return value === '' || value === '|' || value === '>' + return value === '|' || value === '>' } function isInlineArrayLiteral(value: FrontmatterText): boolean {