Skip to content

note-type-registry - #68

Merged
adam4813 merged 5 commits into
mainfrom
note-type-registry
Aug 11, 2026
Merged

note-type-registry#68
adam4813 merged 5 commits into
mainfrom
note-type-registry

Conversation

@adam4813

Copy link
Copy Markdown
Owner

No description provided.

adam4813 and others added 5 commits August 10, 2026 22:43
Extend NoteTypeProvider in packages/core with optional UI capability
fields (supportedModes, sourceProtected, supportsScrollSync, viewComponent,
toolbarItems, contextMenuBuilder) so built-in note types and plugins share
a single registration pattern.

Key changes:
- Add NoteViewMode, NoteTypeToolbarItem, and UI fields to NoteTypeProvider
  in packages/core/src/contracts.ts (fields typed as unknown to keep core
  React-free)
- New NoteViewRegistry in packages/core — runtime map of note-type IDs to
  full NoteTypeProvider; disposer-based register/unregister
- Each built-in note-type package exports registerBuiltinNoteView(registry)
  mirroring the plugin registration pattern
- packages/editor/src/markdown-note-view.ts provides the markdown built-in
  registration (avoids circular dep: core must not import React components)
- packages/editor/src/note-view-descriptor.ts narrows opaque unknown fields
  to typed React ComponentType/ReactNode/NoteViewContextMenuBuilder
- PluginContext and PluginHost gain registerNoteView(); PluginManager wires
  it through buildContext() alongside registerFileHandler()
- use-plugins.ts creates a stable NoteViewRegistry, registers all built-ins
  at hook init, and exposes it via PluginsApi/AppServices
- markdown-editor.tsx replaces hardcoded NOTE_RENDERERS map with registry
  lookup; applies sourceProtected, supportedModes, supportsScrollSync from
  the active descriptor
- ModeToggle accepts supportedModes and supportsScrollSync props to filter
  visible mode buttons and conditionally show the scroll-sync toggle
- Context-menu builder from descriptor seeds noteViewCtxBuilder on type change

All 156 tests pass; typecheck clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tor, extract sync hooks

## Mode consolidation
EditorMode in packages/editor/src/types.ts is now a re-export alias of
NoteViewMode from packages/core — one canonical definition, zero drift.
Inline ['edit','split','rendered'] as EditorMode[] casts replaced with
EDITOR_MODES throughout.

## Built-in registration moved to app.tsx
NoteViewRegistry creation and all registerBuiltinNoteView() calls moved out
of usePlugins into the App component — an explicit app-level concern.
usePlugins now accepts the registry as a parameter and focuses solely on
the plugin lifecycle. The missing-dep lint warning is resolved.

## Sync hooks extracted (packages/editor/src/use-pane-sync.ts)
Three dedicated hooks replace the inline state/ref boilerplate:
  useCursorSync(initialPosition) — { request: CursorRequest, send }
  useScrollSync(initialRatio)    — { request: ScrollRequest,  send }
  useFocusSync()                 — { request: FocusRequest,   send }
A generic usePaneSync<T> base is also exported for advanced use.
Scroll-sync lock refs (feedback-loop prevention) live as plain useRef at
the call site — not inside the hook — to satisfy react-hooks/immutability.

## MarkdownEditor merged into NoteEditor
MarkdownEditor no longer orchestrates file I/O, registry routing, or
callbacks — NoteEditor owns all of that directly. MarkdownEditor is now a
lightweight, self-contained inline editor component (no web-app imports,
accepts callbacks as a prop) for use in board cards, calendar events, etc.
The six cross-package imports from @notes/web/src/... are gone from it.

## Lint fixes
- note-editor: setState-in-effect replaced with inline derivation for
  context-menu builder (descriptor builder computed directly, component
  builder overrides via state as before)
- markdown-editor / note-editor: NoteRenderer resolved via DynamicNoteRenderer
  wrapper (declared at module level) to satisfy react-hooks/static-components
- plugin-manager.test.ts: unused parameter removed

All 156 tests pass; typecheck clean; lint clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eEditorCallbacks

## MarkdownEditor removed
Board cards and calendar event-details now use RenderedEditor directly
(they only ever rendered in 'rendered' mode — MarkdownEditor was pure overhead).
The MarkdownEditor component is deleted; its types migrate to types.ts:
  - MarkdownPane, MarkdownViewState, DEFAULT_MARKDOWN_VIEW_STATE
  - RendererProps (sync props removed — now come from PaneSyncContext)
markdown-editor.tsx is emptied; removed from editor/index.ts.

## useEditorCallbacks hook (apps/web/src/state/use-editor-callbacks.tsx)
Extracts the EditorCallbacks construction (wikilink navigation, file
imports, embed rendering, tag/note listing) out of NoteEditor into a
dedicated hook. NoteEditor now calls useEditorCallbacks(isStandalone).

