Page Properties — Inline Content & FTS
Page Properties — Inline Content & FTS
Covers the full property lifecycle on a page: inserting property references inline via the editor autocomplete triggers,
reading and writing property values, verifying FTS5 indexes property values so they are findable by search, and
confirming the context-panel PropertySection reflects the current frontmatter state. This spec is P2 because typed
properties and inline {{name:value}} syntax are a power-user differentiator — breakage silently degrades a core
workflow without an obvious error.
Properties are stored as frontmatter on a page and exposed through two paths: the inline PropertyRef TipTap node
rendered in the editor body, and the PropertySection component in the context panel. The two views must stay
consistent. Values written via set_property_value must be immediately visible in get_page_properties. Values
embedded as {{name:value}} in block markdown must survive a round-trip through the VFS markdown projection and
re-import.
Preconditions
- HTTP bridge running on port 9990
- A workspace initialized via
initialize_workspacebefore each scenario - Bridge shim injected via
playwright.config.ts
Scenarios
Seed: seed.spec.ts
1. Set a text property value via API and retrieve it
The backend set_property_value command stores a frontmatter value and get_page_properties returns it immediately.
Steps:
- Create a page titled “Property Owner”.
- Call
set_property_valuewithpage_id,property_slug: "summary", andvalue: "A brief description". - Call
get_page_propertieswith the samepage_id.
Expected: The response from get_page_properties contains an entry with slug: "summary" and
value: "A brief description". The entry is classified as is_from_type: false (freeform) because no type definition
was assigned.
2. Set a property and verify it appears in the PropertySection panel
After setting a property value, the context panel (PropertySection) for that page shows the key and value.
Steps:
- Create a page titled “Context Panel Test”.
- Navigate to the page so the editor and context panel are visible.
- Call
set_property_valuewithproperty_slug: "status"andvalue: "draft". - Open or reload the context panel (click the info/details icon or switch to the properties tab).
Expected: The PropertySection panel lists “status” with value “draft”. The value is rendered inline alongside the property label. No reload is required for the panel to update if it subscribes to the current page state.
3. Insert a property reference via {{ autocomplete trigger
Typing {{ in the editor opens a property autocomplete menu for inserting an inline PropertyRef node.
Steps:
- Create a page titled “Inline Props”.
- Click into the editor and place the cursor after the initial empty block.
- Type
{{. - Observe whether an autocomplete menu or suggestion popover appears.
- If a menu appears, select or type a property name such as “summary” and confirm the insertion.
Expected: Typing {{ opens a property suggestion menu. Selecting “summary” inserts an inline {{summary}} or
{{summary:value}} node. The node renders as a visible pill or badge inside the editor content.
4. Insert a property reference via /property slash command
The /property slash command is a second trigger for the same inline insertion flow.
Steps:
- On a page with at least one block, type
/propertyin the editor. - Observe whether the slash-command menu shows a “property” option.
- Select the “property” option from the menu.
- When prompted for a property name, enter “author”.
- Confirm the insertion.
Expected: A {{author}} or {{author:value}} inline node appears at the cursor position. The editor does not show
a duplicate autocomplete popup from the {{ trigger.
5. Property reference renders as inline badge in editor
A saved PropertyRef node displays as a visual pill inside the block content, not as raw {{name}} text.
Steps:
- Create a page and set a property value for
property_slug: "priority"andvalue: "high". - Navigate to the page in the editor.
- Inspect the rendered block content area.
Expected: The property reference appears as a styled inline chip or badge with the property name visible (e.g.,
priority or priority: high). The raw {{priority:high}} syntax is not shown as plain text.
6. Property value displays in {{name:value}} format in saved markdown
When a block containing a property reference is saved, the serialized markdown uses the {{name:value}} colon-separated
format.
Steps:
- Create a page titled “Value Format”.
- Insert the text
{{status:published}}into a block directly viasave_block_content_markdownwith content"Status: {{status:published}}". - Call
get_pageand inspect the block content.
Expected: The stored block content includes {{status:published}} with the colon separator. The status key
appears in the page frontmatter with value "published" after the VFS write-back parses the inline syntax.
7. Clear a property value by setting it to null
Calling set_property_value with value: null removes the frontmatter key entirely.
Steps:
- Create a page and set
property_slug: "summary"withvalue: "initial". - Verify
get_page_propertiesreturnssummary: "initial". - Call
set_property_valueagain withvalue: null(JSON null) for the same slug. - Call
get_page_propertiesagain.
Expected: The second call to get_page_properties either does not include a "summary" entry or returns it with
value: null. The frontmatter key has been removed from the page’s persisted state.
8. FTS5 indexes property values — page is findable by property content
Property values stored in frontmatter are indexed by FTS5, so searching for the value text returns the page.
Steps:
- Create a page titled “Indexed Properties”.
- Call
set_property_valuewithproperty_slug: "protagonist"andvalue: "Elara Vance". - Call
search_pageswith query"Elara Vance".
Expected: The search results include “Indexed Properties”. The page is discoverable by its property value without the property name being mentioned in the page title or block content.
9. Multiple properties on a single page are all returned
A page can hold many property keys and all are returned from get_page_properties.
Steps:
- Create a page titled “Multi-Prop”.
- Call
set_property_valuethree times with different slugs:"genre"→"fantasy","wordCount"→ set via the API as a number or string,"rating"→"5". - Call
get_page_propertiesfor the page.
Expected: The response contains at least three entries — one for each slug. Each entry has the correct slug and
value fields populated. The order may vary.
10. Property value validates against typed property definition
When a property slug matches a registered PropertyDefinition with a specific value_type, setting an incompatible
value is rejected.
Steps:
- Verify a typed property definition exists for slug
"word-count"withvalue_type: "number"(or create one if the API supports it). - Call
set_property_valuewithproperty_slug: "word-count"andvalue: "not-a-number"(a string).
Expected: The call returns a validation error (HTTP 422 or equivalent). The error message indicates the value does not match the expected type. The page’s frontmatter is not updated.
11. Freeform property slug bypasses type validation
A slug with no matching PropertyDefinition is stored as freeform frontmatter with no type check.
Steps:
- Create a page titled “Freeform”.
- Call
set_property_valuewithproperty_slug: "completely-custom"andvalue: {"nested": true}(an object).
Expected: The call succeeds. get_page_properties returns the slug with the stored JSON object as its value and
is_from_type: false.
12. Property persists after navigating away and returning
Frontmatter properties survive a navigation round-trip and are not lost on page reload.
Steps:
- Create a page titled “Persistent Props” and set
property_slug: "chapter"withvalue: "7". - Navigate to a different page (e.g., create and open another page).
- Navigate back to “Persistent Props”.
- Call
get_page_propertiesfor “Persistent Props”.
Expected: The property chapter: "7" is still present. No data was lost during the navigation.
Test Data
| Key | Value | Notes |
|---|---|---|
| text_prop_slug | summary | Freeform text property slug used across basic scenarios |
| typed_number_slug | word-count | Slug for typed Number property (validates value type) |
| freeform_slug | completely-custom | Unknown slug — stored without validation |
| fts_prop_slug | protagonist | Property whose value is searched via FTS5 |
| fts_prop_value | Elara Vance | Unique search term expected to return exactly one page |
| clear_sentinel | null | JSON null passed to remove a frontmatter key |
| multi_prop_count | 3 | Minimum number of properties set in scenario 9 |
| inline_md_format | {{status:published}} | Exact colon-separated format expected in serialized markdown |
Notes
get_page_propertiesreturns both typed properties (from the page’sTypeAssignment) and freeform frontmatter keys not matched to a definition. Theis_from_typeboolean distinguishes them.- The HTTP bridge’s
get_page_propertieshandler is currently a stub returning[](as of the current codebase). These scenarios exercise the Tauri command path. When the bridge is updated to wire the full use case, the bridge-based tests can be enabled. Until then, scenarios involving property retrieval must be driven through the Tauri app (not the bridge stub). set_property_valueis fully wired in the bridge (/invoke/set_property_value).- The
"tags"property slug is special-cased at the Tauri framework layer — it routes to the tag repository rather than generic frontmatter storage. Do not use"tags"as a test slug for generic frontmatter validation. - VFS write-back (scenario 6) requires the VFS to be mounted. The bridge returns 501 for VFS commands. Scenario 6 covers the markdown format stored in the block, not the full VFS projection round-trip.
- FTS5 indexing of frontmatter happens via
pages.raw_markdowncolumn update on save. There may be a brief delay if background indexing is involved. Retry the search query if the first attempt returns no results within the same test. - The dual-trigger pattern (
{{and/property) is mutually exclusive — only one autocomplete popup should be visible at a time. If both fire simultaneously, this is a regression.
Was this page helpful?
Thanks for your feedback!