Commit 58e292d
feat: headless toolbar (#2657)
* feat: headless toolbar
* feat: headless toolbar (caio) (#2669)
* feat: headless toolbar
* fix(headless-toolbar): correctness fixes and DX improvements
Correctness:
- Use resolveStateEditor for undo/redo history depth (fixes header/footer)
- Remove early return gating on color/highlight annotation sync
- Subscribe to zoomChange event for immediate zoom state updates
- Refresh snapshot after execute() for superdoc-level commands
- Fix redundant documentMode self-comparison in isCommandDisabled
DX:
- Make execute() required on HeadlessToolbarController type
- Normalize font-size values with unit (e.g. '12pt' not '12')
- Preserve full font-family CSS value (e.g. 'Arial, sans-serif' not 'Arial')
- Normalize color values to lowercase
- Add execute('image') handler (file picker + insertion)
- Fix demo to use execute() consistently for all commands
- Fix demo font selects to use option.value not option.label
- Remove unused RegistryMode/mode abstraction
- Rewrite README with toolbar: null setup and command reference table
* feat(headless-toolbar): add multi-framework examples and DX improvements
Replace single Vue demo with 5 framework examples showcasing different
toolbar patterns:
- react-shadcn: classic top ribbon (Radix + Tailwind + Lucide)
- react-mui: floating bubble bar (MUI + Material Icons)
- vue-vuetify: sidebar panel (Vuetify 3 + MDI)
- svelte-shadcn: compact bottom bar (Svelte 5 + Tailwind + Lucide)
- vanilla: minimal top bar (plain HTML/CSS/JS + Lucide)
API improvements:
- execute() now auto-restores editor focus after commands
- Add DEFAULT_TEXT_COLOR_OPTIONS and DEFAULT_HIGHLIGHT_COLOR_OPTIONS
constants
* fix(headless-toolbar): address review findings
- Color execute: run annotation sync unconditionally but return the
mark command result (not always true)
- Image execute: add .catch() with console.error instead of silently
swallowing errors
- MUI example: remove unused variables, guard exec against null
controller on first render
* feat(headless-toolbar): add React hook and Vue composable
Ship useHeadlessToolbar() for React and Vue:
import { useHeadlessToolbar } from 'superdoc/headless-toolbar/react';
const { snapshot, execute } = useHeadlessToolbar(superdoc, commands);
Handles subscribe/unsubscribe, state updates, and cleanup
automatically. Eliminates the useState + useEffect + useRef
boilerplate that every React consumer would write.
Vue composable follows the same API with shallowRef reactivity
and onBeforeUnmount cleanup.
Update react-shadcn example to use the hook as proof.
* feat(headless-toolbar): add typed payloads and snapshot values
Add ToolbarPayloadMap and ToolbarValueMap type maps that give
compile-time safety to execute() and snapshot.commands[id].value:
toolbar.execute('font-size', '14pt') // ✓
toolbar.execute('font-size', 14) // ✗ type error
toolbar.execute('bold', 'wrong') // ✗ type error
snapshot.commands['zoom']?.value // type: number | undefined
snapshot.commands['font-size']?.value // type: string | undefined
No runtime changes — types only.
* fix(superdoc): make documentMode optional in Config type
documentMode defaults to 'editing' at runtime but the JSDoc typedef
marked it as required, causing TypeScript errors when constructing
SuperDoc without explicitly passing it.
* chore(examples): remove toolbar: null from all examples
* docs(headless-toolbar): add headless toolbar documentation
Restructure toolbar docs into a group with four pages:
- overview: decision page (built-in vs headless)
- built-in: existing toolbar docs (moved from toolbar.mdx)
- headless: full API reference with command table and typed examples
- examples: 5 framework showcases (React shadcn, React MUI, Vue
Vuetify, Svelte, vanilla JS)
Add doctest support for headless toolbar code examples.
Update SuperDoc configuration docs for toolbar parameter.
Add redirect from /modules/toolbar to /modules/toolbar/overview.
Add superdoc/headless-toolbar to import allowlist.
* fix(headless-toolbar): include fallback fonts in Aptos constant
Aptos constant now includes fallback fonts ('Aptos, Arial, sans-serif')
matching what documents actually store. Without fallbacks, the snapshot
value wouldn't match the constant, breaking select components.
* fix(examples): register Vuetify components and directives
* refactor(examples): simplify Vue Vuetify sidebar toolbar layout
* fix(examples): add Tailwind v4 @reference directive for Svelte styles
* fix(examples): only set select value when it matches an option
* fix(headless-toolbar): address author review feedback
- Remove editorDestroy subscription — the event fires during teardown
when the editor may be in an inconsistent state, causing the refresh
cycle to read from a dying editor. editorCreate is sufficient.
- Remove auto-focus from execute() — the built-in toolbar has nuanced
focus logic (pending marks, header/footer exclusion, dropdown
detection) that a simple view.focus() doesn't replicate. Better
handled as a follow-up with proper parity.
- Restore onMouseDown preventDefault in react-shadcn example since
focus is no longer handled by execute().
- Update docs and README to remove focus handling claims.
---------
Co-authored-by: Artem Nistuley <artem@harbourshare.com>
* fix(headless-toolbar): add missing type exports, null guard, and cleanup
- Export ToolbarCommandStates, ToolbarExecuteFn, ToolbarPayloadMap,
ToolbarValueMap from super-editor index (fixes consumer TS errors)
- Accept null/undefined in Vue composable to match React hook behavior
- Replace duplicated React/Vue hooks in superdoc with re-exports
- Add vite aliases for headless-toolbar sub-path resolution
- Remove unused ALIGN_ICONS, dead CSS in vanilla example
* ci(examples): add headless toolbar examples to CI smoke tests
Add advanced-headless-toolbar job to ci-examples workflow, running
all 5 framework examples (react-shadcn, react-mui, vue-vuetify,
svelte-shadcn, vanilla) as a matrix strategy.
* fix(headless-toolbar): add @types/react and fix type-check error
- Add @types/react as devDependency for headless-toolbar React hook
- Fix TS2345 in react.ts execute callback (unknown → any cast for
typed payload passthrough)
---------
Co-authored-by: Caio Pizzol <97641911+caio-pizzol@users.noreply.github.com>
Co-authored-by: Caio Pizzol <caio@harbourshare.com>1 parent d2c45ec commit 58e292d
76 files changed
Lines changed: 7967 additions & 184 deletions
File tree
- .github/workflows
- apps/docs/modules/toolbar
- examples/advanced/headless-toolbar
- react-mui
- public
- src
- react-shadcn
- public
- src
- svelte-shadcn
- public
- src
- vanilla
- public
- src
- vue-vuetify
- public
- src
- packages
- super-editor
- src
- editors/v1
- headless-toolbar
- helpers
- superdoc
- src
- core
- types
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
134 | 134 | | |
135 | 135 | | |
136 | 136 | | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
137 | 157 | | |
138 | 158 | | |
139 | 159 | | |
| |||
152 | 172 | | |
153 | 173 | | |
154 | 174 | | |
155 | | - | |
| 175 | + | |
156 | 176 | | |
157 | 177 | | |
158 | 178 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
49 | | - | |
| 49 | + | |
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
| |||
136 | 136 | | |
137 | 137 | | |
138 | 138 | | |
139 | | - | |
| 139 | + | |
140 | 140 | | |
141 | 141 | | |
142 | 142 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
Lines changed: 26 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
Binary file not shown.
0 commit comments