mechanica 2.0.0-alpha.9 → 2.0.1

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 (281) hide show
  1. package/README.md +303 -48
  2. package/bin/mechanica.js +7 -3
  3. package/dist/chunks/{ImagePickerDialog-CY-mn2UP.js → ImagePickerDialog-6skSAzJh.js} +44 -0
  4. package/dist/chunks/{create-mechanica-CnBFiyfJ.js → create-mechanica-CYaKXuQd.js} +243 -25
  5. package/dist/chunks/{editor-9tquvDcS.js → editor-B0r4EOIM.js} +84 -9
  6. package/dist/chunks/{page-assets-BCUAuOlo.js → page-assets-CM_MCuoa.js} +20 -2
  7. package/dist/chunks/{preview-G5_AFI5P.js → preview-CbUxh2gL.js} +4 -2
  8. package/dist/cli.js +49 -4
  9. package/dist/composer.js +603 -180
  10. package/dist/editor.css +344 -100
  11. package/dist/editor.js +358 -92
  12. package/dist/index.js +48 -85
  13. package/dist/plugin.js +105 -22
  14. package/dist/types/cli/export.d.ts +6 -0
  15. package/dist/types/cli/page-assets.d.ts +13 -0
  16. package/dist/types/core/create-mechanica.d.ts +3 -1
  17. package/dist/types/core/data-registry.d.ts +2 -0
  18. package/dist/types/core/define-block.d.ts +17 -0
  19. package/dist/types/core/define-data.d.ts +8 -0
  20. package/dist/types/core/define-mechanica-app.d.ts +7 -0
  21. package/dist/types/core/index.d.ts +1 -0
  22. package/dist/types/core/layout.d.ts +27 -0
  23. package/dist/types/core/render-blocks.d.ts +2 -1
  24. package/dist/types/core/state.d.ts +5 -0
  25. package/dist/types/editor/composer/lib/canvas.d.ts +15 -2
  26. package/dist/types/editor/composer/lib/composer-store.d.ts +18 -2
  27. package/dist/types/editor/lib/block-meta.d.ts +16 -0
  28. package/dist/types/editor/lib/bridge.d.ts +8 -0
  29. package/dist/types/editor/lib/data-meta.d.ts +9 -0
  30. package/dist/types/editor/lib/page-list.d.ts +2 -0
  31. package/dist/types/editor/lib/page-setup.d.ts +15 -0
  32. package/dist/types/editor/lib/store.d.ts +9 -0
  33. package/dist/types/editor/lib/types.d.ts +2 -0
  34. package/dist/types/editor/lib/use-block-frames.d.ts +9 -0
  35. package/dist/types/elements/emit-css.d.ts +2 -1
  36. package/dist/types/elements/style-vars.d.ts +10 -8
  37. package/dist/types/vite/dev/pages-store.d.ts +6 -0
  38. package/dist/types/vite/plugin.d.ts +21 -11
  39. package/dist/widgets.js +1 -1
  40. package/package.json +3 -9
  41. package/src/cli/args.ts +0 -33
  42. package/src/cli/build.ts +0 -40
  43. package/src/cli/cli.ts +0 -76
  44. package/src/cli/export.ts +0 -787
  45. package/src/cli/headless.ts +0 -361
  46. package/src/cli/images.ts +0 -67
  47. package/src/cli/page-assets.ts +0 -114
  48. package/src/cli/push.ts +0 -64
  49. package/src/cli/shot.ts +0 -245
  50. package/src/cli/thumbs.ts +0 -252
  51. package/src/compiler/compile-block.ts +0 -212
  52. package/src/core/composed.ts +0 -45
  53. package/src/core/composer-components.ts +0 -31
  54. package/src/core/content.ts +0 -24
  55. package/src/core/create-mechanica.ts +0 -126
  56. package/src/core/data-registry.ts +0 -37
  57. package/src/core/define-block.ts +0 -51
  58. package/src/core/define-data.ts +0 -65
  59. package/src/core/define-mechanica-app.ts +0 -56
  60. package/src/core/field-types.ts +0 -37
  61. package/src/core/image.ts +0 -107
  62. package/src/core/index.ts +0 -57
  63. package/src/core/link.ts +0 -87
  64. package/src/core/load-blocks.ts +0 -59
  65. package/src/core/preview.ts +0 -230
  66. package/src/core/query.ts +0 -39
  67. package/src/core/render-blocks.ts +0 -66
  68. package/src/core/router.ts +0 -137
  69. package/src/core/state.ts +0 -37
  70. package/src/core/use-fetch.ts +0 -14
  71. package/src/core/use-locale.ts +0 -40
  72. package/src/core/use-page-data.ts +0 -10
  73. package/src/core/use-pages.ts +0 -39
  74. package/src/core/use-pagination.ts +0 -75
  75. package/src/editor/EditorApp.vue +0 -396
  76. package/src/editor/components/BlockFrame.vue +0 -18
  77. package/src/editor/components/BlockPalette.vue +0 -338
  78. package/src/editor/components/BlockPreview.vue +0 -127
  79. package/src/editor/components/BlockSettings.vue +0 -63
  80. package/src/editor/components/DataSettings.vue +0 -240
  81. package/src/editor/components/HierarchyTree.vue +0 -568
  82. package/src/editor/components/IconField.vue +0 -201
  83. package/src/editor/components/LocaleSwitcher.vue +0 -239
  84. package/src/editor/components/PageBar.vue +0 -109
  85. package/src/editor/components/PanelToggle.vue +0 -161
  86. package/src/editor/components/TypographyInput.vue +0 -131
  87. package/src/editor/components/VContextMenu.vue +0 -187
  88. package/src/editor/components/VIcon.vue +0 -20
  89. package/src/editor/components/VPopover.vue +0 -135
  90. package/src/editor/components/VSegmented.vue +0 -93
  91. package/src/editor/components/VSelect.vue +0 -222
  92. package/src/editor/composer/ComposerApp.vue +0 -220
  93. package/src/editor/composer/components/AlignGrid.vue +0 -74
  94. package/src/editor/composer/components/AnchorGrid.vue +0 -36
  95. package/src/editor/composer/components/BindField.vue +0 -42
  96. package/src/editor/composer/components/BoxSideInput.vue +0 -141
  97. package/src/editor/composer/components/CanvasOverlay.vue +0 -488
  98. package/src/editor/composer/components/ClassStylePreview.vue +0 -82
  99. package/src/editor/composer/components/ColorField.vue +0 -36
  100. package/src/editor/composer/components/ComponentFields.vue +0 -39
  101. package/src/editor/composer/components/ComposerCanvas.vue +0 -376
  102. package/src/editor/composer/components/ComposerInspector.vue +0 -506
  103. package/src/editor/composer/components/ComposerLayers.vue +0 -28
  104. package/src/editor/composer/components/ComposerSettings.vue +0 -52
  105. package/src/editor/composer/components/InsertToolbar.vue +0 -96
  106. package/src/editor/composer/components/LayerNode.vue +0 -69
  107. package/src/editor/composer/components/MultiSelectPanel.vue +0 -57
  108. package/src/editor/composer/components/NumInput.vue +0 -121
  109. package/src/editor/composer/components/OverrideLabel.vue +0 -21
  110. package/src/editor/composer/components/PropToggle.vue +0 -42
  111. package/src/editor/composer/components/PropsSection.vue +0 -51
  112. package/src/editor/composer/components/SegControl.vue +0 -30
  113. package/src/editor/composer/components/SizeInput.vue +0 -62
  114. package/src/editor/composer/composer.ts +0 -220
  115. package/src/editor/composer/lib/abs.ts +0 -78
  116. package/src/editor/composer/lib/canvas-drop-target.ts +0 -103
  117. package/src/editor/composer/lib/canvas-drop.ts +0 -78
  118. package/src/editor/composer/lib/canvas-overlay.ts +0 -168
  119. package/src/editor/composer/lib/canvas-view.ts +0 -75
  120. package/src/editor/composer/lib/canvas.ts +0 -65
  121. package/src/editor/composer/lib/class-css.ts +0 -103
  122. package/src/editor/composer/lib/class-groups.ts +0 -86
  123. package/src/editor/composer/lib/clipboard.ts +0 -25
  124. package/src/editor/composer/lib/composer-history.ts +0 -81
  125. package/src/editor/composer/lib/composer-store.ts +0 -546
  126. package/src/editor/composer/lib/design-tokens.ts +0 -46
  127. package/src/editor/composer/lib/elements-meta.ts +0 -123
  128. package/src/editor/composer/lib/inspector-props.ts +0 -76
  129. package/src/editor/composer/lib/keys.ts +0 -14
  130. package/src/editor/composer/lib/marquee.ts +0 -26
  131. package/src/editor/composer/lib/normalize-template.ts +0 -51
  132. package/src/editor/composer/lib/padding.ts +0 -71
  133. package/src/editor/composer/lib/scrub.ts +0 -9
  134. package/src/editor/composer/lib/use-insert-dnd.ts +0 -131
  135. package/src/editor/composer/lib/use-layer-dnd.ts +0 -99
  136. package/src/editor/composer/styles/composer.scss +0 -1657
  137. package/src/editor/dialogs/ConfirmDialog.vue +0 -48
  138. package/src/editor/dialogs/DataDialog.vue +0 -16
  139. package/src/editor/dialogs/ImageCropDialog.vue +0 -360
  140. package/src/editor/dialogs/ImagePickerDialog.vue +0 -219
  141. package/src/editor/dialogs/PageFormDialog.vue +0 -127
  142. package/src/editor/dialogs/PagesDialog.vue +0 -885
  143. package/src/editor/dialogs/QuickSwitcher.vue +0 -208
  144. package/src/editor/dialogs/RichTextDialog.vue +0 -38
  145. package/src/editor/editor.ts +0 -247
  146. package/src/editor/fields/FieldControl.vue +0 -31
  147. package/src/editor/fields/builtin.ts +0 -29
  148. package/src/editor/fields/editors/BooleanField.vue +0 -12
  149. package/src/editor/fields/editors/ColorField.vue +0 -12
  150. package/src/editor/fields/editors/EnumField.vue +0 -33
  151. package/src/editor/fields/editors/ImageField.vue +0 -178
  152. package/src/editor/fields/editors/MultiselectField.vue +0 -35
  153. package/src/editor/fields/editors/NumberField.vue +0 -12
  154. package/src/editor/fields/editors/RichTextField.vue +0 -59
  155. package/src/editor/fields/editors/SmartLinkField.vue +0 -457
  156. package/src/editor/fields/editors/StringField.vue +0 -13
  157. package/src/editor/fields/editors/TextField.vue +0 -15
  158. package/src/editor/fields/registry.ts +0 -52
  159. package/src/editor/fields/richtext/RichCodeWidget.vue +0 -120
  160. package/src/editor/fields/richtext/RichImageWidget.vue +0 -129
  161. package/src/editor/fields/richtext/RichTableWidget.vue +0 -96
  162. package/src/editor/fields/richtext/RichTextEditor.vue +0 -792
  163. package/src/editor/fields/richtext/WidgetBoundary.vue +0 -45
  164. package/src/editor/fields/richtext/config.ts +0 -83
  165. package/src/editor/fields/richtext/keys.ts +0 -12
  166. package/src/editor/fields/richtext/widgets.ts +0 -117
  167. package/src/editor/icons/align-center.svg +0 -1
  168. package/src/editor/icons/align-left.svg +0 -1
  169. package/src/editor/icons/align-right.svg +0 -1
  170. package/src/editor/icons/arrow-down.svg +0 -1
  171. package/src/editor/icons/arrow-up.svg +0 -1
  172. package/src/editor/icons/bold.svg +0 -1
  173. package/src/editor/icons/bolt.svg +0 -1
  174. package/src/editor/icons/book.svg +0 -1
  175. package/src/editor/icons/button.svg +0 -1
  176. package/src/editor/icons/check.svg +0 -1
  177. package/src/editor/icons/chevron-down.svg +0 -1
  178. package/src/editor/icons/close.svg +0 -1
  179. package/src/editor/icons/code.svg +0 -1
  180. package/src/editor/icons/column.svg +0 -1
  181. package/src/editor/icons/component.svg +0 -1
  182. package/src/editor/icons/copy.svg +0 -1
  183. package/src/editor/icons/corner.svg +0 -1
  184. package/src/editor/icons/desktop.svg +0 -1
  185. package/src/editor/icons/ellipsis.svg +0 -1
  186. package/src/editor/icons/fill.svg +0 -1
  187. package/src/editor/icons/folder.svg +0 -1
  188. package/src/editor/icons/frame.svg +0 -1
  189. package/src/editor/icons/gap.svg +0 -1
  190. package/src/editor/icons/globe.svg +0 -1
  191. package/src/editor/icons/grip.svg +0 -1
  192. package/src/editor/icons/height.svg +0 -1
  193. package/src/editor/icons/image.svg +0 -1
  194. package/src/editor/icons/info.svg +0 -1
  195. package/src/editor/icons/italic.svg +0 -1
  196. package/src/editor/icons/limits.svg +0 -1
  197. package/src/editor/icons/margin.svg +0 -1
  198. package/src/editor/icons/menu.svg +0 -1
  199. package/src/editor/icons/mobile.svg +0 -1
  200. package/src/editor/icons/named-slots.svg +0 -1
  201. package/src/editor/icons/pencil.svg +0 -1
  202. package/src/editor/icons/plus.svg +0 -1
  203. package/src/editor/icons/position.svg +0 -1
  204. package/src/editor/icons/redo.svg +0 -1
  205. package/src/editor/icons/row.svg +0 -1
  206. package/src/editor/icons/search.svg +0 -1
  207. package/src/editor/icons/sides.svg +0 -1
  208. package/src/editor/icons/sliders.svg +0 -1
  209. package/src/editor/icons/slot.svg +0 -1
  210. package/src/editor/icons/strikethrough.svg +0 -1
  211. package/src/editor/icons/table.svg +0 -4
  212. package/src/editor/icons/tablet.svg +0 -1
  213. package/src/editor/icons/text.svg +0 -1
  214. package/src/editor/icons/trash.svg +0 -1
  215. package/src/editor/icons/underline.svg +0 -1
  216. package/src/editor/icons/undo.svg +0 -1
  217. package/src/editor/icons/upload.svg +0 -1
  218. package/src/editor/icons/width.svg +0 -1
  219. package/src/editor/icons/wrap.svg +0 -1
  220. package/src/editor/lib/block-meta.ts +0 -98
  221. package/src/editor/lib/block-preview.ts +0 -84
  222. package/src/editor/lib/bridge.ts +0 -90
  223. package/src/editor/lib/content-tree.ts +0 -186
  224. package/src/editor/lib/context-menu.ts +0 -52
  225. package/src/editor/lib/drag-controller.ts +0 -217
  226. package/src/editor/lib/history.ts +0 -91
  227. package/src/editor/lib/image-crop.ts +0 -174
  228. package/src/editor/lib/image-size.ts +0 -154
  229. package/src/editor/lib/keyboard.ts +0 -29
  230. package/src/editor/lib/navigation.ts +0 -31
  231. package/src/editor/lib/page-list.ts +0 -89
  232. package/src/editor/lib/panel-dock.ts +0 -97
  233. package/src/editor/lib/recents.ts +0 -31
  234. package/src/editor/lib/save-queue.ts +0 -156
  235. package/src/editor/lib/shortcuts.ts +0 -54
  236. package/src/editor/lib/store.ts +0 -331
  237. package/src/editor/lib/tree-ui.ts +0 -33
  238. package/src/editor/lib/types.ts +0 -24
  239. package/src/editor/lib/typography.ts +0 -80
  240. package/src/editor/lib/use-block-frames.ts +0 -125
  241. package/src/editor/props-panel/ArrayField.vue +0 -106
  242. package/src/editor/props-panel/SchemaField.vue +0 -45
  243. package/src/editor/props-panel/SchemaForm.vue +0 -120
  244. package/src/editor/props-panel/VCollapse.vue +0 -28
  245. package/src/editor/props-panel/humanize.ts +0 -8
  246. package/src/editor/props-panel/use-reorder.ts +0 -90
  247. package/src/editor/styles/_variables.scss +0 -54
  248. package/src/editor/styles/editor.scss +0 -915
  249. package/src/editor/ui/VDialog.vue +0 -95
  250. package/src/editor/ui/VDialogHost.vue +0 -116
  251. package/src/editor/ui/dialog.ts +0 -47
  252. package/src/editor/widget-api.ts +0 -49
  253. package/src/elements/elements.scss +0 -69
  254. package/src/elements/emit-css.ts +0 -153
  255. package/src/elements/index.ts +0 -135
  256. package/src/elements/style-vars.ts +0 -230
  257. package/src/index.ts +0 -1
  258. package/src/svg-plugin.ts +0 -108
  259. package/src/virtual-modules.d.ts +0 -23
  260. package/src/vite/collect-blocks.ts +0 -99
  261. package/src/vite/collect-components.ts +0 -58
  262. package/src/vite/collect-composed.ts +0 -49
  263. package/src/vite/collect-widgets.ts +0 -54
  264. package/src/vite/dev/assets-store.ts +0 -217
  265. package/src/vite/dev/composed-store.ts +0 -99
  266. package/src/vite/dev/composer.ts +0 -46
  267. package/src/vite/dev/data-store.ts +0 -147
  268. package/src/vite/dev/fs-utils.ts +0 -41
  269. package/src/vite/dev/image-preview.ts +0 -102
  270. package/src/vite/dev/middleware.ts +0 -395
  271. package/src/vite/dev/page-state.ts +0 -167
  272. package/src/vite/dev/pages-store.ts +0 -583
  273. package/src/vite/dev/preview.ts +0 -69
  274. package/src/vite/dev/query-dev.ts +0 -33
  275. package/src/vite/entries.ts +0 -200
  276. package/src/vite/generate-pages.ts +0 -51
  277. package/src/vite/index.ts +0 -17
  278. package/src/vite/plugin.ts +0 -697
  279. package/src/vite/read-breakpoints.ts +0 -47
  280. package/src/vite/rich-text-codec.ts +0 -48
  281. package/src/vue-shim.d.ts +0 -14
