Conversation
WalkthroughThis PR implements two cohesive improvements: (1) nullable schema type handling in JSON Schema generation, adding helpers to extract and properly render nullable arrays, enums, and objects from anyOf unions; (2) frontmatter preservation during save by tracking frontmatter edits separately, conditionally passing raw or parsed frontmatter to maintain original formatting when only content changes. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Store as Editor Store
participant Hook as useEditorActions
participant Backend as save_markdown_content
participant FS as File System
User->>Store: Edit frontmatter
Store->>Store: Set isFrontmatterDirty = true
User->>Store: Edit content only
Store->>Store: isFrontmatterDirty remains true/false
User->>Hook: Trigger save
Hook->>Store: Read isFrontmatterDirty, frontmatter, rawFrontmatter
alt Frontmatter was edited
Hook->>Backend: Pass frontmatter (parsed), rawFrontmatter = null
Backend->>Backend: Reorder/normalise frontmatter
else Frontmatter unchanged
Hook->>Backend: Pass frontmatter = null, rawFrontmatter (raw)
Backend->>Backend: Preserve raw frontmatter exactly
end
Backend->>FS: Write markdown with conditional frontmatter handling
FS-->>Backend: Success
Backend-->>Hook: Confirm
Hook->>Store: Set isFrontmatterDirty = false, isDirty = false
Store-->>User: Save complete, state cleared
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25–30 minutes Key areas requiring attention:
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src-tauri/src/schema_merger.rs (1)
511-516: Consider logging whenhandle_array_typefails.The
.ok()on line 514 silently discards any error fromhandle_array_type. While this allows the function to fall through to other handlers, it may mask issues during debugging. Alog::debug!call when an error occurs could aid troubleshooting.if has_null { if let Some(schema) = array_schema { // Delegate to existing handle_array_type logic - return handle_array_type(schema).ok(); + match handle_array_type(schema) { + Ok(info) => return Some(info), + Err(e) => { + log::debug!("[Schema] Nullable array extraction failed: {e}"); + } + } } } Nonesrc-tauri/src/commands/files.rs (1)
383-385: Consider removing redundant public wrapper.The
parse_frontmatter_internalfunction simply delegates to the privateparse_frontmatterfunction without adding any value:pub fn parse_frontmatter_internal(content: &str) -> Result<MarkdownContent, String> { parse_frontmatter(content) }If external modules need to call
parse_frontmatter, consider making the original function public directly rather than adding a wrapper. If this wrapper serves a specific purpose (e.g., testing or future extensibility), please document it.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
docs/tasks-done/task-2025-12-04-fix-nullable-schema-types.md(1 hunks)src-tauri/src/commands/files.rs(4 hunks)src-tauri/src/schema_merger.rs(4 hunks)src/hooks/editor/useEditorActions.ts(3 hunks)src/hooks/editor/useEditorHandlers.test.ts(2 hunks)src/hooks/useEditorFileContent.ts(1 hunks)src/store/editorStore.ts(6 hunks)test/dummy-astro-project/.astro/collections/notes.schema.json(1 hunks)test/dummy-astro-project/.astro/settings.json(1 hunks)test/dummy-astro-project/src/content.config.ts(1 hunks)test/dummy-astro-project/src/content/notes/frontmatter-preservation-test.md(1 hunks)test/dummy-astro-project/src/content/notes/nullable-schema-test.md(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src-tauri/src/commands/files.rs (1)
src-tauri/src/commands/mdx_components.rs (1)
validate_project_path(17-32)
🪛 LanguageTool
test/dummy-astro-project/src/content/notes/nullable-schema-test.md
[grammar] ~23-~23: The plural noun “keywords” cannot be used with the article “a”.
Context: ...lished']).nullish()- Should render as a dropdown 2. **keywords** -z.array(z.string()).nullish()` - S...
(A_NNS)
[duplication] ~30-~30: Possible typo: you repeated a word.
Context: ...eld should show a tag input, not a text field - scores field should show a number array input
(ENGLISH_WORD_REPEAT_RULE)
docs/tasks-done/task-2025-12-04-fix-nullable-schema-types.md
[uncategorized] ~652-~652: A comma may be missing after the conjunctive/linking adverb ‘Currently’.
Context: ...s) saves editor state when save fails. Currently it saves frontmatterbut notisFront...
(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)
[uncategorized] ~656-~656: Use a comma before “and” if it connects two independent clauses (unless they are closely connected and short).
Context: ...s is acceptable because recovery is rare and data integrity matters more than format...
(COMMA_COMPOUND_SENTENCE_2)
🪛 markdownlint-cli2 (0.18.1)
docs/tasks-done/task-2025-12-04-fix-nullable-schema-types.md
299-299: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (rust)
🔇 Additional comments (20)
test/dummy-astro-project/.astro/settings.json (1)
3-3: Verify the necessity of committing this test fixture change.This is an auto-generated settings file in the test project directory. The timestamp modification appears to be a touch update rather than a functional change. Test fixtures and configuration files that are auto-generated by the build or test tooling should typically not be committed to version control, or the PR description should clearly explain why this file has been modified.
Confirm whether this change is intentional and necessary for the PR to function correctly, or if it should be excluded from the commit.
src-tauri/src/schema_merger.rs (4)
418-444: LGTM! Well-structured nullable type handling.The prioritisation of nullable primitive, array, and enum extraction before falling back to string is correct. This directly addresses issue #68 where
z.number().nullish()was incorrectly typed as "string".
446-486: Solid implementation with appropriate guards.The logic correctly identifies nullable primitives while properly deferring strings with enum values to the dedicated enum handler. The explicit 2-element check and type matching are robust.
520-556: LGTM! Correctly extracts nullable enums.The logic properly identifies nullable enum patterns by checking for both a null type and a string type with enum values. The catch-all
_ => {}is appropriate here since the function needs to examine all schemas in the union.
1264-1304: Excellent regression test for issue #68.The test explicitly documents the bug and verifies both
latandlngfields are correctly typed as "number" regardless of the order of elements in the anyOf array. This directly validates the fix for the reported issue.src/hooks/editor/useEditorHandlers.test.ts (1)
28-28: LGTM! Mock state correctly extended.The addition of
isFrontmatterDirtyto the mock store state and its initialisation tofalsealigns with the new editor store field. This ensures tests remain compatible with the updated store interface.Also applies to: 74-74
src/hooks/useEditorFileContent.ts (1)
39-45: LGTM! Correctly resets frontmatter dirty state on load.Resetting
isFrontmatterDirty: falsewhen syncing from disk ensures the editor correctly treats loaded content as clean. This is essential for the save flow to preserve original frontmatter formatting when only content has changed.test/dummy-astro-project/src/content/notes/nullable-schema-test.md (1)
1-31: LGTM! Useful test fixture for nullable schema types.The test file provides concrete test data for verifying nullable enum and array handling. The frontmatter values correctly exercise the new nullable schema paths (
status,keywords,scores).Note: The static analysis warnings are false positives — the grammar check misunderstands the technical documentation context, and "tag input" vs "text field" are distinct UI element types, not duplications.
src/hooks/editor/useEditorActions.ts (3)
32-39: LGTM! Clean destructuring of frontmatter state.The addition of
rawFrontmatterandisFrontmatterDirtyto the destructured state is well-organised and follows existing patterns.
73-82: Core fix for issue #68 — preserves raw frontmatter when unedited.The conditional logic correctly determines which frontmatter representation to send:
- When
isFrontmatterDirtyistrue: sends the parsed frontmatter object (user made sidebar edits)- When
false: sendsrawFrontmatterto preserve original YAML formatting and type informationThis ensures that numeric values like
lat/lngremain as numbers when only the content body is edited.
91-95: LGTM! Correctly resets dirty flags after save.Resetting both
isDirtyandisFrontmatterDirtytofalseafter a successful save ensures the editor state accurately reflects that all changes have been persisted.test/dummy-astro-project/src/content/notes/frontmatter-preservation-test.md (1)
1-36: LGTM! Clear test documentation.This test file provides clear instructions and expected behaviours for frontmatter preservation testing. The documentation of both preserved and re-serialised scenarios is helpful for validation.
src/store/editorStore.ts (1)
19-20: LGTM! Correct dirty state tracking implementation.The
isFrontmatterDirtyflag is properly integrated:
- Initialised to
falseand reset on file open/close operations- Set to
truewhen frontmatter is updated viaupdateFrontmatterorupdateFrontmatterField- Intentionally NOT modified in
setEditorContent, which correctly preserves the distinction between content-only and frontmatter editsThis implementation aligns with the PR's objective to track frontmatter modifications separately from content changes.
Also applies to: 47-47, 70-70, 101-101, 125-125, 147-147
test/dummy-astro-project/.astro/collections/notes.schema.json (1)
50-90: LGTM! Nullable schema types correctly defined.The three new properties follow the correct
anyOf: [type, null]pattern for nullable types:
status: nullable enum with valid valueskeywords: nullable array of stringsscores: nullable array of numbersThese additions align with the PR's objective to support nullable schema types and match the Zod definitions in
content.config.ts.test/dummy-astro-project/src/content.config.ts (1)
48-51: LGTM! Zod schemas correctly defined for testing.The nullable Zod schemas match the JSON Schema definitions and provide comprehensive test coverage for:
- Nullable enums (
status)- Nullable string arrays (
keywords)- Nullable number arrays (
scores)These test schemas align with the PR's objective to support nullable schema types properly.
src-tauri/src/commands/files.rs (4)
364-378: Consider defensive check for impossible state.The match logic correctly handles the three scenarios:
- Frontmatter edited → use parsed and normalize
- Frontmatter unchanged → preserve raw formatting
- No frontmatter → content only
However, consider adding a defensive check for the impossible case where both
frontmatterandraw_frontmatterareSome:let new_content = match (frontmatter, raw_frontmatter) { // Frontmatter was edited - reorder and normalize (Some(fm), _) => rebuild_markdown_with_frontmatter_and_imports_ordered( &fm, &imports, &content, schema_field_order, )?, + // Defensive: If both are present, prefer parsed (shouldn't happen from frontend) + (None, Some(_)) => { + return Err("Invalid state: received both frontmatter and raw_frontmatter".to_string()); + }, // Frontmatter unchanged - preserve original (non-empty) (None, Some(ref raw)) if !raw.trim().is_empty() => { rebuild_markdown_with_raw_frontmatter(raw, &imports, &content)? } // No frontmatter at all (None, None, or empty string) _ => rebuild_markdown_content_only(&imports, &content)?, };Actually, on second thought, the current pattern
(Some(fm), _)already handles this by giving precedence to parsed frontmatter. The code is correct as-is.
716-755: LGTM! Raw frontmatter preservation correctly implemented.The
rebuild_markdown_with_raw_frontmatterfunction correctly:
- Preserves the raw frontmatter string exactly as-is (no normalisation)
- Handles imports and content with proper spacing
- Ensures consistent newline handling
The blank line before imports (line 733) is always added because this function is only called when raw frontmatter is non-empty (guarded in
save_markdown_contentline 373), ensuring consistency with the normalised path.
758-786: LGTM! Content-only rebuild correctly implemented.The
rebuild_markdown_content_onlyfunction properly handles the case where there's no frontmatter:
- Includes imports if present
- Adds appropriate spacing between imports and content
- Ensures trailing newline
The spacing logic is consistent with the other rebuild functions.
1589-1649: Excellent test coverage for new rebuild functions.The test suite comprehensively covers:
- Raw frontmatter preservation including datetime format (lines 1589-1605)
- Raw frontmatter with imports (lines 1607-1620)
- Content-only rebuild without imports (lines 1622-1635)
- Content-only rebuild with imports (lines 1637-1649)
These tests validate the key behaviours of the frontmatter preservation feature.
docs/tasks-done/task-2025-12-04-fix-nullable-schema-types.md (1)
1-688: Comprehensive documentation of implemented features.This documentation thoroughly explains:
- The nullable schema types problem and solution approach
- The frontmatter preservation feature with architecture diagrams
- Implementation details with code examples
- Test plans and success criteria
- Edge cases and gotchas
The documented implementation aligns with the code changes reviewed in
editorStore.tsandfiles.rs. This serves as excellent reference documentation for future maintainers.
Closes #68
Summary by CodeRabbit
Release Notes
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.