## PaneSyncContext (packages/editor/src/pane-sync-context.tsx)
A React context that carries the full pane-sync state so components no
longer need sync props drilled through them:
  - PaneSyncProvider  — wraps source + rendered panes in NoteEditor
  - useSourcePaneSync()  — NativeSourceEditor calls this for cursor/scroll/
                           focus requests, handlers, callbacks, isReadOnly
  - useRenderedPaneSync() — RenderedEditor calls this for the same
Both hooks return undefined outside a provider (graceful standalone use).

## Updated consumers
- NativeSourceEditor: drops all sync/callbacks props; calls useSourcePaneSync()
  and merges context over prop fallbacks. Props kept optional for backward compat.
- RenderedEditor: drops all sync props from RenderedEditorProps; calls
  useRenderedPaneSync() and merges context over props.
- NoteEditor: wraps markdown panes in PaneSyncProvider; passes only
  value/onChange/path/isStandalone/callbacks/onRegisterContextMenu to
  DynamicNoteRenderer (sync no longer prop-drilled).

All 156 tests pass; typecheck clean; lint clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Component

## Naming — one language, everything is a 'note type'

NoteTypeProvider renamed to NoteTypeDescriptor:
  - The 'Provider' suffix implied a React context provider; NoteEditor
    already called it 'activeDescriptor', which is the right term.

NoteViewRegistry merged into NoteTypeRegistry:
  - Both classes stored the same NoteTypeDescriptor objects; two separate
    registries was confusing mixing of 'Type' and 'View' namespaces.
  - NoteTypeRegistry.register() now returns a () => void disposer so
    plugins and built-ins can unregister cleanly (NoteViewDisposer removed).

Registration functions renamed for consistency:
  - registerBuiltinNoteView  -> registerBuiltinNoteType (all note-* packages)
  - registerMarkdownNoteView -> registerMarkdownNoteType (@notes/editor)
  - PluginContext.registerNoteView -> registerNoteType
  - Variable noteViewRegistry -> noteTypeRegistry everywhere

## NoteTypeViewDescriptor — properly typed viewComponent

Added NoteTypeViewDescriptor in packages/editor/src/note-view-descriptor.ts,
extending NoteTypeDescriptor with React-typed fields:
  - viewComponent?: ComponentType<RendererProps>  (not unknown)
  - toolbarItems?: TypedNoteTypeToolbarItem[]
  - contextMenuBuilder?: NoteViewContextMenuBuilder

All registerBuiltinNoteType functions now annotate their descriptor as
NoteTypeViewDescriptor so TypeScript validates the view component type at
registration time, not just at the point of use.

## Uniform RendererProps for all note view components

BoardView, CanvasView, CalendarView, MermaidView, GridView all previously
had bespoke local props interfaces. Each now uses RendererProps directly.

All 156 tests pass; typecheck clean; lint clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ove full type to editor

The previous split had NoteTypeDescriptor (core, unknown fields) and
NoteTypeViewDescriptor (editor, typed fields) — two interfaces for the
same concept, and a correspondingly split markdownNoteType / registerMarkdownNoteType.

## New structure

packages/core:
  NoteTypeDetector { id, detect }   — minimal interface for server-side detection
  NoteTypeRegistry<T extends NoteTypeDetector>  — generic; default T = NoteTypeDetector

packages/editor:
  NoteTypeDescriptor extends NoteTypeDetector  — THE complete descriptor; no opaque
    fields, all React types are concrete (viewComponent: ComponentType<RendererProps>,
    toolbarItems: NoteTypeToolbarItem[], contextMenuBuilder: NoteViewContextMenuBuilder)
  NoteTypeToolbarItem  — moved here from core (now properly typed with ReactNode)
  EditorMode defined directly (no NoteViewMode alias from core)

## Consequences

- NoteTypeViewDescriptor deleted entirely — it IS NoteTypeDescriptor now
- markdownNoteType in core is a bare NoteTypeDetector (id + detect only)
- markdownDescriptor in packages/editor is the complete descriptor, including
  viewComponent: RenderedEditor, supportedModes, supportsScrollSync — one place
- registerMarkdownNoteType accepts NoteTypeRegistry<NoteTypeDescriptor>
- All note-* packages import NoteTypeDescriptor from @notes/editor
- NoteTypeRegistry<NoteTypeDescriptor> used client-side; server still uses
  the untyped default (NoteTypeRegistry == NoteTypeRegistry<NoteTypeDetector>)
- Accessor helpers (getNoteViewComponent etc.) simplified: no casts needed since
  fields are already properly typed in NoteTypeDescriptor

All 156 tests pass; typecheck clean; lint clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@adam4813
adam4813 merged commit 6367b94 into main Aug 11, 2026
4 checks passed
@adam4813
adam4813 deleted the note-type-registry branch August 11, 2026 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant