mechanica 2.0.0-alpha.9 → 2.0.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/README.md +288 -48
- package/bin/mechanica.js +7 -3
- package/dist/chunks/{ImagePickerDialog-CY-mn2UP.js → ImagePickerDialog-6skSAzJh.js} +44 -0
- package/dist/chunks/{create-mechanica-CnBFiyfJ.js → create-mechanica-CYaKXuQd.js} +243 -25
- package/dist/chunks/{editor-9tquvDcS.js → editor-B0r4EOIM.js} +84 -9
- package/dist/chunks/{page-assets-BCUAuOlo.js → page-assets-Bp6mxPSy.js} +14 -2
- package/dist/chunks/{preview-G5_AFI5P.js → preview-CbUxh2gL.js} +4 -2
- package/dist/cli.js +49 -4
- package/dist/composer.js +603 -180
- package/dist/editor.css +344 -100
- package/dist/editor.js +358 -92
- package/dist/index.js +48 -85
- package/dist/plugin.js +105 -22
- package/dist/types/cli/export.d.ts +6 -0
- package/dist/types/cli/page-assets.d.ts +7 -0
- package/dist/types/core/create-mechanica.d.ts +3 -1
- package/dist/types/core/data-registry.d.ts +2 -0
- package/dist/types/core/define-block.d.ts +17 -0
- package/dist/types/core/define-data.d.ts +8 -0
- package/dist/types/core/define-mechanica-app.d.ts +7 -0
- package/dist/types/core/index.d.ts +1 -0
- package/dist/types/core/layout.d.ts +27 -0
- package/dist/types/core/render-blocks.d.ts +2 -1
- package/dist/types/core/state.d.ts +5 -0
- package/dist/types/editor/composer/lib/canvas.d.ts +15 -2
- package/dist/types/editor/composer/lib/composer-store.d.ts +18 -2
- package/dist/types/editor/lib/block-meta.d.ts +16 -0
- package/dist/types/editor/lib/bridge.d.ts +8 -0
- package/dist/types/editor/lib/data-meta.d.ts +9 -0
- package/dist/types/editor/lib/page-list.d.ts +2 -0
- package/dist/types/editor/lib/page-setup.d.ts +15 -0
- package/dist/types/editor/lib/store.d.ts +9 -0
- package/dist/types/editor/lib/types.d.ts +2 -0
- package/dist/types/editor/lib/use-block-frames.d.ts +9 -0
- package/dist/types/elements/emit-css.d.ts +2 -1
- package/dist/types/elements/style-vars.d.ts +10 -8
- package/dist/types/vite/dev/pages-store.d.ts +6 -0
- package/dist/types/vite/plugin.d.ts +21 -11
- package/dist/widgets.js +1 -1
- package/package.json +2 -8
- package/src/cli/args.ts +0 -33
- package/src/cli/build.ts +0 -40
- package/src/cli/cli.ts +0 -76
- package/src/cli/export.ts +0 -787
- package/src/cli/headless.ts +0 -361
- package/src/cli/images.ts +0 -67
- package/src/cli/page-assets.ts +0 -114
- package/src/cli/push.ts +0 -64
- package/src/cli/shot.ts +0 -245
- package/src/cli/thumbs.ts +0 -252
- package/src/compiler/compile-block.ts +0 -212
- package/src/core/composed.ts +0 -45
- package/src/core/composer-components.ts +0 -31
- package/src/core/content.ts +0 -24
- package/src/core/create-mechanica.ts +0 -126
- package/src/core/data-registry.ts +0 -37
- package/src/core/define-block.ts +0 -51
- package/src/core/define-data.ts +0 -65
- package/src/core/define-mechanica-app.ts +0 -56
- package/src/core/field-types.ts +0 -37
- package/src/core/image.ts +0 -107
- package/src/core/index.ts +0 -57
- package/src/core/link.ts +0 -87
- package/src/core/load-blocks.ts +0 -59
- package/src/core/preview.ts +0 -230
- package/src/core/query.ts +0 -39
- package/src/core/render-blocks.ts +0 -66
- package/src/core/router.ts +0 -137
- package/src/core/state.ts +0 -37
- package/src/core/use-fetch.ts +0 -14
- package/src/core/use-locale.ts +0 -40
- package/src/core/use-page-data.ts +0 -10
- package/src/core/use-pages.ts +0 -39
- package/src/core/use-pagination.ts +0 -75
- package/src/editor/EditorApp.vue +0 -396
- package/src/editor/components/BlockFrame.vue +0 -18
- package/src/editor/components/BlockPalette.vue +0 -338
- package/src/editor/components/BlockPreview.vue +0 -127
- package/src/editor/components/BlockSettings.vue +0 -63
- package/src/editor/components/DataSettings.vue +0 -240
- package/src/editor/components/HierarchyTree.vue +0 -568
- package/src/editor/components/IconField.vue +0 -201
- package/src/editor/components/LocaleSwitcher.vue +0 -239
- package/src/editor/components/PageBar.vue +0 -109
- package/src/editor/components/PanelToggle.vue +0 -161
- package/src/editor/components/TypographyInput.vue +0 -131
- package/src/editor/components/VContextMenu.vue +0 -187
- package/src/editor/components/VIcon.vue +0 -20
- package/src/editor/components/VPopover.vue +0 -135
- package/src/editor/components/VSegmented.vue +0 -93
- package/src/editor/components/VSelect.vue +0 -222
- package/src/editor/composer/ComposerApp.vue +0 -220
- package/src/editor/composer/components/AlignGrid.vue +0 -74
- package/src/editor/composer/components/AnchorGrid.vue +0 -36
- package/src/editor/composer/components/BindField.vue +0 -42
- package/src/editor/composer/components/BoxSideInput.vue +0 -141
- package/src/editor/composer/components/CanvasOverlay.vue +0 -488
- package/src/editor/composer/components/ClassStylePreview.vue +0 -82
- package/src/editor/composer/components/ColorField.vue +0 -36
- package/src/editor/composer/components/ComponentFields.vue +0 -39
- package/src/editor/composer/components/ComposerCanvas.vue +0 -376
- package/src/editor/composer/components/ComposerInspector.vue +0 -506
- package/src/editor/composer/components/ComposerLayers.vue +0 -28
- package/src/editor/composer/components/ComposerSettings.vue +0 -52
- package/src/editor/composer/components/InsertToolbar.vue +0 -96
- package/src/editor/composer/components/LayerNode.vue +0 -69
- package/src/editor/composer/components/MultiSelectPanel.vue +0 -57
- package/src/editor/composer/components/NumInput.vue +0 -121
- package/src/editor/composer/components/OverrideLabel.vue +0 -21
- package/src/editor/composer/components/PropToggle.vue +0 -42
- package/src/editor/composer/components/PropsSection.vue +0 -51
- package/src/editor/composer/components/SegControl.vue +0 -30
- package/src/editor/composer/components/SizeInput.vue +0 -62
- package/src/editor/composer/composer.ts +0 -220
- package/src/editor/composer/lib/abs.ts +0 -78
- package/src/editor/composer/lib/canvas-drop-target.ts +0 -103
- package/src/editor/composer/lib/canvas-drop.ts +0 -78
- package/src/editor/composer/lib/canvas-overlay.ts +0 -168
- package/src/editor/composer/lib/canvas-view.ts +0 -75
- package/src/editor/composer/lib/canvas.ts +0 -65
- package/src/editor/composer/lib/class-css.ts +0 -103
- package/src/editor/composer/lib/class-groups.ts +0 -86
- package/src/editor/composer/lib/clipboard.ts +0 -25
- package/src/editor/composer/lib/composer-history.ts +0 -81
- package/src/editor/composer/lib/composer-store.ts +0 -546
- package/src/editor/composer/lib/design-tokens.ts +0 -46
- package/src/editor/composer/lib/elements-meta.ts +0 -123
- package/src/editor/composer/lib/inspector-props.ts +0 -76
- package/src/editor/composer/lib/keys.ts +0 -14
- package/src/editor/composer/lib/marquee.ts +0 -26
- package/src/editor/composer/lib/normalize-template.ts +0 -51
- package/src/editor/composer/lib/padding.ts +0 -71
- package/src/editor/composer/lib/scrub.ts +0 -9
- package/src/editor/composer/lib/use-insert-dnd.ts +0 -131
- package/src/editor/composer/lib/use-layer-dnd.ts +0 -99
- package/src/editor/composer/styles/composer.scss +0 -1657
- package/src/editor/dialogs/ConfirmDialog.vue +0 -48
- package/src/editor/dialogs/DataDialog.vue +0 -16
- package/src/editor/dialogs/ImageCropDialog.vue +0 -360
- package/src/editor/dialogs/ImagePickerDialog.vue +0 -219
- package/src/editor/dialogs/PageFormDialog.vue +0 -127
- package/src/editor/dialogs/PagesDialog.vue +0 -885
- package/src/editor/dialogs/QuickSwitcher.vue +0 -208
- package/src/editor/dialogs/RichTextDialog.vue +0 -38
- package/src/editor/editor.ts +0 -247
- package/src/editor/fields/FieldControl.vue +0 -31
- package/src/editor/fields/builtin.ts +0 -29
- package/src/editor/fields/editors/BooleanField.vue +0 -12
- package/src/editor/fields/editors/ColorField.vue +0 -12
- package/src/editor/fields/editors/EnumField.vue +0 -33
- package/src/editor/fields/editors/ImageField.vue +0 -178
- package/src/editor/fields/editors/MultiselectField.vue +0 -35
- package/src/editor/fields/editors/NumberField.vue +0 -12
- package/src/editor/fields/editors/RichTextField.vue +0 -59
- package/src/editor/fields/editors/SmartLinkField.vue +0 -457
- package/src/editor/fields/editors/StringField.vue +0 -13
- package/src/editor/fields/editors/TextField.vue +0 -15
- package/src/editor/fields/registry.ts +0 -52
- package/src/editor/fields/richtext/RichCodeWidget.vue +0 -120
- package/src/editor/fields/richtext/RichImageWidget.vue +0 -129
- package/src/editor/fields/richtext/RichTableWidget.vue +0 -96
- package/src/editor/fields/richtext/RichTextEditor.vue +0 -792
- package/src/editor/fields/richtext/WidgetBoundary.vue +0 -45
- package/src/editor/fields/richtext/config.ts +0 -83
- package/src/editor/fields/richtext/keys.ts +0 -12
- package/src/editor/fields/richtext/widgets.ts +0 -117
- package/src/editor/icons/align-center.svg +0 -1
- package/src/editor/icons/align-left.svg +0 -1
- package/src/editor/icons/align-right.svg +0 -1
- package/src/editor/icons/arrow-down.svg +0 -1
- package/src/editor/icons/arrow-up.svg +0 -1
- package/src/editor/icons/bold.svg +0 -1
- package/src/editor/icons/bolt.svg +0 -1
- package/src/editor/icons/book.svg +0 -1
- package/src/editor/icons/button.svg +0 -1
- package/src/editor/icons/check.svg +0 -1
- package/src/editor/icons/chevron-down.svg +0 -1
- package/src/editor/icons/close.svg +0 -1
- package/src/editor/icons/code.svg +0 -1
- package/src/editor/icons/column.svg +0 -1
- package/src/editor/icons/component.svg +0 -1
- package/src/editor/icons/copy.svg +0 -1
- package/src/editor/icons/corner.svg +0 -1
- package/src/editor/icons/desktop.svg +0 -1
- package/src/editor/icons/ellipsis.svg +0 -1
- package/src/editor/icons/fill.svg +0 -1
- package/src/editor/icons/folder.svg +0 -1
- package/src/editor/icons/frame.svg +0 -1
- package/src/editor/icons/gap.svg +0 -1
- package/src/editor/icons/globe.svg +0 -1
- package/src/editor/icons/grip.svg +0 -1
- package/src/editor/icons/height.svg +0 -1
- package/src/editor/icons/image.svg +0 -1
- package/src/editor/icons/info.svg +0 -1
- package/src/editor/icons/italic.svg +0 -1
- package/src/editor/icons/limits.svg +0 -1
- package/src/editor/icons/margin.svg +0 -1
- package/src/editor/icons/menu.svg +0 -1
- package/src/editor/icons/mobile.svg +0 -1
- package/src/editor/icons/named-slots.svg +0 -1
- package/src/editor/icons/pencil.svg +0 -1
- package/src/editor/icons/plus.svg +0 -1
- package/src/editor/icons/position.svg +0 -1
- package/src/editor/icons/redo.svg +0 -1
- package/src/editor/icons/row.svg +0 -1
- package/src/editor/icons/search.svg +0 -1
- package/src/editor/icons/sides.svg +0 -1
- package/src/editor/icons/sliders.svg +0 -1
- package/src/editor/icons/slot.svg +0 -1
- package/src/editor/icons/strikethrough.svg +0 -1
- package/src/editor/icons/table.svg +0 -4
- package/src/editor/icons/tablet.svg +0 -1
- package/src/editor/icons/text.svg +0 -1
- package/src/editor/icons/trash.svg +0 -1
- package/src/editor/icons/underline.svg +0 -1
- package/src/editor/icons/undo.svg +0 -1
- package/src/editor/icons/upload.svg +0 -1
- package/src/editor/icons/width.svg +0 -1
- package/src/editor/icons/wrap.svg +0 -1
- package/src/editor/lib/block-meta.ts +0 -98
- package/src/editor/lib/block-preview.ts +0 -84
- package/src/editor/lib/bridge.ts +0 -90
- package/src/editor/lib/content-tree.ts +0 -186
- package/src/editor/lib/context-menu.ts +0 -52
- package/src/editor/lib/drag-controller.ts +0 -217
- package/src/editor/lib/history.ts +0 -91
- package/src/editor/lib/image-crop.ts +0 -174
- package/src/editor/lib/image-size.ts +0 -154
- package/src/editor/lib/keyboard.ts +0 -29
- package/src/editor/lib/navigation.ts +0 -31
- package/src/editor/lib/page-list.ts +0 -89
- package/src/editor/lib/panel-dock.ts +0 -97
- package/src/editor/lib/recents.ts +0 -31
- package/src/editor/lib/save-queue.ts +0 -156
- package/src/editor/lib/shortcuts.ts +0 -54
- package/src/editor/lib/store.ts +0 -331
- package/src/editor/lib/tree-ui.ts +0 -33
- package/src/editor/lib/types.ts +0 -24
- package/src/editor/lib/typography.ts +0 -80
- package/src/editor/lib/use-block-frames.ts +0 -125
- package/src/editor/props-panel/ArrayField.vue +0 -106
- package/src/editor/props-panel/SchemaField.vue +0 -45
- package/src/editor/props-panel/SchemaForm.vue +0 -120
- package/src/editor/props-panel/VCollapse.vue +0 -28
- package/src/editor/props-panel/humanize.ts +0 -8
- package/src/editor/props-panel/use-reorder.ts +0 -90
- package/src/editor/styles/_variables.scss +0 -54
- package/src/editor/styles/editor.scss +0 -915
- package/src/editor/ui/VDialog.vue +0 -95
- package/src/editor/ui/VDialogHost.vue +0 -116
- package/src/editor/ui/dialog.ts +0 -47
- package/src/editor/widget-api.ts +0 -49
- package/src/elements/elements.scss +0 -69
- package/src/elements/emit-css.ts +0 -153
- package/src/elements/index.ts +0 -135
- package/src/elements/style-vars.ts +0 -230
- package/src/index.ts +0 -1
- package/src/svg-plugin.ts +0 -108
- package/src/virtual-modules.d.ts +0 -23
- package/src/vite/collect-blocks.ts +0 -99
- package/src/vite/collect-components.ts +0 -58
- package/src/vite/collect-composed.ts +0 -49
- package/src/vite/collect-widgets.ts +0 -54
- package/src/vite/dev/assets-store.ts +0 -217
- package/src/vite/dev/composed-store.ts +0 -99
- package/src/vite/dev/composer.ts +0 -46
- package/src/vite/dev/data-store.ts +0 -147
- package/src/vite/dev/fs-utils.ts +0 -41
- package/src/vite/dev/image-preview.ts +0 -102
- package/src/vite/dev/middleware.ts +0 -395
- package/src/vite/dev/page-state.ts +0 -167
- package/src/vite/dev/pages-store.ts +0 -583
- package/src/vite/dev/preview.ts +0 -69
- package/src/vite/dev/query-dev.ts +0 -33
- package/src/vite/entries.ts +0 -200
- package/src/vite/generate-pages.ts +0 -51
- package/src/vite/index.ts +0 -17
- package/src/vite/plugin.ts +0 -697
- package/src/vite/read-breakpoints.ts +0 -47
- package/src/vite/rich-text-codec.ts +0 -48
- 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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12
|
+
## Quick start
|
|
10
13
|
|
|
11
14
|
```bash
|
|
12
15
|
npm create mechanica@latest my-site
|
|
13
|
-
|
|
16
|
+
cd my-site
|
|
17
|
+
npm install
|
|
18
|
+
npm run dev
|
|
14
19
|
```
|
|
15
20
|
|
|
16
|
-
|
|
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
|
|
26
|
+
npm run export
|
|
20
27
|
```
|
|
21
28
|
|
|
22
|
-
|
|
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 a `CLAUDE.md` that teaches AI coding assistants 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,299 @@ 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
125
|
+
|
|
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
|
+
@subtitle
|
|
143
|
+
We build fast websites
|
|
144
|
+
for people who care about the details.
|
|
145
|
+
:::
|
|
146
|
+
```
|
|
81
147
|
|
|
82
|
-
|
|
83
|
-
|
|
148
|
+
- 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`).
|
|
149
|
+
- Each block is a fence: `::: <blockId>`, then its props as YAML, then `@field` regions for long text, closed by `:::`. A region fills a `text` prop with its text as written, and a `richText` prop with its Markdown turned into rich text. Blocks nest by placing fences inside a container's fence.
|
|
150
|
+
- Props left out take their schema defaults, so a hand-written page renders exactly like one made in the editor.
|
|
151
|
+
|
|
152
|
+
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.
|
|
153
|
+
|
|
154
|
+
## Shared data and `<head>`
|
|
155
|
+
|
|
156
|
+
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
157
|
|
|
85
158
|
```ts
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
159
|
+
// src/data/head.ts
|
|
160
|
+
import { defineData } from 'mechanica'
|
|
161
|
+
|
|
162
|
+
export const useHead = defineData({
|
|
163
|
+
id: 'head',
|
|
164
|
+
title: 'Page head',
|
|
165
|
+
props: {
|
|
166
|
+
title: { type: 'string', default: 'My site' },
|
|
167
|
+
description: 'string',
|
|
168
|
+
},
|
|
90
169
|
})
|
|
91
170
|
```
|
|
92
171
|
|
|
93
|
-
|
|
94
|
-
|
|
172
|
+
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.
|
|
173
|
+
|
|
174
|
+
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.
|
|
175
|
+
|
|
176
|
+
`index.html` is templated with the same data, in dev and in the export:
|
|
177
|
+
|
|
178
|
+
```html
|
|
179
|
+
<title>{{ head.title }}</title>
|
|
180
|
+
<meta name="description" content="{{ head.description }}" />
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`{{ 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.
|
|
184
|
+
|
|
185
|
+
## Layouts and whole-page blocks
|
|
186
|
+
|
|
187
|
+
Give pages different shells by declaring named layouts. Each layout component renders `<Content />` inside its own chrome, and the root renders `<Layout />`:
|
|
188
|
+
|
|
95
189
|
```ts
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
190
|
+
// src/main.ts
|
|
191
|
+
export default defineMechanicaApp({
|
|
192
|
+
root: App, // its template: <Layout />
|
|
193
|
+
layouts: { site: SiteLayout, bare: BareLayout }, // the first entry is the default
|
|
194
|
+
})
|
|
100
195
|
```
|
|
101
196
|
|
|
102
|
-
|
|
103
|
-
|
|
197
|
+
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.
|
|
198
|
+
|
|
199
|
+
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.
|
|
200
|
+
|
|
201
|
+
## Images
|
|
202
|
+
|
|
203
|
+
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.
|
|
204
|
+
|
|
205
|
+
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.
|
|
206
|
+
|
|
207
|
+
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.
|
|
208
|
+
|
|
209
|
+
## Links and navigation
|
|
210
|
+
|
|
211
|
+
`<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.
|
|
212
|
+
|
|
213
|
+
## Listing pages and pagination
|
|
214
|
+
|
|
215
|
+
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:
|
|
104
216
|
|
|
105
|
-
### Custom field types
|
|
106
217
|
```ts
|
|
107
|
-
import {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
])
|
|
218
|
+
import { usePages, usePagination } from 'mechanica'
|
|
219
|
+
import { usePostMeta } from '../data/post-meta' // defineData({ id: 'postMeta', props: { date: 'string' } })
|
|
220
|
+
|
|
221
|
+
const latest = usePages({ folderName: 'blog', data: [usePostMeta], sort: { by: 'postMeta.date', dir: 'desc' }, limit: 3 })
|
|
222
|
+
|
|
223
|
+
const blog = usePagination({ folderName: 'blog', data: [usePostMeta], pageSize: 10 })
|
|
224
|
+
// blog.items, blog.page, blog.pageCount, blog.prevPath, blog.nextPath, blog.pathFor(n)
|
|
111
225
|
```
|
|
112
226
|
|
|
113
|
-
|
|
227
|
+
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.
|
|
228
|
+
|
|
229
|
+
## Generated pages
|
|
230
|
+
|
|
231
|
+
To produce many pages from an external source (a product catalog, API docs) without a `.page.md` file each, give the plugin page providers:
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
// vite.config.ts
|
|
235
|
+
import { mechanica, type PageProvider } from 'mechanica/plugin'
|
|
236
|
+
|
|
237
|
+
const products: PageProvider = async () => {
|
|
238
|
+
const items = await fetch('https://api.example.com/products').then((res) => res.json())
|
|
239
|
+
return items.map((item) => ({
|
|
240
|
+
path: `/shop/${item.slug}`,
|
|
241
|
+
content: [{ id: item.slug, blockId: 'product', data: { title: item.title } }],
|
|
242
|
+
data: { head: { title: item.title } },
|
|
243
|
+
}))
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// plugins: [mechanica({ generatePages: [products] }), vue()]
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
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.
|
|
250
|
+
|
|
251
|
+
## Multi-language sites
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
mechanica({ locales: ['en', 'de'] }) // the first locale is the default
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
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.
|
|
258
|
+
|
|
259
|
+
`<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.
|
|
260
|
+
|
|
261
|
+
## Rich text and widgets
|
|
262
|
+
|
|
263
|
+
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.
|
|
264
|
+
|
|
265
|
+
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`.
|
|
266
|
+
|
|
267
|
+
## Block Composer
|
|
268
|
+
|
|
269
|
+
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.
|
|
270
|
+
|
|
271
|
+
`src/composer.ts` hands the composer your design system:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
import { defineComposer } from 'mechanica'
|
|
275
|
+
import UiButton from './components/UiButton.vue'
|
|
276
|
+
|
|
277
|
+
export default defineComposer({
|
|
278
|
+
components: {
|
|
279
|
+
button: { component: UiButton, name: 'Button', props: { label: { type: 'string', default: 'Button' } } },
|
|
280
|
+
},
|
|
281
|
+
classes: { container: { title: 'Container', on: 'frame' } }, // your CSS classes, offered as element styles
|
|
282
|
+
breakpoints: { md: 1024, sm: 640 },
|
|
283
|
+
})
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## SEO
|
|
287
|
+
|
|
288
|
+
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.
|
|
289
|
+
|
|
290
|
+
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.
|
|
291
|
+
|
|
292
|
+
## Code splitting
|
|
293
|
+
|
|
294
|
+
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.
|
|
295
|
+
|
|
296
|
+
## Checking your work visually
|
|
114
297
|
|
|
115
298
|
```bash
|
|
116
|
-
mechanica
|
|
117
|
-
mechanica
|
|
118
|
-
mechanica
|
|
119
|
-
mechanica thumbs
|
|
120
|
-
mechanica push --key <apiKey> --host <url> # upload dist/ to a backend
|
|
299
|
+
npx mechanica shot hero # screenshot one block, from its previewData
|
|
300
|
+
npx mechanica shot /about --width 1440,390 # a whole page, at two widths
|
|
301
|
+
npx mechanica thumbs # page thumbnails for the editor's page browser
|
|
302
|
+
npx mechanica thumbs --blocks # block thumbnails for the palette
|
|
121
303
|
```
|
|
122
304
|
|
|
123
|
-
|
|
305
|
+
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.
|
|
306
|
+
|
|
307
|
+
## CLI
|
|
308
|
+
|
|
309
|
+
| Command | What it does |
|
|
310
|
+
| --- | --- |
|
|
311
|
+
| `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) |
|
|
312
|
+
| `mechanica build` | Build the client and server bundles into `dist/` (`export` runs this for you) |
|
|
313
|
+
| `mechanica shot <blockId \| /path>` | Screenshot a block or a page. Flags: `--width`, `--data`, `--out`, `--full`, `--server`, `--browser` |
|
|
314
|
+
| `mechanica thumbs [--blocks]` | Regenerate page or block thumbnails for the editor |
|
|
315
|
+
| `mechanica images` | Generate image dimensions and blur-up previews for `.mech/assets` (needs `sharp`; `--force` redoes all) |
|
|
316
|
+
| `mechanica push` | Experimental: upload `dist/` to a Mechanica hosting backend |
|
|
317
|
+
|
|
318
|
+
## Plugin options
|
|
319
|
+
|
|
320
|
+
`mechanica(options)` from `mechanica/plugin`; every option is optional.
|
|
321
|
+
|
|
322
|
+
| Option | Default | |
|
|
323
|
+
| --- | --- | --- |
|
|
324
|
+
| `siteUrl`, `siteName` | none | Public origin and name: `{{ site.* }}` templating and SEO output at export |
|
|
325
|
+
| `locales` | none | `['en', 'de']` or `{ default, all, labels }` turns on multi-language pages |
|
|
326
|
+
| `generatePages` | none | Page providers (see above) |
|
|
327
|
+
| `blockChunks` | `'bundled'` | `'per-block'` gives each block its own chunk |
|
|
328
|
+
| `assetsUrl` | none | CDN origin for exported assets. Set Vite's `base` to the same URL |
|
|
329
|
+
| `entry` | `'src/main.ts'` | Module exporting `defineMechanicaApp(…)` |
|
|
330
|
+
| `mount` | `'#app'` | Mount selector |
|
|
331
|
+
| `blocksDir` | `'src/blocks'` | Where blocks are collected from |
|
|
332
|
+
| `widgetsDir` | `'src/widgets'` | Where rich-text widgets are collected from |
|
|
333
|
+
| `composerFile` | `'src/composer.ts'` | The Block Composer manifest |
|
|
334
|
+
| `mechDir` | `'.mech'` | Content directory |
|
|
335
|
+
|
|
336
|
+
## API
|
|
337
|
+
|
|
338
|
+
From `mechanica`:
|
|
339
|
+
|
|
340
|
+
- **App and data:** `defineMechanicaApp`, `defineData`, `defineComposer`
|
|
341
|
+
- **Components:** `<Content>`, `<Layout>`, `<Link>`, `<Image>`
|
|
342
|
+
- **Composables:** `usePages`, `usePagination`, `useFetch`, `useLocale`, `useLayout`, `usePageData` (the current page's path, `meta`, layout and locale), `useRouter`, `useRoute`
|
|
343
|
+
- **Helpers:** `imagePosition`
|
|
344
|
+
|
|
345
|
+
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.
|
|
346
|
+
|
|
347
|
+
## Upgrading from 1.x
|
|
348
|
+
|
|
349
|
+
Mechanica 2 is a rewrite, and a 1.x project has to be ported by hand; there is no automatic migration. What changes:
|
|
350
|
+
|
|
351
|
+
- Vite 8 and `@vitejs/plugin-vue` 6 are required.
|
|
352
|
+
- `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.
|
|
353
|
+
- The plugin is a named import, `import { mechanica } from 'mechanica/plugin'`, and its `entrySsr` option is now `entry`.
|
|
354
|
+
- 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.
|
|
355
|
+
- Pages are `.page.md` files instead of `.mech/pages/**.json`, and JSON pages are ignored. Uploaded files in `.mech/assets/` keep working.
|
|
356
|
+
- `RouterLink` and `SmartLink` are replaced by `<Link>` (whose active class is now `is-active`), and `useQuery` by `usePages`, `usePagination` and `useFetch`.
|
|
357
|
+
- `{{ }}` placeholders in `index.html` are now HTML-escaped.
|
|
358
|
+
|
|
359
|
+
1.x remains installable as `mechanica@1`.
|
|
360
|
+
|
|
361
|
+
## License
|
|
362
|
+
|
|
363
|
+
MIT
|
package/bin/mechanica.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
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",
|