@plutocms/docs 0.0.0-stage → 0.15.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.
Files changed (61) hide show
  1. package/.gitattributes +1 -0
  2. package/.prettierignore +5 -0
  3. package/CHANGELOG.md +178 -0
  4. package/FEATURES.md +30 -0
  5. package/README.md +39 -2
  6. package/app/assets/css/pluto-layer.css +40 -0
  7. package/app/components/Pluto/Docs/Blocks/PlutoBlockEditor.vue +163 -0
  8. package/app/components/Pluto/Docs/Blocks/PlutoBlockInspector.vue +519 -0
  9. package/app/components/Pluto/Docs/Blocks/PlutoBlockMarkdownSplit.vue +159 -0
  10. package/app/components/Pluto/Docs/Blocks/PlutoBlockModeSwitch.vue +46 -0
  11. package/app/components/Pluto/Docs/Blocks/PlutoBlockOutline.vue +146 -0
  12. package/app/components/Pluto/Docs/Blocks/PlutoBlockOutlineList.vue +347 -0
  13. package/app/components/Pluto/Docs/Blocks/PlutoBlockPalette.vue +85 -0
  14. package/app/components/Pluto/Docs/Blocks/PlutoBlockPreviewFrame.vue +233 -0
  15. package/app/components/Pluto/Docs/Blocks/PlutoBlockPropControl.vue +246 -0
  16. package/app/components/Pluto/Docs/Blocks/PlutoBlockRawEditor.vue +62 -0
  17. package/app/components/Pluto/Docs/Blocks/PlutoBlockTextEditor.vue +92 -0
  18. package/app/components/Pluto/Docs/Blocks/PlutoPreviewBlock.vue +80 -0
  19. package/app/components/Pluto/Docs/PlutoFieldMdc.vue +117 -0
  20. package/app/components/Pluto/Docs/PlutoMdcInsertMenu.vue +36 -0
  21. package/app/components/PlutoContentPreview.vue +35 -0
  22. package/app/composables/pluto-block-editor.ts +443 -0
  23. package/app/composables/pluto-content-edit-link.ts +19 -0
  24. package/app/composables/pluto-content-preview.ts +29 -0
  25. package/app/composables/pluto-content-runtime.ts +237 -0
  26. package/app/pages/_pluto/preview.vue +555 -0
  27. package/app/plugins/pluto-docs.ts +50 -0
  28. package/i18n/locales/en.json +253 -0
  29. package/i18n/locales/pt-BR.json +289 -0
  30. package/modules/pluto-docs/index.ts +123 -0
  31. package/modules/pluto-docs/manifest.ts +159 -0
  32. package/modules/pluto-docs/options.ts +224 -0
  33. package/nuxt.config.ts +36 -0
  34. package/package.json +55 -4
  35. package/server/handlers/navigation.get.ts +7 -0
  36. package/server/handlers/page.get.ts +7 -0
  37. package/server/handlers/surround.get.ts +7 -0
  38. package/server/plugins/pluto-docs.ts +25 -0
  39. package/server/utils/pluto-docs-runtime.ts +504 -0
  40. package/shared/types/docs.d.ts +40 -0
  41. package/shared/types/pluto-manifest.d.ts +6 -0
  42. package/shared/utils/blocks/editor.ts +357 -0
  43. package/shared/utils/blocks/history.ts +94 -0
  44. package/shared/utils/blocks/mdc-props.ts +321 -0
  45. package/shared/utils/blocks/operations.ts +692 -0
  46. package/shared/utils/blocks/parse.ts +444 -0
  47. package/shared/utils/blocks/presets.ts +383 -0
  48. package/shared/utils/blocks/preview-marks.ts +98 -0
  49. package/shared/utils/blocks/preview-protocol.ts +195 -0
  50. package/shared/utils/blocks/registry.ts +163 -0
  51. package/shared/utils/blocks/serialize.ts +107 -0
  52. package/shared/utils/blocks/tree.ts +85 -0
  53. package/shared/utils/blocks/types.ts +165 -0
  54. package/shared/utils/content-route.ts +40 -0
  55. package/shared/utils/docs-type.ts +290 -0
  56. package/shared/utils/mdc-snippets.ts +114 -0
  57. package/shared/utils/navigation-config.ts +19 -0
  58. package/shared/utils/runtime-content.ts +403 -0
  59. package/tailwind.lint.css +10 -0
  60. package/tsconfig.json +18 -0
  61. package/vitest.config.ts +7 -0
