@plutocms/docs 0.0.0-stage → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +1 -0
- package/.prettierignore +5 -0
- package/CHANGELOG.md +186 -0
- package/FEATURES.md +34 -0
- package/README.md +40 -2
- package/app/assets/css/pluto-layer.css +40 -0
- package/app/components/Pluto/Docs/Blocks/PlutoBlockEditor.vue +165 -0
- package/app/components/Pluto/Docs/Blocks/PlutoBlockInspector.vue +519 -0
- package/app/components/Pluto/Docs/Blocks/PlutoBlockMarkdownSplit.vue +161 -0
- package/app/components/Pluto/Docs/Blocks/PlutoBlockModeSwitch.vue +46 -0
- package/app/components/Pluto/Docs/Blocks/PlutoBlockOutline.vue +146 -0
- package/app/components/Pluto/Docs/Blocks/PlutoBlockOutlineList.vue +347 -0
- package/app/components/Pluto/Docs/Blocks/PlutoBlockPalette.vue +86 -0
- package/app/components/Pluto/Docs/Blocks/PlutoBlockPreviewFrame.vue +232 -0
- package/app/components/Pluto/Docs/Blocks/PlutoBlockPropControl.vue +269 -0
- package/app/components/Pluto/Docs/Blocks/PlutoBlockRawEditor.vue +62 -0
- package/app/components/Pluto/Docs/Blocks/PlutoBlockTextEditor.vue +92 -0
- package/app/components/Pluto/Docs/Blocks/PlutoPreviewBlock.vue +80 -0
- package/app/components/Pluto/Docs/PlutoFieldMdc.vue +117 -0
- package/app/components/Pluto/Docs/PlutoMdcInsertMenu.vue +36 -0
- package/app/components/PlutoContentPreview.vue +35 -0
- package/app/composables/pluto-block-editor.ts +459 -0
- package/app/composables/pluto-content-edit-link.ts +19 -0
- package/app/composables/pluto-content-preview.ts +29 -0
- package/app/composables/pluto-content-runtime.ts +237 -0
- package/app/pages/_pluto/preview.vue +555 -0
- package/app/plugins/pluto-docs.ts +50 -0
- package/i18n/locales/en.json +253 -0
- package/i18n/locales/pt-BR.json +289 -0
- package/modules/pluto-docs/blocks.ts +217 -0
- package/modules/pluto-docs/index.ts +230 -0
- package/modules/pluto-docs/manifest.ts +159 -0
- package/modules/pluto-docs/options.ts +258 -0
- package/nuxt.config.ts +36 -0
- package/package.json +56 -4
- package/server/handlers/navigation.get.ts +7 -0
- package/server/handlers/page.get.ts +7 -0
- package/server/handlers/surround.get.ts +7 -0
- package/server/plugins/pluto-docs.ts +25 -0
- package/server/utils/pluto-docs-runtime.ts +504 -0
- package/shared/types/docs.d.ts +40 -0
- package/shared/types/pluto-blocks.d.ts +10 -0
- package/shared/types/pluto-manifest.d.ts +6 -0
- package/shared/utils/blocks/component-meta.ts +285 -0
- package/shared/utils/blocks/define.ts +15 -0
- package/shared/utils/blocks/editor.ts +357 -0
- package/shared/utils/blocks/history.ts +94 -0
- package/shared/utils/blocks/mdc-props.ts +321 -0
- package/shared/utils/blocks/operations.ts +692 -0
- package/shared/utils/blocks/parse.ts +444 -0
- package/shared/utils/blocks/presets.ts +384 -0
- package/shared/utils/blocks/preview-marks.ts +98 -0
- package/shared/utils/blocks/preview-protocol.ts +195 -0
- package/shared/utils/blocks/registry.ts +295 -0
- package/shared/utils/blocks/serialize.ts +107 -0
- package/shared/utils/blocks/tree.ts +85 -0
- package/shared/utils/blocks/types.ts +195 -0
- package/shared/utils/content-route.ts +40 -0
- package/shared/utils/docs-type.ts +290 -0
- package/shared/utils/mdc-snippets.ts +114 -0
- package/shared/utils/navigation-config.ts +19 -0
- package/shared/utils/runtime-content.ts +403 -0
- package/tailwind.lint.css +10 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +7 -0
package/.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test/fixtures/** -text
|
package/.prettierignore
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.16.0](https://github.com/plutocms/docs/compare/v0.15.0...v0.16.0) (2026-10-31)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* block sources and component meta mapping for the editor ([#75](https://github.com/plutocms/docs/issues/75)) ([cc04cf0](https://github.com/plutocms/docs/commit/cc04cf044112b6d4432817711c1e8ed34cce1b7e))
|
|
9
|
+
* discover editor blocks from definePlutoBlock in components/content ([#78](https://github.com/plutocms/docs/issues/78)) ([0ce82b9](https://github.com/plutocms/docs/commit/0ce82b9672b276c6b50edf6f647fd42490595c84))
|
|
10
|
+
|
|
11
|
+
## [0.15.0](https://github.com/plutocms/nuxt-content/compare/v0.14.0...v0.15.0) (2026-10-31)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### ⚠ BREAKING CHANGES
|
|
15
|
+
|
|
16
|
+
* the package is now @plutocms/docs. The config key plutoNuxtContent is now plutoDocs. The i18n namespace nuxt-content is now docs. The env var PLUTO_NUXT_CONTENT_PATH is not used in this repo.
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* rename the package to @plutocms/docs ([#71](https://github.com/plutocms/nuxt-content/issues/71)) ([b480cfa](https://github.com/plutocms/nuxt-content/commit/b480cfacca3df143ce5844ec26f46679e5ea293b))
|
|
21
|
+
|
|
22
|
+
## [0.14.0](https://github.com/plutocms/nuxt-content/compare/v0.13.0...v0.14.0) (2026-10-31)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
* the docs types go to the pages nav group, with a navGroup option ([#65](https://github.com/plutocms/nuxt-content/issues/65)) ([8d366e7](https://github.com/plutocms/nuxt-content/commit/8d366e76558d7a9b163aa6c9fca77a9eaa2a9358))
|
|
28
|
+
|
|
29
|
+
## [0.13.0](https://github.com/plutocms/nuxt-content/compare/v0.12.3...v0.13.0) (2026-10-30)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### ⚠ BREAKING CHANGES
|
|
33
|
+
|
|
34
|
+
* the layer extends @plutocms/core >=0.38.0, not @plutocms/pluto. PLUTO_PLUTO_PATH is now PLUTO_CORE_PATH.
|
|
35
|
+
|
|
36
|
+
### Features
|
|
37
|
+
|
|
38
|
+
* extend @plutocms/core ([#59](https://github.com/plutocms/nuxt-content/issues/59)) ([a5ca2b9](https://github.com/plutocms/nuxt-content/commit/a5ca2b9a1b340d6ceb3d00487414d098c9fae683))
|
|
39
|
+
|
|
40
|
+
## [0.12.3](https://github.com/plutocms/nuxt-content/compare/v0.12.2...v0.12.3) (2026-10-29)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Bug Fixes
|
|
44
|
+
|
|
45
|
+
* **deps:** accept every 0.x version of the [@plutocms](https://github.com/plutocms) packages; shared CI and the update workflow ([#42](https://github.com/plutocms/nuxt-content/issues/42)) ([91b2f58](https://github.com/plutocms/nuxt-content/commit/91b2f58e1f9fa6a47866df6d84e89b59552d9af4))
|
|
46
|
+
|
|
47
|
+
## [0.12.2](https://github.com/plutocms/nuxt-content/compare/v0.12.1...v0.12.2) (2026-10-29)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Bug Fixes
|
|
51
|
+
|
|
52
|
+
* **deps:** use @plutocms/pluto 0.34 ([#40](https://github.com/plutocms/nuxt-content/issues/40)) ([000c153](https://github.com/plutocms/nuxt-content/commit/000c1538d414bc95483a4293f826a747d47820d5))
|
|
53
|
+
|
|
54
|
+
## [0.12.1](https://github.com/plutocms/nuxt-content/compare/v0.12.0...v0.12.1) (2026-10-29)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
### Bug Fixes
|
|
58
|
+
|
|
59
|
+
* **deps:** use @plutocms/pluto 0.33 ([#38](https://github.com/plutocms/nuxt-content/issues/38)) ([ca54a9f](https://github.com/plutocms/nuxt-content/commit/ca54a9fb8b2bfa8ce5349fbcbd488941413064d6))
|
|
60
|
+
|
|
61
|
+
## [0.12.0](https://github.com/plutocms/nuxt-content/compare/v0.11.0...v0.12.0) (2026-10-29)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
### Features
|
|
65
|
+
|
|
66
|
+
* **mdc:** use the Markdown code editor of core ([#36](https://github.com/plutocms/nuxt-content/issues/36)) ([b11c355](https://github.com/plutocms/nuxt-content/commit/b11c3550c165373a7080bf4583ceddd4b104d2ae))
|
|
67
|
+
|
|
68
|
+
## [0.11.0](https://github.com/plutocms/nuxt-content/compare/v0.10.0...v0.11.0) (2026-10-28)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
### Features
|
|
72
|
+
|
|
73
|
+
* **editor:** block list layout, scroll area, preview loading, Markdown footer, canonical classes and Prettier ([#33](https://github.com/plutocms/nuxt-content/issues/33)) ([6743ca9](https://github.com/plutocms/nuxt-content/commit/6743ca98f044641fc4ac8074270efb1b6ce33e75))
|
|
74
|
+
|
|
75
|
+
## [0.10.0](https://github.com/plutocms/nuxt-content/compare/v0.9.0...v0.10.0) (2026-10-28)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
### Features
|
|
79
|
+
|
|
80
|
+
* **editor:** show the block row controls on hover ([#30](https://github.com/plutocms/nuxt-content/issues/30)) ([5a2faf5](https://github.com/plutocms/nuxt-content/commit/5a2faf56461cc944670931eafea1cbac705623a4))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
### Bug Fixes
|
|
84
|
+
|
|
85
|
+
* **deps:** use @plutocms/pluto 0.29 ([#32](https://github.com/plutocms/nuxt-content/issues/32)) ([423c6db](https://github.com/plutocms/nuxt-content/commit/423c6dba402f67ad8306651e377c5697041d9ddb))
|
|
86
|
+
|
|
87
|
+
## [0.9.0](https://github.com/plutocms/nuxt-content/compare/v0.8.0...v0.9.0) (2026-10-27)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
### ⚠ BREAKING CHANGES
|
|
91
|
+
|
|
92
|
+
* the layer reads `plutoGithub`, not `plutoCms`. An app that sets `plutoCms` options sets `plutoGithub` now.
|
|
93
|
+
|
|
94
|
+
### Features
|
|
95
|
+
|
|
96
|
+
* use the GitHub names of pluto 0.28 ([#28](https://github.com/plutocms/nuxt-content/issues/28)) ([f3f2ee7](https://github.com/plutocms/nuxt-content/commit/f3f2ee7dc08a0f5872eab82fb02e7271cded822f))
|
|
97
|
+
|
|
98
|
+
## [0.8.0](https://github.com/plutocms/nuxt-content/compare/v0.7.0...v0.8.0) (2026-10-27)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
### ⚠ BREAKING CHANGES
|
|
102
|
+
|
|
103
|
+
* `/api/_pluto/nuxt-content/:type/{page,navigation,surround}` is now `/api/pluto/v1/nuxt-content/:type/{page,navigation,surround}`, and the answer is `{ data }`. A site that uses the composables needs no change.
|
|
104
|
+
|
|
105
|
+
### Features
|
|
106
|
+
|
|
107
|
+
* serve the layer routes on the public API of Pluto, and make the docs pages public ([#26](https://github.com/plutocms/nuxt-content/issues/26)) ([9c1ddd9](https://github.com/plutocms/nuxt-content/commit/9c1ddd982d7c24ff22e70337a345a76ddeb63131))
|
|
108
|
+
|
|
109
|
+
## [0.7.0](https://github.com/plutocms/nuxt-content/compare/v0.6.0...v0.7.0) (2026-10-27)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
### ⚠ BREAKING CHANGES
|
|
113
|
+
|
|
114
|
+
* the default render mode is now 'hybrid'. Content pages must not be prerendered in hybrid or runtime mode. Set `render: 'build'` to keep a fully static site. Needs @plutocms/pluto with `markPublicRead` and `readFile`.
|
|
115
|
+
|
|
116
|
+
### Features
|
|
117
|
+
|
|
118
|
+
* render the pages in hybrid mode by default, with build and runtime modes ([#24](https://github.com/plutocms/nuxt-content/issues/24)) ([44efefe](https://github.com/plutocms/nuxt-content/commit/44efefec130c9a48744ec79eba0f454399f5560b))
|
|
119
|
+
|
|
120
|
+
## [0.6.0](https://github.com/plutocms/nuxt-content/compare/v0.5.0...v0.6.0) (2026-10-27)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
### Features
|
|
124
|
+
|
|
125
|
+
* use the admin bar buttons of core, and shorten the README ([#22](https://github.com/plutocms/nuxt-content/issues/22)) ([568f54b](https://github.com/plutocms/nuxt-content/commit/568f54b3f0a73e0346c44f2aec44e6c34c0320fe))
|
|
126
|
+
|
|
127
|
+
## [0.5.0](https://github.com/plutocms/nuxt-content/compare/v0.4.1...v0.5.0) (2026-10-26)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
### Features
|
|
131
|
+
|
|
132
|
+
* **deps:** use @plutocms/pluto 0.24 ([#20](https://github.com/plutocms/nuxt-content/issues/20)) ([acb7195](https://github.com/plutocms/nuxt-content/commit/acb71955bdf7f2fb18d545e6281aaef50cc9fc23))
|
|
133
|
+
|
|
134
|
+
## [0.4.1](https://github.com/plutocms/nuxt-content/compare/v0.4.0...v0.4.1) (2026-10-26)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
### Bug Fixes
|
|
138
|
+
|
|
139
|
+
* **editor:** type the editor for the sites that type-check the layer ([#18](https://github.com/plutocms/nuxt-content/issues/18)) ([c7b08bb](https://github.com/plutocms/nuxt-content/commit/c7b08bbb92e7417487303d599ba42820c0d07059))
|
|
140
|
+
|
|
141
|
+
## [0.4.0](https://github.com/plutocms/nuxt-content/compare/v0.3.2...v0.4.0) (2026-10-26)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
### Features
|
|
145
|
+
|
|
146
|
+
* add the visual block editor and a hybrid Markdown mode ([#16](https://github.com/plutocms/nuxt-content/issues/16)) ([22c8c5e](https://github.com/plutocms/nuxt-content/commit/22c8c5e7f7f745ced4ef01d85160b08aa5749d7a))
|
|
147
|
+
|
|
148
|
+
## [0.3.2](https://github.com/plutocms/nuxt-content/compare/v0.3.1...v0.3.2) (2026-10-25)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
### Bug Fixes
|
|
152
|
+
|
|
153
|
+
* **i18n:** use the nuxt-content namespace, so core translates the labels ([#14](https://github.com/plutocms/nuxt-content/issues/14)) ([4c44e3c](https://github.com/plutocms/nuxt-content/commit/4c44e3ce43d8a7d47e9ce0cd20477c41a676ba39))
|
|
154
|
+
|
|
155
|
+
## [0.3.1](https://github.com/plutocms/nuxt-content/compare/v0.3.0...v0.3.1) (2026-10-25)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
### Bug Fixes
|
|
159
|
+
|
|
160
|
+
* **i18n:** translate the doc type in the admin ([#12](https://github.com/plutocms/nuxt-content/issues/12)) ([2ff7034](https://github.com/plutocms/nuxt-content/commit/2ff70343257ecf912be2e39a94a73a80a9c8aeee))
|
|
161
|
+
|
|
162
|
+
## [0.3.0](https://github.com/plutocms/nuxt-content/compare/v0.2.0...v0.3.0) (2026-10-25)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
### Features
|
|
166
|
+
|
|
167
|
+
* add New Page and Edit Page to the admin bar ([#9](https://github.com/plutocms/nuxt-content/issues/9)) ([c5fe6ad](https://github.com/plutocms/nuxt-content/commit/c5fe6ad5ebc4a868aacfe8013647b325fc3898a7))
|
|
168
|
+
|
|
169
|
+
## [0.2.0](https://github.com/plutocms/nuxt-content/compare/v0.1.0...v0.2.0) (2026-10-25)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
### Features
|
|
173
|
+
|
|
174
|
+
* **deps:** use @plutocms/pluto 0.20 and default to no post type ([#6](https://github.com/plutocms/nuxt-content/issues/6)) ([b4fb0f0](https://github.com/plutocms/nuxt-content/commit/b4fb0f044da3411162047503e9ac8ab23df48001))
|
|
175
|
+
|
|
176
|
+
## 0.1.0 (2026-10-25)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
### Features
|
|
180
|
+
|
|
181
|
+
* add the Nuxt Content layer with a docs preset ([90db635](https://github.com/plutocms/nuxt-content/commit/90db63534eead5ed108d01304eae3092072a44dc))
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
### Miscellaneous Chores
|
|
185
|
+
|
|
186
|
+
* start the release line at 0.1.0 ([36cfc78](https://github.com/plutocms/nuxt-content/commit/36cfc7892ae904e2bc7014da643c1fd3e5b18d1a))
|
package/FEATURES.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Features
|
|
2
|
+
|
|
3
|
+
This file lists every implemented feature of this project. Each entry links to a skill file
|
|
4
|
+
with full detail, so this file stays short.
|
|
5
|
+
|
|
6
|
+
See the workspace-level `CLAUDE.md` (one directory up) for the delegation, writing, and git
|
|
7
|
+
policies shared by every project here.
|
|
8
|
+
|
|
9
|
+
- Docs setup: the `plutoDocs` module, its options, and the plugins that register
|
|
10
|
+
the types. See @.claude/skills/docs-setup/SKILL.md.
|
|
11
|
+
- Docs preset: the `docs` page type, the frontmatter hooks, and tree order. See
|
|
12
|
+
@.claude/skills/docs-preset/SKILL.md.
|
|
13
|
+
- MDC field: the `mdc` field widget. Visual mode (the block editor) and Markdown mode (with the
|
|
14
|
+
Insert menu of snippets). See @.claude/skills/mdc-field/SKILL.md.
|
|
15
|
+
- Render modes: `plutoDocs.render` is `'hybrid'` (default: the build data, and the repository for the
|
|
16
|
+
pages that changed after the build), `'build'` or `'runtime'`. A build manifest, server routes, `.navigation.yml`
|
|
17
|
+
and frontmatter parity, and the composables `usePlutoContentPage`, `usePlutoContentNavigation` and
|
|
18
|
+
`usePlutoContentSurround` that hide the mode. The layer routes are on the public API
|
|
19
|
+
(`/api/pluto/v1/docs/:type/{page,navigation,surround}`, `{ data }` answers), and the docs type is
|
|
20
|
+
public on core's `/api/pluto/v1/content/<type>`. See @.claude/skills/docs-setup/SKILL.md.
|
|
21
|
+
- Page links: the View link of the form (`previewUrl`), the "New Page" and "Edit Page" buttons of the admin bar (core draws them from `adminBar`),
|
|
22
|
+
`contentRoutePath`, `contentEditPath`, and `usePlutoContentEditLink`. See
|
|
23
|
+
@.claude/skills/page-links/SKILL.md.
|
|
24
|
+
- Admin bar offset: CSS that keeps a Nuxt UI header under the Pluto admin bar. See
|
|
25
|
+
@.claude/skills/admin-bar-offset/SKILL.md.
|
|
26
|
+
- Block model: the pure model of the visual block editor (parse, serialize, edit, history,
|
|
27
|
+
presets, registry, preview protocol). The text of an unedited block stays byte for byte. See
|
|
28
|
+
@.claude/skills/block-model/SKILL.md.
|
|
29
|
+
- Block discovery: a component in `components/content/` (the app or a layer) that calls
|
|
30
|
+
`definePlutoBlock({...})` becomes a block in the palette, with its props and slots read from its
|
|
31
|
+
TypeScript types. Opt-in. The option `plutoDocs.blocks.discover` turns it off. See
|
|
32
|
+
@.claude/skills/block-model/SKILL.md.
|
|
33
|
+
- Block editor: the visual editor for the page body. A live preview, the block list, the palette, and
|
|
34
|
+
the controls of the selected block, with no raw MDC. See @.claude/skills/block-editor/SKILL.md.
|
package/README.md
CHANGED
|
@@ -1,3 +1,41 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Docs layer for Pluto
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Edit the Markdown pages of a [Nuxt Content](https://content.nuxt.com) site in the Pluto admin,
|
|
4
|
+
with a visual block editor and a live preview. The pages stay Markdown files in your GitHub
|
|
5
|
+
repository, and each save is a commit.
|
|
6
|
+
|
|
7
|
+
**Docs:** [Docs layer](https://pluto.vulmix.dev/docs/layers/docs) ·
|
|
8
|
+
[Edit pages visually](https://pluto.vulmix.dev/docs/guides/edit-pages-visually)
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @plutocms/docs
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
// nuxt.config.ts
|
|
18
|
+
export default defineNuxtConfig({
|
|
19
|
+
extends: [['@plutocms/docs', { install: true }]],
|
|
20
|
+
|
|
21
|
+
plutoDocs: {
|
|
22
|
+
types: [{ preset: 'docs', name: 'doc', source: '' }],
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The layer brings `@plutocms/core`. It needs Node 22.5 or newer. The docs show the rest of the
|
|
28
|
+
setup: the collections, the CSS entry, `PlutoRoot`, and the GitHub App.
|
|
29
|
+
|
|
30
|
+
## What you get
|
|
31
|
+
|
|
32
|
+
- A `docs` page type for nested, numbered Markdown pages.
|
|
33
|
+
- A visual block editor with a live preview, and a Markdown mode.
|
|
34
|
+
- Your own components in the palette: call `definePlutoBlock({ label, icon })` in a component of `components/content/`. The props and slots come from its TypeScript types.
|
|
35
|
+
- Hybrid render mode (default): a save shows on the site at once, with no wait for a build. Build and runtime modes are options.
|
|
36
|
+
- "New Page" and "Edit Page" in the admin bar, and an "Edit in admin" link for the public page.
|
|
37
|
+
- The pages on the public API of Pluto (`/api/pluto/v1/content/<type>`), for a site in any stack.
|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
|
|
41
|
+
MIT
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The docs layer's CSS. It is not a Tailwind entry.
|
|
3
|
+
*
|
|
4
|
+
* The app's CSS entry owns the one Tailwind build of the app. It imports this
|
|
5
|
+
* file through `#build/pluto.css`. This file imports no Tailwind, adds no
|
|
6
|
+
* @theme and no @source. See the single-tailwind-build skill in pluto.
|
|
7
|
+
*
|
|
8
|
+
* It offsets a Nuxt UI site (a docs site) under the Pluto admin bar. The bar
|
|
9
|
+
* is `position: fixed`, 40px high, and shows on public pages for signed-in
|
|
10
|
+
* staff. Pluto marks <html> with `data-pluto-admin-bar` while the bar shows.
|
|
11
|
+
*
|
|
12
|
+
* The site sets its header height in `--pluto-site-header-height` (default
|
|
13
|
+
* 4rem). Without the bar nothing changes. With the bar:
|
|
14
|
+
* - the page moves down by the bar height (`body` padding), so no content is
|
|
15
|
+
* under the bar;
|
|
16
|
+
* - `--ui-header-height` grows by the bar height. The Nuxt UI parts that stick
|
|
17
|
+
* below the header read it: the page aside, the table of contents, the
|
|
18
|
+
* mobile menu. They then stick below the bar and the header;
|
|
19
|
+
* - `UHeader` (class `pluto-header-offset`) keeps its own height and sticks
|
|
20
|
+
* below the bar.
|
|
21
|
+
* The admin pages (`data-pluto-admin` on <html>) never use these rules.
|
|
22
|
+
*/
|
|
23
|
+
:root:not([data-pluto-admin]) {
|
|
24
|
+
--pluto-site-header-height: 4rem;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
:root[data-pluto-admin-bar]:not([data-pluto-admin]) {
|
|
28
|
+
--ui-header-height: calc(
|
|
29
|
+
var(--pluto-site-header-height) + var(--pluto-admin-bar-height)
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
:root[data-pluto-admin-bar]:not([data-pluto-admin]) body {
|
|
34
|
+
padding-top: var(--pluto-admin-bar-height);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
:root[data-pluto-admin-bar]:not([data-pluto-admin]) .pluto-header-offset {
|
|
38
|
+
top: var(--pluto-admin-bar-height);
|
|
39
|
+
height: var(--pluto-site-header-height);
|
|
40
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// The component layer. Only the two editor roots import it, so it stays out of the public pages.
|
|
3
|
+
import componentBlocks from '#pluto-docs/blocks'
|
|
4
|
+
// The visual block editor: the canvas of the 'canvas' form layout. The canvas
|
|
5
|
+
// shows the live preview of the page. The block list goes in the left panel,
|
|
6
|
+
// and the controls of the selected block go in the right panel (a tab of the
|
|
7
|
+
// form's aside). The widget keeps all the state, so the panels are slots of
|
|
8
|
+
// this one component tree. The value is the Markdown text of the page body. A
|
|
9
|
+
// block that no edit touched keeps its bytes, so opening and saving a page
|
|
10
|
+
// with no edit changes nothing. See the block-editor skill.
|
|
11
|
+
import PlutoBlockInspector from './PlutoBlockInspector.vue'
|
|
12
|
+
import PlutoBlockOutline from './PlutoBlockOutline.vue'
|
|
13
|
+
import PlutoBlockPalette from './PlutoBlockPalette.vue'
|
|
14
|
+
import PlutoBlockPreviewFrame from './PlutoBlockPreviewFrame.vue'
|
|
15
|
+
|
|
16
|
+
const props = defineProps<{
|
|
17
|
+
modelValue: string
|
|
18
|
+
disabled?: boolean
|
|
19
|
+
mode: 'visual' | 'markdown'
|
|
20
|
+
}>()
|
|
21
|
+
|
|
22
|
+
const emit = defineEmits<{
|
|
23
|
+
'update:modelValue': [value: string]
|
|
24
|
+
'update:mode': [mode: 'visual' | 'markdown']
|
|
25
|
+
}>()
|
|
26
|
+
|
|
27
|
+
const { t } = usePlutoI18n()
|
|
28
|
+
const form = usePlutoContentForm()
|
|
29
|
+
const blockSources = usePlutoBlockSources(componentBlocks)
|
|
30
|
+
|
|
31
|
+
const editor = createPlutoBlockEditor({
|
|
32
|
+
source: () => props.modelValue,
|
|
33
|
+
emit: (value) => emit('update:modelValue', value),
|
|
34
|
+
sources: blockSources,
|
|
35
|
+
})
|
|
36
|
+
provide(plutoBlockEditorKey, editor)
|
|
37
|
+
|
|
38
|
+
// The front matter for the preview header: the item without the body and the internal `_` keys.
|
|
39
|
+
const frontmatter = computed<Record<string, unknown>>(() => {
|
|
40
|
+
const item = form?.item.value ?? {}
|
|
41
|
+
return Object.fromEntries(
|
|
42
|
+
Object.entries(item).filter(
|
|
43
|
+
([key]) => key !== 'body' && !key.startsWith('_')
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// A selection brings the Block tab forward (and opens the panel on a narrow screen). On a narrow
|
|
49
|
+
// screen the block list is a slideover. A choice in it closes the slideover, so the controls show.
|
|
50
|
+
const wide = useMediaQuery('(min-width: 1024px)')
|
|
51
|
+
watch(
|
|
52
|
+
() => editor.selection.value,
|
|
53
|
+
(selection) => {
|
|
54
|
+
if (!selection) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
if (!wide.value && selection.source === 'ui') {
|
|
58
|
+
document
|
|
59
|
+
.querySelector('[data-block-outline]')
|
|
60
|
+
?.closest('[role="dialog"]')
|
|
61
|
+
?.querySelector<HTMLElement>('button[data-slot="close"]')
|
|
62
|
+
?.click()
|
|
63
|
+
}
|
|
64
|
+
form?.openPanel('block')
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
// One undo stack for the page: Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z (or Ctrl+Y). They act while the
|
|
69
|
+
// focus is in the editor (a panel or the canvas). Elsewhere (the Page tab) the browser keeps its own undo.
|
|
70
|
+
function onKeydown(event: KeyboardEvent) {
|
|
71
|
+
if (props.disabled || !(event.ctrlKey || event.metaKey) || event.altKey) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
const target = event.target
|
|
75
|
+
const inside =
|
|
76
|
+
target instanceof Element &&
|
|
77
|
+
(target.closest('[data-pluto-block-ui]') ||
|
|
78
|
+
target.closest('[data-block-canvas]'))
|
|
79
|
+
const key = event.key.toLowerCase()
|
|
80
|
+
if (!inside) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
const row =
|
|
84
|
+
target instanceof Element
|
|
85
|
+
? target.closest<HTMLElement>('[data-block-row]')?.dataset.blockRow
|
|
86
|
+
: undefined
|
|
87
|
+
if (key === 'z' && !event.shiftKey) {
|
|
88
|
+
event.preventDefault()
|
|
89
|
+
editor.undo()
|
|
90
|
+
} else if ((key === 'z' && event.shiftKey) || key === 'y') {
|
|
91
|
+
event.preventDefault()
|
|
92
|
+
editor.redo()
|
|
93
|
+
} else {
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
// A list row that moves in the DOM loses the focus. Give it back, so the next shortcut works.
|
|
97
|
+
nextTick(() => {
|
|
98
|
+
if (
|
|
99
|
+
row &&
|
|
100
|
+
(!document.activeElement || document.activeElement === document.body)
|
|
101
|
+
) {
|
|
102
|
+
document
|
|
103
|
+
.querySelector<HTMLElement>(`[data-block-row="${CSS.escape(row)}"]`)
|
|
104
|
+
?.focus()
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
onMounted(() => document.addEventListener('keydown', onKeydown, true))
|
|
110
|
+
onBeforeUnmount(() => document.removeEventListener('keydown', onKeydown, true))
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<template>
|
|
114
|
+
<div class="flex h-full min-h-0 flex-col" data-block-canvas>
|
|
115
|
+
<div v-if="editor.error.value" class="p-4">
|
|
116
|
+
<UAlert
|
|
117
|
+
:title="t('docs.editor.parseErrorTitle')"
|
|
118
|
+
:description="t('docs.editor.parseError')"
|
|
119
|
+
:actions="[
|
|
120
|
+
{
|
|
121
|
+
label: t('docs.editor.switchToMarkdown'),
|
|
122
|
+
color: 'neutral',
|
|
123
|
+
variant: 'outline',
|
|
124
|
+
onClick: () => emit('update:mode', 'markdown'),
|
|
125
|
+
},
|
|
126
|
+
]"
|
|
127
|
+
color="warning"
|
|
128
|
+
variant="subtle"
|
|
129
|
+
icon="i-lucide-triangle-alert"
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<PlutoBlockPreviewFrame
|
|
134
|
+
v-else
|
|
135
|
+
:frontmatter="frontmatter"
|
|
136
|
+
class="min-h-0 flex-1"
|
|
137
|
+
/>
|
|
138
|
+
|
|
139
|
+
<template v-if="!editor.error.value">
|
|
140
|
+
<PlutoContentCanvasPanel
|
|
141
|
+
id="blocks"
|
|
142
|
+
:label="t('docs.editor.blocks')"
|
|
143
|
+
side="left"
|
|
144
|
+
icon="i-lucide-blocks"
|
|
145
|
+
>
|
|
146
|
+
<PlutoBlockOutline
|
|
147
|
+
:mode="mode"
|
|
148
|
+
:disabled="disabled"
|
|
149
|
+
@update:mode="emit('update:mode', $event)"
|
|
150
|
+
/>
|
|
151
|
+
</PlutoContentCanvasPanel>
|
|
152
|
+
|
|
153
|
+
<PlutoContentCanvasPanel
|
|
154
|
+
id="block"
|
|
155
|
+
:label="t('docs.editor.blockTab')"
|
|
156
|
+
side="right"
|
|
157
|
+
icon="i-lucide-square-pen"
|
|
158
|
+
>
|
|
159
|
+
<PlutoBlockInspector :disabled="disabled" />
|
|
160
|
+
</PlutoContentCanvasPanel>
|
|
161
|
+
|
|
162
|
+
<PlutoBlockPalette />
|
|
163
|
+
</template>
|
|
164
|
+
</div>
|
|
165
|
+
</template>
|