package/README.md CHANGED
@@ -1,28 +1,61 @@
1
1
  # mechanica
2
2
 
3
- Build Vue 3 websites with a visual block editor. Authors write Vue SFC **blocks**, a visual editor arranges them into pages, and the result renders server-side and ships statically or via a backend.
3
+ Build Vue 3 websites with a visual block editor. You write **blocks** — ordinary Vue single-file components with a typed props schema — and arrange them into **pages** in an editor that runs on top of your live site. Pages are stored as readable Markdown files in your project, and `mechanica export` renders every page to static HTML.
4
4
 
5
- Built for **Vite 8 / Vue 3.5 / Bun**.
5
+ - **Blocks are just Vue.** One compile-time macro, `defineBlock`, declares a block's props; everything else is plain Vue 3.
6
+ - **The editor is your site.** In development the editor overlays the real page: add blocks from a palette, edit props in forms, drag to reorder, undo/redo, manage pages.
7
+ - **Pages are files.** `.mech/pages/about.page.md` is Markdown with YAML props — editable in the editor, by hand, or by an AI assistant, with clean git diffs.
8
+ - **Static output, SEO included.** Per-page HTML and code splitting, sitemap, canonical URLs, hreflang and JSON-LD.
6
9
 
7
- ## Install
10
+ Built on Vite 8 and Vue 3.5. Runs on Node.js 20.19+ or 22.12+ (npm, pnpm, yarn) or [Bun](https://bun.sh).
8
11
 
9
- Works with plain Node ≥ 20.19 (npm/pnpm/yarn) or [Bun](https://bun.sh) — Bun installs run the TypeScript source directly, Node uses the compiled build. The fastest start is the scaffolder:
12
+ ## Quick start
10
13
 
11
14
  ```bash
12
15
  npm create mechanica@latest my-site
13
- # or: bun create mechanica my-site
16
+ cd my-site
17
+ npm install
18
+ npm run dev
14
19
  ```
15
20
 
16
- Or add it to an existing Vite + Vue project:
21
+ Open the URL Vite prints (`http://127.0.0.1:5173/`). The starter page appears with the editor on top of it: click a block to edit its props, add blocks from the palette, and open the page browser to create more pages. Every edit is saved into `.mech/` as you go.
22
+
23
+ When you're ready to ship:
17
24
 
18
25
  ```bash
19
- npm install mechanica
26
+ npm run export
20
27
  ```
21
28
 
22
- ## Quick start
29
+ `export/` now holds a static site: an `index.html` per page, hashed JS and CSS in `assets/`, your uploads in `media/`. It's built to be served from the root of a domain; upload it to any static host, or look at it locally with `npx vite preview --outDir export`.
30
+
31
+ Prefer Bun? `bun create mechanica my-site`, then `bun install` and `bun run dev`.
32
+
33
+ ## A Mechanica project
34
+
35
+ ```text
36
+ index.html the document shell; {{ }} placeholders fill <head> per page
37
+ vite.config.ts plugins: [mechanica(), vue()]
38
+ src/main.ts export default defineMechanicaApp({ root: App })
39
+ src/App.vue site-wide chrome around <Content />
40
+ src/blocks/*.vue blocks: every file appears in the editor's palette
41
+ src/data/*.ts shared data entries (defineData)
42
+ src/composer.ts optional: your design system for the Block Composer
43
+ .mech/pages/ the pages, one .page.md file each
44
+ .mech/assets/ uploaded files
45
+ .mech/data.json site-wide data values
46
+ ```
47
+
48
+ `.mech/` is content — commit it. Scaffolded projects also include an `AGENTS.md` that teaches AI coding assistants (Claude Code, Codex and others) to author blocks and pages in this format.
49
+
50
+ ### Adding Mechanica to an existing Vite + Vue project
51
+
52
+ ```bash
53
+ npm install mechanica vue
54
+ npm install -D vite @vitejs/plugin-vue
55
+ ```
23
56
 
24
- **`vite.config.ts`**
25
57
  ```ts
58
+ // vite.config.ts
26
59
  import { defineConfig } from 'vite'
27
60
  import vue from '@vitejs/plugin-vue'
28
61
  import { mechanica } from 'mechanica/plugin'
@@ -32,92 +65,314 @@ export default defineConfig({
32
65
  })
33
66
  ```
34
67
 
35
- **`index.html`** — just a mount point; the plugin injects the entry and editor:
36
- ```html
37
- <body><div id="app"></div></body>
38
- ```
39
-
40
- **`src/main.ts`** — export an app definition (no `createApp`/`mount` yourself):
41
68
  ```ts
69
+ // src/main.ts — export an app definition; don't call createApp()/mount() yourself
42
70
  import { defineMechanicaApp } from 'mechanica'
43
71
  import App from './App.vue'
44
72
 
45
73
  export default defineMechanicaApp({ root: App })
46
74
  ```
47
75
 
48
- **`src/App.vue`** — render the page content somewhere:
49
76
  ```vue
77
+ <!-- src/App.vue -->
50
78
  <template>
51
79
  <Content />
52
80
  </template>
81
+
53
82
  <script setup lang="ts">
54
83
  import { Content } from 'mechanica'
55
84
  </script>
56
85
  ```
57
86
 
58
- **`src/blocks/Hero.vue`** — a block. `defineBlock` is a global macro (no import):
87
+ `index.html` needs only a mount point, `<div id="app"></div>`. Leave out the usual `<script type="module" src="/src/main.ts">`: the plugin injects the entry (and, in dev, the editor) itself.
88
+
89
+ ## Blocks
90
+
91
+ A block is a `.vue` file in `src/blocks/`. Its `<script setup>` calls the global `defineBlock` macro (no import needed), which declares the editable props and returns them typed:
92
+
59
93
  ```vue
94
+ <!-- src/blocks/Hero.vue -->
60
95
  <template>
61
- <section>
96
+ <section class="hero">
62
97
  <h1>{{ props.title }}</h1>
63
- <p>{{ props.subtitle }}</p>
98
+ <p v-if="props.subtitle">{{ props.subtitle }}</p>
99
+ <Image :image="props.photo" eager />
100
+ <Link v-if="props.cta?.url" :to="props.cta">{{ props.cta.title || 'Read more' }}</Link>
64
101
  </section>
65
102
  </template>
103
+
66
104
  <script setup lang="ts">
105
+ import { Image, Link } from 'mechanica'
106
+
67
107
  const props = defineBlock({
68
108
  name: 'Hero',
69
109
  category: 'Content',
70
110
  props: {
71
- title: { type: 'string', default: 'Hello' },
111
+ title: { type: 'string', label: 'Headline', default: 'Hello' },
72
112
  subtitle: 'text',
113
+ photo: 'image',
114
+ cta: 'smartLink',
73
115
  },
116
+ // Example values for the palette preview and `mechanica shot`
117
+ previewData: { title: 'Build sites visually', subtitle: 'Blocks are plain Vue components.' },
74
118
  })
75
119
  </script>
76
120
  ```
77
121
 
78
- Run `vite` and open the page — the editor overlay lets you add Hero blocks and edit their props live.
122
+ The block's id is its kebab-cased file name (`Hero.vue` → `hero`). Props use [compact-json-schema](https://www.npmjs.com/package/compact-json-schema): the JSON types (`string`, `number`, `boolean`, `object`, `array`) plus Mechanica's field types `text`, `richText`, `image`, `color`, `smartLink` and `multiselect`. The object form takes `default`, `label`, `description`, `placeholder` and `enum` (rendered as a select).
79
123
 
80
- ## Authoring API
124
+ Other `defineBlock` options: `icon`, `description` and `order` for the palette; `hidden` and `devOnly`; `standalone`, `folders` and `layouts` to control where the palette offers the block; `chunk` for code splitting; `slots` for named slots. A block whose template contains `<slot />` becomes a container, and other blocks nest inside it in the editor.
81
125
 
82
- ### `defineBlock(descriptor)` — the one macro
83
- No import; used inside a block's `<script setup>`. Fields use [compact-json-schema](https://www.npmjs.com/package/compact-json-schema). Built-in field types: `string`, `text`, `number`, `boolean`, `color`, `image`, `file`, `smartLink`, `multiselect`, `richText`, plus `array`/`object`. Object-form fields take editor metadata (`label`, `description`, `default`, `placeholder`).
126
+ Give every block meaningful `previewData`: it drives the palette's hover preview, the standalone preview route and `mechanica shot`. A `$slots` key fills slots with child blocks, e.g. `$slots: { default: [{ blockId: 'card', data: { title: 'One' } }] }`.
127
+
128
+ ## Pages
129
+
130
+ Each page is a `.page.md` file under `.mech/pages/`, and its path is the URL: `about.page.md` → `/about`, `blog/index.page.md` → `/blog`, `index.page.md` → `/`.
131
+
132
+ ```markdown
133
+ ---
134
+ name: About
135
+ data:
136
+ head:
137
+ title: About us
138
+ ---
139
+
140
+ ::: hero
141
+ title: About us
142
+ cta: { url: /contacts, title: Write to us }
143
+ @subtitle
144
+ We build fast websites
145
+ for people who care about the details.
146
+ :::
147
+
148
+ ::: columns
149
+ gap: lg
150
+ ::: card slot=left
151
+ title: Fast
152
+ :::
153
+ ::: card slot=right
154
+ title: Readable
155
+ :::
156
+ ::: /columns
157
+ ```
158
+
159
+ - The YAML frontmatter holds the editor label (`name`), page-scoped data (`data`) and optional `layout`, `draft: true` (hidden from listings and the export) and `meta` (`noindex: true`, `lastmod`).
160
+ - Each block is a fence: `::: <blockId>`, then its props as YAML, then `@field` regions for long text, then any child blocks, closed by `:::`. Keep that order: once a region has started, a `key: value` line is part of its text.
161
+ - A region runs until the next `@field`, child block or `:::`, and is taken as written, blank lines and Markdown included. It fills a `text` prop with that text, and a `richText` prop with its Markdown turned into rich text.
162
+ - Blocks nest by placing fences inside a container's fence, and `slot=<name>` puts a child into a named slot (a container's children go either all to the default slot or all to named ones). Nothing is indented: every `:::` and `@field` starts at the beginning of its line, and a block is a child of whichever fence is still open. A container may close with `::: /<blockId>`, which reports a missing `:::` at the line where the fences stop pairing up.
163
+ - Props are YAML, so quote a value that starts with `#` or contains `: `, as in `href: "#pricing"`. Inside a region nothing needs quoting; only a line that starts with `:::`, or consists of just `@name`, takes a leading backslash (`\:::`), and not even that inside a code fence.
164
+ - An `image` prop points at an uploaded file: `photo: { src: /@mechanica/assets/team.jpg, alt: "Our team" }`.
165
+ - Props left out take their schema defaults, so a hand-written page renders exactly like one made in the editor.
166
+
167
+ With `npm run dev` running, edits you make to these files show up in the editor live, and the editor won't overwrite a file that changed on disk underneath it.
168
+
169
+ ## Shared data and `<head>`
170
+
171
+ Data that isn't a block prop (a site header, page titles, a post date) is declared with `defineData`, an ordinary imported function that returns a hook:
84
172
 
85
173
  ```ts
86
- defineBlock({
87
- name, category, icon, description, order, hidden,
88
- props: { /* schema */ },
89
- slots: ['default'], // auto-detected from <slot> if omitted
174
+ // src/data/head.ts
175
+ import { defineData } from 'mechanica'
176
+
177
+ export const useHead = defineData({
178
+ id: 'head',
179
+ title: 'Page head',
180
+ props: {
181
+ title: { type: 'string', default: 'My site' },
182
+ description: 'string',
183
+ },
90
184
  })
91
185
  ```
92
186
 
93
- ### `defineData({ id, scope, props })` — shared data
94
- An **imported function** (not a macro), used in a standalone module; returns a hook:
187
+ Call `useHead()` in any component to read the values. An entry registers when its module is imported, usually by the block or component that uses it. Data that only `index.html` reads, like `head`, still needs importing somewhere (the starter's `App.vue` imports `./data/head`): unregistered entries are left out of the Page data window, page state and `{{ }}` placeholders.
188
+
189
+ Values are edited in the editor's **Page data** window, where each entry can be set for one page, for a folder of pages, or site-wide; a page value overrides its folder's, which overrides the site's. They're stored in the page file, `.mech/folders.json` and `.mech/data.json` respectively. Pass `folder: 'blog'` to offer an entry only on pages under that folder, and `localized: true` to translate its site/folder value per language.
190
+
191
+ `index.html` is templated with the same data, in dev and in the export:
192
+
193
+ ```html
194
+ <title>{{ head.title }}</title>
195
+ <meta name="description" content="{{ head.description }}" />
196
+ ```
197
+
198
+ `{{ page.path }}`, `{{ site.url }}` and `{{ site.name }}` are available too, and the triple-brace form `{{{ expr }}}` emits raw JSON for `<script type="application/ld+json">` blocks.
199
+
200
+ ## Layouts and whole-page blocks
201
+
202
+ Give pages different shells by declaring named layouts. Each layout component renders `<Content />` inside its own chrome, and the root renders `<Layout />`:
203
+
95
204
  ```ts
96
- // data/header.ts
97
- import { defineData } from 'mechanica'
98
- export const useHeader = defineData({ id: 'header', scope: 'site', props: { logo: 'image' } })
99
- // in a component: const header = useHeader()
205
+ // src/main.ts
206
+ export default defineMechanicaApp({
207
+ root: App, // its template: <Layout />
208
+ layouts: { site: SiteLayout, bare: BareLayout }, // the first entry is the default
209
+ })
210
+ ```
211
+
212
+ A page picks its layout with `layout: bare` in its frontmatter, or in the editor's **Page setup** pane. Pages without one get the first layout, and `useLayout().name` tells a component which layout is active.
213
+
214
+ Blocks with `standalone: true` are whole pages in one block, such as a 404 or a contact form. The palette offers them only on empty pages ("Start this page"), so they don't clutter it elsewhere.
215
+
216
+ ## Images
217
+
218
+ Render an `image` prop with the `<Image>` component. It outputs `alt`, intrinsic `width`/`height` (no layout shift), native lazy loading, and a blurred inline preview until the real image arrives. Add `eager` to above-the-fold images.
219
+
220
+ The editor captures dimensions and the blur-up preview on upload. For images added by hand, `mechanica images` generates them headlessly; it needs `sharp` in your project (`npm install -D sharp`), and the export fills in anything missing when `sharp` is installed.
221
+
222
+ Editors can set a focal point and crop from the image field's **Position / Crop** action. Cropping is opt-in per field, via `crop: { width: 1200, height: 630 }` (fixed output size), `crop: { aspect: 16 / 9 }` or `crop: true` (free) on its schema. Crops are non-destructive: the original stays, and a derived file is rendered in its place. For images painted as a CSS background, `imagePosition(image)` turns the focal point into a `background-position` value.
223
+
224
+ ## Links and navigation
225
+
226
+ `<Link :to="…">` takes a path string or a `smartLink` value. Internal links navigate client-side without a full reload, get an `is-active` class on the current page, and add the language prefix on multi-language sites. `useRouter().push(path)` navigates from code, and `useRoute().path` is the current path.
227
+
228
+ ## Listing pages and pagination
229
+
230
+ Blocks can query the site's pages. Queries run at build time and their results are baked into the page, so there's no runtime API to host:
231
+
232
+ ```ts
233
+ import { usePages, usePagination } from 'mechanica'
234
+ import { usePostMeta } from '../data/post-meta' // defineData({ id: 'postMeta', props: { date: 'string' } })
235
+
236
+ const latest = usePages({ folderName: 'blog', data: [usePostMeta], sort: { by: 'postMeta.date', dir: 'desc' }, limit: 3 })
237
+
238
+ const blog = usePagination({ folderName: 'blog', data: [usePostMeta], pageSize: 10 })
239
+ // blog.items, blog.page, blog.pageCount, blog.prevPath, blog.nextPath, blog.pathFor(n)
100
240
  ```
101
241
 
102
- ### Components & composables
103
- `<Content>`, `<Link :to="..." />` (string path or `smartLink` object), `useRouter`, `useRoute`, `usePages`, `useFetch`, `usePageData`.
242
+ A paginated page exports as real pages, `/blog`, `/blog/2` and so on, each with its own slice. `useFetch({ url })` fetches external JSON the same way: on the server, baked into the output.
243
+
244
+ ## Generated pages
245
+
246
+ To produce many pages from an external source (a product catalog, API docs) without a `.page.md` file each, give the plugin page providers:
104
247
 
105
- ### Custom field types
106
248
  ```ts
107
- import { defineFieldType, registerFields } from 'mechanica/editor'
108
- registerFields([
109
- defineFieldType({ name: 'video', schema: { type: 'object', properties: { src: 'string' } }, editor: VideoField }),
110
- ])
249
+ // vite.config.ts
250
+ import { mechanica, type PageProvider } from 'mechanica/plugin'
251
+
252
+ const products: PageProvider = async () => {
253
+ const items = await fetch('https://api.example.com/products').then((res) => res.json())
254
+ return items.map((item) => ({
255
+ path: `/shop/${item.slug}`,
256
+ content: [{ id: item.slug, blockId: 'product', data: { title: item.title } }],
257
+ data: { head: { title: item.title } },
258
+ }))
259
+ }
260
+
261
+ // plugins: [mechanica({ generatePages: [products] }), vue()]
111
262
  ```
112
263
 
113
- ## CLI
264
+ Providers run when the dev server starts and at build. Generated pages render through the normal pipeline, appear in the sitemap and are read-only in the editor.
265
+
266
+ ## Multi-language sites
267
+
268
+ ```ts
269
+ mechanica({ locales: ['en', 'de'] }) // the first locale is the default
270
+ ```
271
+
272
+ The default language is served at unprefixed URLs, the others under `/de/…`. A translation is a sibling file, `about@de.page.md`, created from the editor's language switcher. It stores only the fields that differ, so images, links and anything else left untranslated keep following the default-language page.
273
+
274
+ `<Link>` keeps visitors in their language automatically. For a language switcher, `useLocale()` returns `locale`, `locales` and `localePath()`, and `<Link :to="path" :locale="code">` targets a specific language. The export writes each translation to its own URL (pages without a translation are skipped for that language), and with `siteUrl` set it adds `hreflang` alternates to the page head and the sitemap.
275
+
276
+ ## Rich text and widgets
277
+
278
+ A `richText` prop is edited with a WYSIWYG editor for headings, lists, bold/italic/underline, images, code, callouts and tables. It switches to a Markdown view, where you can also write links as `[text](url)`, and it's stored as Markdown inside the page file. Its value is a [vuewrite](https://www.npmjs.com/package/vuewrite) document; render it with vuewrite's `TextViewer`, as the starter's `RichTextView.vue` does.
279
+
280
+ You can add your own content widgets to the editor's Insert menu: put a module in `src/widgets/` that default-exports `defineWidget({ type, title, icon, create, editor })` from `mechanica/widgets`, then render the widget through a `#<type>` slot on your `TextViewer`.
281
+
282
+ ## Block Composer
283
+
284
+ Designers can build new blocks without code. The palette's **New block** button opens the composer: frames with auto layout, text, images and your own components, per-breakpoint styles, and repeated items. Any value can be bound to a prop, so each placed copy stays editable. Composed blocks are saved as `.mech/blocks/<id>.block.yml`, appear in the palette under "Site blocks", and render, preview and export like any other block.
285
+
286
+ `src/composer.ts` hands the composer your design system:
287
+
288
+ ```ts
289
+ import { defineComposer } from 'mechanica'
290
+ import UiButton from './components/UiButton.vue'
291
+
292
+ export default defineComposer({
293
+ components: {
294
+ button: { component: UiButton, name: 'Button', props: { label: { type: 'string', default: 'Button' } } },
295
+ },
296
+ classes: { container: { title: 'Container', on: 'frame' } }, // your CSS classes, offered as element styles
297
+ breakpoints: { md: 1024, sm: 640 },
298
+ })
299
+ ```
300
+
301
+ ## SEO
302
+
303
+ Set `siteUrl` on the plugin, or pass `--site-url` to `mechanica export`, and the export adds what nobody should write by hand: canonical and `og:url` tags, absolute social-image URLs, `rel=prev/next` on paginated pages, BreadcrumbList JSON-LD from the folder structure, `sitemap.xml` and `robots.txt` (a `public/robots.txt` of your own takes precedence). Add `siteName` for WebSite JSON-LD on the home page. Tags already in your `index.html` are never duplicated. `meta: { noindex: true }` in a page's frontmatter keeps it out of search engines and the sitemap.
304
+
305
+ Every export also warns about missing titles and descriptions, pages without exactly one `<h1>`, images without `alt`, duplicate titles, and internal links to pages that don't exist. A page at `/404` is also written as `404.html`, which most static hosts serve for unknown URLs.
306
+
307
+ ## Code splitting
308
+
309
+ By default all blocks share one `blocks` chunk, cached across pages. Mark heavy, rarely used blocks with `chunk: 'charts'` in `defineBlock` to split them into their own chunk, or set the plugin's `blockChunks: 'per-block'`. The export adds per-page `<link rel="modulepreload">` and stylesheet tags, so each page loads exactly the block code it uses.
310
+
311
+ ## Checking your work visually
114
312
 
115
313
  ```bash
116
- mechanica build # client + SSR bundles → dist/
117
- mechanica export # statically render every .mech page → export/
118
- mechanica shot <blockId | /page/path> # headless screenshot of a block or page
119
- mechanica thumbs [--blocks] # regenerate page / block thumbnails
120
- mechanica push --key <apiKey> --host <url> # upload dist/ to a backend
314
+ npx mechanica shot hero # screenshot one block, from its previewData
315
+ npx mechanica shot /about --width 1440,390 # a whole page, at two widths
316
+ npx mechanica thumbs # page thumbnails for the editor's page browser
317
+ npx mechanica thumbs --blocks # block thumbnails for the palette
121
318
  ```
122
319
 
123
- Page content lives in `.mech/pages/**.page.md` — a human-readable, Markdown-centric format equally editable by the visual editor, by hand, and by AI tools (rich text persists as plain Markdown).
320
+ These drive a locally installed Chrome or Edge (or `--browser <path>`) and need Node.js 22+ or Bun. They reuse a dev server running on port 5173 and start one if there isn't. Screenshots go to `.mech/shots/`, which is handy for reviewing a block or letting an AI assistant check its own work.
321
+
322
+ ## CLI
323
+
324
+ | Command | What it does |
325
+ | --- | --- |
326
+ | `mechanica export` | Build, then render every page to static HTML in `export/`. Flags: `--site-url`, `--site-name`, `--assets-url` (serve `/assets` and `/media` from a CDN) |
327
+ | `mechanica build` | Build the client and server bundles into `dist/` (`export` runs this for you) |
328
+ | `mechanica shot <blockId \| /path>` | Screenshot a block or a page. Flags: `--width`, `--data`, `--out`, `--full`, `--server`, `--browser` |
329
+ | `mechanica thumbs [--blocks]` | Regenerate page or block thumbnails for the editor |
330
+ | `mechanica images` | Generate image dimensions and blur-up previews for `.mech/assets` (needs `sharp`; `--force` redoes all) |
331
+ | `mechanica push` | Experimental: upload `dist/` to a Mechanica hosting backend |
332
+
333
+ ## Plugin options
334
+
335
+ `mechanica(options)` from `mechanica/plugin`; every option is optional.
336
+
337
+ | Option | Default | |
338
+ | --- | --- | --- |
339
+ | `siteUrl`, `siteName` | none | Public origin and name: `{{ site.* }}` templating and SEO output at export |
340
+ | `locales` | none | `['en', 'de']` or `{ default, all, labels }` turns on multi-language pages |
341
+ | `generatePages` | none | Page providers (see above) |
342
+ | `blockChunks` | `'bundled'` | `'per-block'` gives each block its own chunk |
343
+ | `assetsUrl` | none | CDN origin for exported assets. Set Vite's `base` to the same URL |
344
+ | `entry` | `'src/main.ts'` | Module exporting `defineMechanicaApp(…)` |
345
+ | `mount` | `'#app'` | Mount selector |
346
+ | `blocksDir` | `'src/blocks'` | Where blocks are collected from |
347
+ | `widgetsDir` | `'src/widgets'` | Where rich-text widgets are collected from |
348
+ | `composerFile` | `'src/composer.ts'` | The Block Composer manifest |
349
+ | `mechDir` | `'.mech'` | Content directory |
350
+
351
+ ## API
352
+
353
+ From `mechanica`:
354
+
355
+ - **App and data:** `defineMechanicaApp`, `defineData`, `defineComposer`
356
+ - **Components:** `<Content>`, `<Layout>`, `<Link>`, `<Image>`
357
+ - **Composables:** `usePages`, `usePagination`, `useFetch`, `useLocale`, `useLayout`, `usePageData` (the current page's path, `meta`, layout and locale), `useRouter`, `useRoute`
358
+ - **Helpers:** `imagePosition`
359
+
360
+ From `mechanica/plugin`: `mechanica` and the `PageProvider` type. From `mechanica/widgets`: `defineWidget` and `useWidgetServices` (uploads and the image picker inside widget editors). `mechanica/editor` and `mechanica/composer` are loaded by the plugin in development; you don't import them.
361
+
362
+ ## Upgrading from 1.x
363
+
364
+ Mechanica 2 is a rewrite, and a 1.x project has to be ported by hand; there is no automatic migration. What changes:
365
+
366
+ - Vite 8 and `@vitejs/plugin-vue` 6 are required.
367
+ - `src/main.ts` exports `defineMechanicaApp({ root: App })` instead of calling `createSSRApp(App).use(createMechanica()).mount('#app')`, and `index.html` no longer loads it with a `<script>` tag.
368
+ - The plugin is a named import, `import { mechanica } from 'mechanica/plugin'`, and its `entrySsr` option is now `entry`.
369
+ - In `defineBlock`, `group` is now `category` and a field's label is `label` (it was `name`). Block ids default to the kebab-cased file name (`Headline.vue` → `headline`); set `id` to keep an old one.
370
+ - Pages are `.page.md` files instead of `.mech/pages/**.json`, and JSON pages are ignored. Uploaded files in `.mech/assets/` keep working.
371
+ - `RouterLink` and `SmartLink` are replaced by `<Link>` (whose active class is now `is-active`), and `useQuery` by `usePages`, `usePagination` and `useFetch`.
372
+ - `{{ }}` placeholders in `index.html` are now HTML-escaped.
373
+
374
+ 1.x remains installable as `mechanica@1`.
375
+
376
+ ## License
377
+
378
+ MIT
package/bin/mechanica.js CHANGED
@@ -1,7 +1,11 @@
1
1
  #!/usr/bin/env node
2
- // Under Bun, run the TS source directly (monorepo dev, Bun-native installs —
3
- // always live, no build needed). Under Node, use the compiled CLI from dist/.
4
- const entry = process.versions.bun ? '../src/cli/cli.ts' : '../dist/cli.js'
2
+ // In the repo checkout (src/ present) under Bun, run the TS source directly —
3
+ // monorepo dev stays live with no build. Published tarballs ship dist/ only,
4
+ // so installed packages always run the compiled CLI, under Node and Bun alike.
5
+ import { existsSync } from 'node:fs'
6
+
7
+ const srcEntry = new URL('../src/cli/cli.ts', import.meta.url)
8
+ const entry = process.versions.bun && existsSync(srcEntry) ? '../src/cli/cli.ts' : '../dist/cli.js'
5
9
 
6
10
  const { run } = await import(entry)
7
11
 
@@ -27,6 +27,8 @@ var contents = {
27
27
  "corner": `<path d="M5 20v-9a6 6 0 0 1 6-6h9"/>`,
28
28
  "desktop": `<rect x="3" y="4.5" width="18" height="12.5" rx="2"/><path d="M12 17v3"/><path d="M8.5 20.5h7"/>`,
29
29
  "ellipsis": `<circle cx="5" cy="12" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="19" cy="12" r="2"/>`,
30
+ "eye-off": `<path d="M4 4l16 16"/><path d="M10.6 6c.46-.06.93-.1 1.4-.1 6 0 9.5 6.1 9.5 6.1a17.3 17.3 0 0 1-2.6 3.3"/><path d="M6.4 7.5A16.6 16.6 0 0 0 2.5 12s3.5 6.1 9.5 6.1c1.2 0 2.3-.24 3.3-.63"/><path d="M9.9 9.9a3 3 0 0 0 4.2 4.2"/>`,
31
+ "eye": `<path d="M2.5 12s3.5-6.5 9.5-6.5S21.5 12 21.5 12s-3.5 6.5-9.5 6.5S2.5 12 2.5 12z"/><circle cx="12" cy="12" r="3"/>`,
30
32
  "fill": `<path d="M12 3.5c3.2 3.7 5.5 6.9 5.5 9.7a5.5 5.5 0 0 1-11 0c0-2.8 2.3-6 5.5-9.7z"/>`,
31
33
  "folder": `<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>`,
32
34
  "frame": `<rect x="4" y="4" width="16" height="16" rx="2"/><path d="M4 9.5h16M9.5 9.5V20"/>`,
@@ -38,6 +40,7 @@ var contents = {
38
40
  "info": `<circle cx="12" cy="12" r="9"/><line x1="12" y1="11" x2="12" y2="16"/><line x1="12" y1="8" x2="12.01" y2="8"/>`,
39
41
  "italic": `<line x1="19" y1="4" x2="10" y2="4"/><line x1="14" y1="20" x2="5" y2="20"/><line x1="15" y1="4" x2="9" y2="20"/>`,
40
42
  "limits": `<rect x="3" y="3" width="18" height="18" rx="2"/><rect x="8" y="8" width="8" height="8" rx="1.5" stroke-dasharray="2.5 2.5"/>`,
43
+ "link": `<path d="M10 14a5 5 0 0 0 7.1.4l3-3a5 5 0 0 0-7.1-7.1l-1.7 1.7"/><path d="M14 10a5 5 0 0 0-7.1-.4l-3 3a5 5 0 0 0 7.1 7.1l1.7-1.7"/>`,
41
44
  "margin": `<rect x="8" y="8" width="8" height="8" rx="1"/><path d="M12 2.5v3"/><path d="M12 18.5v3"/><path d="M2.5 12h3"/><path d="M18.5 12h3"/>`,
42
45
  "menu": `<path d="M3 6h18"/><path d="M3 12h18"/><path d="M3 18h18"/>`,
43
46
  "mobile": `<rect x="7" y="2.5" width="10" height="19" rx="2.2"/><path d="M10.75 18.25h2.5"/>`,
@@ -46,6 +49,7 @@ var contents = {
46
49
  "plus": `<path d="M12 5v14"/><path d="M5 12h14"/>`,
47
50
  "position": `<circle cx="12" cy="12" r="5"/><path d="M12 2.5V5"/><path d="M12 19v2.5"/><path d="M2.5 12H5"/><path d="M19 12h2.5"/>`,
48
51
  "redo": `<path d="M21 7v6h-6"/><path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3L21 13"/>`,
52
+ "repeat": `<rect x="3" y="6" width="7" height="12" rx="1.5"/><rect x="14" y="6" width="7" height="12" rx="1.5" stroke-dasharray="2.5 2.5"/>`,
49
53
  "row": `<rect x="4" y="5" width="6.5" height="14" rx="1.6"/><rect x="13.5" y="5" width="6.5" height="14" rx="1.6"/>`,
50
54
  "search": `<circle cx="11" cy="11" r="7"/><path d="m21 21-4.35-4.35"/>`,
51
55
  "sides": `<rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6" rx="1"/>`,
@@ -230,6 +234,26 @@ var attrs = {
230
234
  "viewBox": "0 0 24 24",
231
235
  "fill": "currentColor"
232
236
  },
237
+ "eye-off": {
238
+ "width": "24",
239
+ "height": "24",
240
+ "viewBox": "0 0 24 24",
241
+ "fill": "none",
242
+ "stroke": "currentColor",
243
+ "stroke-width": "1.6",
244
+ "stroke-linecap": "round",
245
+ "stroke-linejoin": "round"
246
+ },
247
+ "eye": {
248
+ "width": "24",
249
+ "height": "24",
250
+ "viewBox": "0 0 24 24",
251
+ "fill": "none",
252
+ "stroke": "currentColor",
253
+ "stroke-width": "1.6",
254
+ "stroke-linecap": "round",
255
+ "stroke-linejoin": "round"
256
+ },
233
257
  "fill": {
234
258
  "width": "24",
235
259
  "height": "24",
@@ -329,6 +353,16 @@ var attrs = {
329
353
  "stroke-linecap": "round",
330
354
  "stroke-linejoin": "round"
331
355
  },
356
+ "link": {
357
+ "width": "24",
358
+ "height": "24",
359
+ "viewBox": "0 0 24 24",
360
+ "fill": "none",
361
+ "stroke": "currentColor",
362
+ "stroke-width": "1.6",
363
+ "stroke-linecap": "round",
364
+ "stroke-linejoin": "round"
365
+ },
332
366
  "margin": {
333
367
  "width": "24",
334
368
  "height": "24",
@@ -399,6 +433,16 @@ var attrs = {
399
433
  "stroke-linecap": "round",
400
434
  "stroke-linejoin": "round"
401
435
  },
436
+ "repeat": {
437
+ "width": "24",
438
+ "height": "24",
439
+ "viewBox": "0 0 24 24",
440
+ "fill": "none",
441
+ "stroke": "currentColor",
442
+ "stroke-width": "1.6",
443
+ "stroke-linecap": "round",
444
+ "stroke-linejoin": "round"
445
+ },
402
446
  "row": {
403
447
  "width": "24",
404
448
  "height": "24",