@@ -0,0 +1,253 @@
1
+ {
2
+ "docs": {
3
+ "capabilities": {
4
+ "write": "Create and edit pages",
5
+ "delete": "Delete pages"
6
+ },
7
+ "mdc": {
8
+ "insert": "Insert",
9
+ "placeholder": "Write the page in Markdown. Use the Insert menu for MDC components."
10
+ },
11
+ "snippets": {
12
+ "note": "Note",
13
+ "tip": "Tip",
14
+ "warning": "Warning",
15
+ "caution": "Caution",
16
+ "callout": "Callout",
17
+ "codeGroup": "Code group",
18
+ "tabs": "Tabs",
19
+ "steps": "Steps",
20
+ "cardGroup": "Card group",
21
+ "accordion": "Accordion",
22
+ "fieldGroup": "Field group",
23
+ "kbd": "Keyboard key",
24
+ "icon": "Icon",
25
+ "codeFile": "Code block with file name"
26
+ },
27
+ "blocks": {
28
+ "kinds": {
29
+ "text": "Text",
30
+ "code": "Code",
31
+ "raw": "Markdown",
32
+ "table": "Table",
33
+ "textDescription": "A title and a text.",
34
+ "tableDescription": "Rows and columns of text.",
35
+ "codeDescription": "A block of code with colors."
36
+ },
37
+ "groups": {
38
+ "text": "Text",
39
+ "callouts": "Callouts",
40
+ "layout": "Layout",
41
+ "code": "Code",
42
+ "landing": "Landing page",
43
+ "site": "Site"
44
+ },
45
+ "props": {
46
+ "icon": "Icon",
47
+ "to": "Link",
48
+ "target": "Open the link in",
49
+ "title": "Title",
50
+ "color": "Color",
51
+ "level": "Heading level",
52
+ "accordionType": "Open items",
53
+ "label": "Label",
54
+ "description": "Description",
55
+ "sync": "Sync key",
56
+ "name": "Name",
57
+ "openText": "Text to open",
58
+ "closeText": "Text to close",
59
+ "fieldType": "Type",
60
+ "required": "Required",
61
+ "value": "Value",
62
+ "headline": "Headline",
63
+ "orientation": "Layout direction",
64
+ "reverse": "Reverse the order",
65
+ "variant": "Style",
66
+ "size": "Size",
67
+ "spotlight": "Spotlight effect"
68
+ },
69
+ "slots": {
70
+ "default": "Content",
71
+ "headline": "Headline",
72
+ "title": "Title",
73
+ "description": "Text",
74
+ "links": "Buttons",
75
+ "features": "Features"
76
+ },
77
+ "note": {
78
+ "label": "Note",
79
+ "description": "A blue box for extra information."
80
+ },
81
+ "tip": {
82
+ "label": "Tip",
83
+ "description": "A green box with a useful tip."
84
+ },
85
+ "warning": {
86
+ "label": "Warning",
87
+ "description": "An orange box for something to be careful about."
88
+ },
89
+ "caution": {
90
+ "label": "Caution",
91
+ "description": "A red box for a risk or a breaking change."
92
+ },
93
+ "callout": {
94
+ "label": "Callout",
95
+ "description": "A box with an icon and a color that you choose."
96
+ },
97
+ "card": {
98
+ "label": "Card",
99
+ "description": "A box with a title, an icon and a link."
100
+ },
101
+ "card-group": {
102
+ "label": "Card group",
103
+ "description": "Cards side by side in a grid."
104
+ },
105
+ "steps": {
106
+ "label": "Steps",
107
+ "description": "A numbered list of steps with headings."
108
+ },
109
+ "accordion": {
110
+ "label": "Accordion",
111
+ "description": "Questions that open and close."
112
+ },
113
+ "accordion-item": {
114
+ "label": "Accordion item",
115
+ "description": "One question and its answer."
116
+ },
117
+ "tabs": {
118
+ "label": "Tabs",
119
+ "description": "Content in tabs, one tab at a time."
120
+ },
121
+ "tabs-item": {
122
+ "label": "Tab",
123
+ "description": "One tab of a group of tabs."
124
+ },
125
+ "collapsible": {
126
+ "label": "Collapsible",
127
+ "description": "Text that the reader opens with a click."
128
+ },
129
+ "field-group": {
130
+ "label": "Field group",
131
+ "description": "A list of fields, for an API or a setting."
132
+ },
133
+ "field": {
134
+ "label": "Field",
135
+ "description": "One field with a name, a type and a text."
136
+ },
137
+ "code-group": {
138
+ "label": "Code group",
139
+ "description": "Code blocks in tabs, for example one per package manager."
140
+ },
141
+ "code-collapse": {
142
+ "label": "Code that collapses",
143
+ "description": "A long code block that the reader opens."
144
+ },
145
+ "badge": {
146
+ "label": "Badge",
147
+ "description": "A small label inside a text."
148
+ },
149
+ "kbd": {
150
+ "label": "Keyboard key",
151
+ "description": "A keyboard key inside a text."
152
+ },
153
+ "icon": {
154
+ "label": "Icon",
155
+ "description": "An icon inside a text."
156
+ },
157
+ "u-page-hero": {
158
+ "label": "Hero",
159
+ "description": "The big title at the top of a landing page."
160
+ },
161
+ "u-page-section": {
162
+ "label": "Page section",
163
+ "description": "A section with a title, a text and features."
164
+ },
165
+ "u-page-feature": {
166
+ "label": "Feature",
167
+ "description": "An icon, a title and a short text."
168
+ },
169
+ "u-page-card": {
170
+ "label": "Page card",
171
+ "description": "A card for a landing page, with a title and a text."
172
+ },
173
+ "u-page-grid": {
174
+ "label": "Card grid",
175
+ "description": "A grid that holds page cards."
176
+ },
177
+ "u-page-c-t-a": {
178
+ "label": "Call to action",
179
+ "description": "A box that asks the reader to do something."
180
+ },
181
+ "u-button": {
182
+ "label": "Button",
183
+ "description": "A button that links to a page."
184
+ }
185
+ },
186
+ "preview": {
187
+ "title": "Page preview",
188
+ "parseError": "This block could not be read for the preview.",
189
+ "blockError": "This block could not show in the preview.",
190
+ "notShown": "This block does not show in the preview. It still works on the page."
191
+ },
192
+ "editor": {
193
+ "previewTitle": "Preview of the page",
194
+ "previewLoading": "Loading the preview…",
195
+ "previewFailedTitle": "The preview did not load",
196
+ "previewFailed": "The page preview did not answer. You can still edit the blocks. Check that the site has the preview page, then try again.",
197
+ "previewRetry": "Try again",
198
+ "addBlock": "Add a block",
199
+ "addBlockDescription": "Pick a block to add to the page.",
200
+ "searchBlocks": "Search blocks…",
201
+ "noBlocks": "No block found.",
202
+ "mode": "Editor",
203
+ "visual": "Visual",
204
+ "markdown": "Markdown",
205
+ "blocks": "Blocks",
206
+ "blockTab": "Block",
207
+ "undo": "Undo",
208
+ "redo": "Redo",
209
+ "add": "Add",
210
+ "moveUp": "Move up",
211
+ "moveDown": "Move down",
212
+ "addBelow": "Add a block below",
213
+ "duplicate": "Duplicate",
214
+ "delete": "Delete",
215
+ "edited": "Changed",
216
+ "expand": "Show the blocks inside {name}",
217
+ "collapse": "Hide the blocks inside {name}",
218
+ "blockActions": "Actions for {name}",
219
+ "addTo": "Add to {name}",
220
+ "emptyTitle": "This page has no blocks yet",
221
+ "emptyDescription": "Add a text, a note or a card to start.",
222
+ "blockPanel": "Block settings",
223
+ "nothingSelectedTitle": "No block selected",
224
+ "nothingSelected": "Click a block in the preview or in the list to change it.",
225
+ "selectParent": "Back to {name}",
226
+ "rawNote": "This is the Markdown of the block. A change here replaces the block.",
227
+ "rawLabel": "Markdown of the block",
228
+ "textLabel": "Text of the block",
229
+ "textPlaceholder": "Write here…",
230
+ "language": "Language",
231
+ "languageHelp": "For the colors, for example ts.",
232
+ "fileName": "File name",
233
+ "fileNameHelp": "Shows above the code.",
234
+ "code": "Code",
235
+ "tableNote": "A table is a Markdown block. Edit the cells in the box. A visual table editor is planned.",
236
+ "rawBlockNote": "This block has Markdown that the visual editor does not change. You can edit it as Markdown.",
237
+ "content": "Content",
238
+ "settings": "Settings",
239
+ "advanced": "Advanced",
240
+ "slotEmpty": "Nothing here yet.",
241
+ "editAsMarkdown": "Edit as Markdown",
242
+ "backToControls": "Back to the controls",
243
+ "applyMarkdown": "Apply",
244
+ "default": "Default",
245
+ "iconHelp": "An icon name, for example i-lucide-rocket.",
246
+ "linkPlaceholder": "/page or https://…",
247
+ "parseErrorTitle": "The visual editor cannot open this page",
248
+ "parseError": "The Markdown has a part that the visual editor cannot read. Use the Markdown editor to fix it.",
249
+ "switchToMarkdown": "Open the Markdown editor",
250
+ "addItem": "Add {name}"
251
+ }
252
+ }
253
+ }
@@ -0,0 +1,289 @@
1
+ {
2
+ "docs": {
3
+ "capabilities": {
4
+ "write": "Criar e editar páginas",
5
+ "delete": "Excluir páginas"
6
+ },
7
+ "mdc": {
8
+ "insert": "Inserir",
9
+ "placeholder": "Escreva a página em Markdown. Use o menu Inserir para os componentes MDC."
10
+ },
11
+ "snippets": {
12
+ "note": "Nota",
13
+ "tip": "Dica",
14
+ "warning": "Aviso",
15
+ "caution": "Atenção",
16
+ "callout": "Destaque",
17
+ "codeGroup": "Grupo de código",
18
+ "tabs": "Abas",
19
+ "steps": "Passos",
20
+ "cardGroup": "Grupo de cartões",
21
+ "accordion": "Acordeão",
22
+ "fieldGroup": "Grupo de campos",
23
+ "kbd": "Tecla",
24
+ "icon": "Ícone",
25
+ "codeFile": "Bloco de código com nome de arquivo"
26
+ },
27
+ "blocks": {
28
+ "kinds": {
29
+ "text": "Texto",
30
+ "code": "Código",
31
+ "raw": "Markdown",
32
+ "table": "Tabela",
33
+ "textDescription": "Um título e um texto.",
34
+ "tableDescription": "Linhas e colunas de texto.",
35
+ "codeDescription": "Um bloco de código com cores."
36
+ },
37
+ "groups": {
38
+ "text": "Texto",
39
+ "callouts": "Destaques",
40
+ "layout": "Layout",
41
+ "code": "Código",
42
+ "landing": "Página inicial",
43
+ "site": "Site"
44
+ },
45
+ "props": {
46
+ "icon": "Ícone",
47
+ "to": "Link",
48
+ "target": "Abrir o link em",
49
+ "title": "Título",
50
+ "color": "Cor",
51
+ "level": "Nível do título",
52
+ "accordionType": "Itens abertos",
53
+ "label": "Rótulo",
54
+ "description": "Descrição",
55
+ "sync": "Chave de sincronia",
56
+ "name": "Nome",
57
+ "openText": "Texto para abrir",
58
+ "closeText": "Texto para fechar",
59
+ "fieldType": "Tipo",
60
+ "required": "Obrigatório",
61
+ "value": "Valor",
62
+ "headline": "Sobretítulo",
63
+ "orientation": "Direção do layout",
64
+ "reverse": "Inverter a ordem",
65
+ "variant": "Estilo",
66
+ "size": "Tamanho",
67
+ "spotlight": "Efeito de destaque"
68
+ },
69
+ "slots": {
70
+ "default": "Conteúdo",
71
+ "headline": "Sobretítulo",
72
+ "title": "Título",
73
+ "description": "Texto",
74
+ "links": "Botões",
75
+ "features": "Recursos"
76
+ },
77
+ "note": {
78
+ "label": "Nota",
79
+ "description": "Uma caixa azul para informação extra."
80
+ },
81
+ "tip": {
82
+ "label": "Dica",
83
+ "description": "Uma caixa verde com uma dica útil."
84
+ },
85
+ "warning": {
86
+ "label": "Aviso",
87
+ "description": "Uma caixa laranja para algo que pede cuidado."
88
+ },
89
+ "caution": {
90
+ "label": "Atenção",
91
+ "description": "Uma caixa vermelha para um risco ou uma mudança que quebra."
92
+ },
93
+ "callout": {
94
+ "label": "Destaque",
95
+ "description": "Uma caixa com ícone e cor à sua escolha."
96
+ },
97
+ "card": {
98
+ "label": "Cartão",
99
+ "description": "Uma caixa com título, ícone e link."
100
+ },
101
+ "card-group": {
102
+ "label": "Grupo de cartões",
103
+ "description": "Cartões lado a lado em uma grade."
104
+ },
105
+ "steps": {
106
+ "label": "Passos",
107
+ "description": "Uma lista numerada de passos com títulos."
108
+ },
109
+ "accordion": {
110
+ "label": "Acordeão",
111
+ "description": "Perguntas que abrem e fecham."
112
+ },
113
+ "accordion-item": {
114
+ "label": "Item do acordeão",
115
+ "description": "Uma pergunta e a resposta."
116
+ },
117
+ "tabs": {
118
+ "label": "Abas",
119
+ "description": "Conteúdo em abas, uma aba por vez."
120
+ },
121
+ "tabs-item": {
122
+ "label": "Aba",
123
+ "description": "Uma aba de um grupo de abas."
124
+ },
125
+ "collapsible": {
126
+ "label": "Conteúdo recolhível",
127
+ "description": "Texto que o leitor abre com um clique."
128
+ },
129
+ "field-group": {
130
+ "label": "Grupo de campos",
131
+ "description": "Uma lista de campos, para uma API ou uma configuração."
132
+ },
133
+ "field": {
134
+ "label": "Campo",
135
+ "description": "Um campo com nome, tipo e texto."
136
+ },
137
+ "code-group": {
138
+ "label": "Grupo de código",
139
+ "description": "Blocos de código em abas, por exemplo um por gerenciador de pacotes."
140
+ },
141
+ "code-collapse": {
142
+ "label": "Código recolhível",
143
+ "description": "Um bloco de código longo que o leitor abre."
144
+ },
145
+ "badge": {
146
+ "label": "Selo",
147
+ "description": "Um rótulo pequeno dentro de um texto."
148
+ },
149
+ "kbd": {
150
+ "label": "Tecla",
151
+ "description": "Uma tecla do teclado dentro de um texto."
152
+ },
153
+ "icon": {
154
+ "label": "Ícone",
155
+ "description": "Um ícone dentro de um texto."
156
+ },
157
+ "u-page-hero": {
158
+ "label": "Destaque de abertura",
159
+ "description": "O título grande no topo de uma página inicial."
160
+ },
161
+ "u-page-section": {
162
+ "label": "Seção da página",
163
+ "description": "Uma seção com título, texto e recursos."
164
+ },
165
+ "u-page-feature": {
166
+ "label": "Recurso",
167
+ "description": "Um ícone, um título e um texto curto."
168
+ },
169
+ "u-page-card": {
170
+ "label": "Cartão da página",
171
+ "description": "Um cartão para a página inicial, com título e texto."
172
+ },
173
+ "u-page-grid": {
174
+ "label": "Grade de cartões",
175
+ "description": "Uma grade que reúne cartões da página."
176
+ },
177
+ "u-page-c-t-a": {
178
+ "label": "Chamada para ação",
179
+ "description": "Uma caixa que pede uma ação ao leitor."
180
+ },
181
+ "u-button": {
182
+ "label": "Botão",
183
+ "description": "Um botão que leva a uma página."
184
+ }
185
+ },
186
+ "preview": {
187
+ "title": "Prévia da página",
188
+ "parseError": "Não foi possível ler este bloco para a prévia.",
189
+ "blockError": "Este bloco não pôde aparecer na prévia.",
190
+ "notShown": "Este bloco não aparece na prévia. Ele continua funcionando na página."
191
+ },
192
+ "editor": {
193
+ "previewTitle": "Prévia da página",
194
+ "previewLoading": "Carregando a prévia…",
195
+ "previewFailedTitle": "A prévia não carregou",
196
+ "previewFailed": "A prévia da página não respondeu. Você ainda pode editar os blocos. Veja se o site tem a página de prévia e tente de novo.",
197
+ "previewRetry": "Tentar de novo",
198
+ "addBlock": "Adicionar um bloco",
199
+ "addBlockDescription": "Escolha um bloco para adicionar à página.",
200
+ "searchBlocks": "Buscar blocos…",
201
+ "noBlocks": "Nenhum bloco encontrado.",
202
+ "mode": "Editor",
203
+ "visual": "Visual",
204
+ "markdown": "Markdown",
205
+ "blocks": "Blocos",
206
+ "blockTab": "Bloco",
207
+ "undo": "Desfazer",
208
+ "redo": "Refazer",
209
+ "add": "Adicionar",
210
+ "moveUp": "Mover para cima",
211
+ "moveDown": "Mover para baixo",
212
+ "addBelow": "Adicionar um bloco abaixo",
213
+ "duplicate": "Duplicar",
214
+ "delete": "Excluir",
215
+ "edited": "Alterado",
216
+ "expand": "Mostrar os blocos dentro de {name}",
217
+ "collapse": "Ocultar os blocos dentro de {name}",
218
+ "blockActions": "Ações de {name}",
219
+ "addTo": "Adicionar em {name}",
220
+ "emptyTitle": "Esta página ainda não tem blocos",
221
+ "emptyDescription": "Adicione um texto, uma nota ou um cartão para começar.",
222
+ "blockPanel": "Configurações do bloco",
223
+ "nothingSelectedTitle": "Nenhum bloco selecionado",
224
+ "nothingSelected": "Clique em um bloco na prévia ou na lista para alterá-lo.",
225
+ "selectParent": "Voltar para {name}",
226
+ "rawNote": "Este é o Markdown do bloco. Uma alteração aqui substitui o bloco.",
227
+ "rawLabel": "Markdown do bloco",
228
+ "textLabel": "Texto do bloco",
229
+ "textPlaceholder": "Escreva aqui…",
230
+ "language": "Linguagem",
231
+ "languageHelp": "Para as cores, por exemplo ts.",
232
+ "fileName": "Nome do arquivo",
233
+ "fileNameHelp": "Aparece acima do código.",
234
+ "code": "Código",
235
+ "tableNote": "Uma tabela é um bloco de Markdown. Edite as células na caixa. Um editor visual de tabela está nos planos.",
236
+ "rawBlockNote": "Este bloco tem Markdown que o editor visual não altera. Você pode editá-lo como Markdown.",
237
+ "content": "Conteúdo",
238
+ "settings": "Configurações",
239
+ "advanced": "Avançado",
240
+ "slotEmpty": "Nada aqui ainda.",
241
+ "editAsMarkdown": "Editar como Markdown",
242
+ "backToControls": "Voltar para os controles",
243
+ "applyMarkdown": "Aplicar",
244
+ "default": "Padrão",
245
+ "iconHelp": "O nome de um ícone, por exemplo i-lucide-rocket.",
246
+ "linkPlaceholder": "/pagina ou https://…",
247
+ "parseErrorTitle": "O editor visual não consegue abrir esta página",
248
+ "parseError": "O Markdown tem uma parte que o editor visual não consegue ler. Use o editor de Markdown para corrigir.",
249
+ "switchToMarkdown": "Abrir o editor de Markdown",
250
+ "addItem": "Adicionar {name}"
251
+ }
252
+ },
253
+ "content": {
254
+ "doc": {
255
+ "label": "Página",
256
+ "labelPlural": "Páginas",
257
+ "fields": {
258
+ "title": {
259
+ "label": "Título"
260
+ },
261
+ "slug": {
262
+ "label": "Caminho",
263
+ "description": "O caminho do arquivo sem .md, por exemplo 2.core/03.content-model. Um caminho novo move o arquivo."
264
+ },
265
+ "description": {
266
+ "label": "Descrição"
267
+ },
268
+ "body": {
269
+ "label": "Conteúdo"
270
+ },
271
+ "navigationTitle": {
272
+ "label": "Título na navegação"
273
+ },
274
+ "navigationIcon": {
275
+ "label": "Ícone na navegação"
276
+ },
277
+ "navigationHidden": {
278
+ "label": "Ocultar da navegação"
279
+ },
280
+ "seoTitle": {
281
+ "label": "Título para SEO"
282
+ },
283
+ "seoDescription": {
284
+ "label": "Descrição para SEO"
285
+ }
286
+ }
287
+ }
288
+ }
289
+ }
@@ -0,0 +1,123 @@
1
+ import type { PlutoDocsOptions } from './options'
2
+ import { join } from 'node:path'
3
+ import {
4
+ addServerHandler,
5
+ createResolver,
6
+ defineNuxtModule,
7
+ useLogger,
8
+ } from '@nuxt/kit'
9
+ import { PREVIEW_PATH } from '../../shared/utils/blocks/preview-protocol'
10
+ import { buildContentManifest, manifestModuleSource } from './manifest'
11
+ import { resolvePlutoDocsOptions } from './options'
12
+
13
+ export type { PlutoDocsOptions, PlutoDocsTypeOptions } from './options'
14
+
15
+ /** The routes of the run-time render mode, on the public API of Pluto. The composables of the layer call them. */
16
+ export const RUNTIME_ROUTE_BASE = '/api/pluto/v1/docs'
17
+
18
+ /**
19
+ * Registers the content types of the docs layer. It is OFF when the
20
+ * app turns the standalone CMS off (`plutoGithub.enabled: false`). The default
21
+ * `'auto'` and `true` both count as on. The module only checks the options
22
+ * and copies them to the public runtime config. The client plugin
23
+ * (`app/plugins/pluto-docs.ts`) and the Nitro plugin
24
+ * (`server/plugins/pluto-docs.ts`) of the layer build the types from
25
+ * that config. See the docs-setup skill.
26
+ */
27
+ export default defineNuxtModule<PlutoDocsOptions>({
28
+ meta: { name: 'pluto-docs', configKey: 'plutoDocs' },
29
+ defaults: { types: [] },
30
+ setup(options, nuxt) {
31
+ const logger = useLogger('pluto-docs')
32
+ const cms = (
33
+ nuxt.options as { plutoGithub?: { enabled?: boolean | 'auto' } }
34
+ ).plutoGithub
35
+
36
+ const { enabled, render, runtimeMaxAge, types, errors } =
37
+ resolvePlutoDocsOptions(options, {
38
+ cmsEnabled: cms?.enabled !== false,
39
+ })
40
+
41
+ for (const error of errors) {
42
+ logger.error(error)
43
+ }
44
+ if (!enabled) {
45
+ return
46
+ }
47
+
48
+ // The plugins read this key. It is absent when the layer is off.
49
+ nuxt.options.runtimeConfig.public.plutoDocs = {
50
+ types,
51
+ render,
52
+ runtimeMaxAge,
53
+ }
54
+
55
+ if (render === 'hybrid' || render === 'runtime') {
56
+ const { resolve } = createResolver(import.meta.url)
57
+ // The toc rules of Nuxt Content, so the run-time parser makes the same table of contents.
58
+ const content = (
59
+ nuxt.options as {
60
+ content?: {
61
+ build?: {
62
+ markdown?: { toc?: { depth?: number; searchDepth?: number } }
63
+ }
64
+ }
65
+ }
66
+ ).content
67
+ nuxt.options.runtimeConfig.plutoDocs = {
68
+ toc: content?.build?.markdown?.toc,
69
+ } as never
70
+ for (const name of ['page', 'navigation', 'surround']) {
71
+ addServerHandler({
72
+ route: `${RUNTIME_ROUTE_BASE}/:type/${name}`,
73
+ method: 'get',
74
+ handler: resolve(`../../server/handlers/${name}.get`),
75
+ })
76
+ }
77
+ }
78
+
79
+ // The build manifest: content id → version, for each type. The server compares it with the
80
+ // store (hybrid). It is a virtual module, so it is part of the server bundle and matches the
81
+ // pages that Nuxt Content builds in the same run. It is made once, when the bundler first asks.
82
+ const makeManifest = () => {
83
+ // The CMS module sets the storage and the content dir. They are known here, after all modules ran.
84
+ const cmsConfig = nuxt.options.runtimeConfig.public.plutoGithub as
85
+ { storage?: string; dir?: string } | undefined
86
+ return buildContentManifest(types, {
87
+ contentDir: join(nuxt.options.rootDir, cmsConfig?.dir ?? 'content'),
88
+ storage: cmsConfig?.storage ?? 'github',
89
+ })
90
+ }
91
+
92
+ const manifestId = '#pluto-docs/manifest'
93
+ let manifestSource: string | undefined
94
+ nuxt.hook('nitro:config', (nitroConfig) => {
95
+ nitroConfig.virtual ||= {}
96
+ nitroConfig.virtual[manifestId] = () => {
97
+ if (manifestSource === undefined) {
98
+ manifestSource = manifestModuleSource(
99
+ render === 'hybrid' ? makeManifest() : {}
100
+ )
101
+ }
102
+ return manifestSource
103
+ }
104
+ })
105
+
106
+ // The preview page of the visual editor is a client-only page that no search engine should list.
107
+ // It is not prerendered, and the response says noindex. An app rule for the same path wins.
108
+ nuxt.options.routeRules = {
109
+ [PREVIEW_PATH]: {
110
+ ssr: false,
111
+ prerender: false,
112
+ headers: { 'x-robots-tag': 'noindex, nofollow' },
113
+ },
114
+ ...nuxt.options.routeRules,
115
+ }
116
+
117
+ if (!nuxt.options._prepare) {
118
+ logger.info(
119
+ `Docs layer on. Types: ${types.map((type) => type.name).join(', ') || 'none'}. Render: ${render}.`
120
+ )
121
+ }
122
+ },
123
+ })