@atom-forge/brickee 0.0.0-stage → 0.0.2

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 (85) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/LICENSE +21 -0
  3. package/README-AI.md +286 -0
  4. package/README.md +41 -2
  5. package/dist/Editor.svelte +1956 -0
  6. package/dist/Editor.svelte.d.ts +19 -0
  7. package/dist/LinkPrompt.svelte +42 -0
  8. package/dist/LinkPrompt.svelte.d.ts +8 -0
  9. package/dist/PluginHost.svelte +41 -0
  10. package/dist/PluginHost.svelte.d.ts +13 -0
  11. package/dist/Preview.svelte +182 -0
  12. package/dist/Preview.svelte.d.ts +32 -0
  13. package/dist/PropsBar.svelte +405 -0
  14. package/dist/PropsBar.svelte.d.ts +17 -0
  15. package/dist/code.d.ts +12 -0
  16. package/dist/code.js +15 -0
  17. package/dist/dom.d.ts +29 -0
  18. package/dist/dom.js +224 -0
  19. package/dist/i18n.d.ts +7 -0
  20. package/dist/i18n.js +18 -0
  21. package/dist/index.d.ts +13 -0
  22. package/dist/index.js +16 -0
  23. package/dist/kit/Empty.svelte +24 -0
  24. package/dist/kit/Empty.svelte.d.ts +8 -0
  25. package/dist/kit/Frame.svelte +76 -0
  26. package/dist/kit/Frame.svelte.d.ts +15 -0
  27. package/dist/kit/util.d.ts +13 -0
  28. package/dist/kit/util.js +28 -0
  29. package/dist/locales/hu.d.ts +2 -0
  30. package/dist/locales/hu.js +142 -0
  31. package/dist/markdown.d.ts +23 -0
  32. package/dist/markdown.js +219 -0
  33. package/dist/model.d.ts +21 -0
  34. package/dist/model.js +45 -0
  35. package/dist/palette.d.ts +19 -0
  36. package/dist/palette.js +23 -0
  37. package/dist/plugins/Gap.svelte +28 -0
  38. package/dist/plugins/Gap.svelte.d.ts +15 -0
  39. package/dist/plugins/GapPreview.svelte +17 -0
  40. package/dist/plugins/GapPreview.svelte.d.ts +5 -0
  41. package/dist/plugins/Map.svelte +47 -0
  42. package/dist/plugins/Map.svelte.d.ts +12 -0
  43. package/dist/plugins/MapPreview.svelte +26 -0
  44. package/dist/plugins/MapPreview.svelte.d.ts +5 -0
  45. package/dist/plugins/Table.svelte +537 -0
  46. package/dist/plugins/Table.svelte.d.ts +28 -0
  47. package/dist/plugins/TablePreview.svelte +62 -0
  48. package/dist/plugins/TablePreview.svelte.d.ts +5 -0
  49. package/dist/plugins/YouTube.svelte +52 -0
  50. package/dist/plugins/YouTube.svelte.d.ts +8 -0
  51. package/dist/plugins/YouTubePreview.svelte +76 -0
  52. package/dist/plugins/YouTubePreview.svelte.d.ts +5 -0
  53. package/dist/plugins/index.d.ts +20 -0
  54. package/dist/plugins/index.js +105 -0
  55. package/dist/plugins/util.d.ts +3 -0
  56. package/dist/plugins/util.js +8 -0
  57. package/dist/schema.d.ts +11 -0
  58. package/dist/schema.js +25 -0
  59. package/dist/scroll.d.ts +18 -0
  60. package/dist/scroll.js +57 -0
  61. package/dist/types.d.ts +132 -0
  62. package/dist/types.js +43 -0
  63. package/dist/view.d.ts +6 -0
  64. package/dist/view.js +11 -0
  65. package/docs/en/bundled-plugins.md +99 -0
  66. package/docs/en/documents-and-markdown.md +114 -0
  67. package/docs/en/editor-and-preview.md +139 -0
  68. package/docs/en/getting-started.md +85 -0
  69. package/docs/en/host-contexts.md +169 -0
  70. package/docs/en/index.md +38 -0
  71. package/docs/en/recipes-and-pitfalls.md +123 -0
  72. package/docs/en/server-side.md +95 -0
  73. package/docs/en/upgrading.md +71 -0
  74. package/docs/en/writing-plugins.md +405 -0
  75. package/docs/hu/bundled-plugins.md +99 -0
  76. package/docs/hu/documents-and-markdown.md +114 -0
  77. package/docs/hu/editor-and-preview.md +139 -0
  78. package/docs/hu/getting-started.md +85 -0
  79. package/docs/hu/host-contexts.md +169 -0
  80. package/docs/hu/index.md +38 -0
  81. package/docs/hu/recipes-and-pitfalls.md +123 -0
  82. package/docs/hu/server-side.md +95 -0
  83. package/docs/hu/upgrading.md +71 -0
  84. package/docs/hu/writing-plugins.md +405 -0
  85. package/package.json +114 -5
@@ -0,0 +1,71 @@
1
+ # Frissítés
2
+
3
+ [← Előző](recipes-and-pitfalls.md) · [Tartalom](index.md)
4
+
5
+ Ez a fejezet azokat a változásokat sorolja fel, amelyek teendőt igényelhetnek, amikor az `@atom-forge/brickee` újabb verziójára váltasz. Minden szakasz egy verziólépést tárgyal, a legújabbal kezdve, és minden változás egy **Teendő:** bekezdéssel zárul.
6
+
7
+ A tőled semmit nem igénylő változások – új exportok, javítások, dokumentációfrissítések – csak a változásnaplóban (`CHANGELOG.md`, a csomaggal együtt szállítva) szerepelnek.
8
+
9
+ ## A 0.0.1-ről a 0.0.2-re
10
+
11
+ ### A képek és a média már nem részei a csomagnak
12
+
13
+ A csomag többé nem ismeri a kép fogalmát. Hogy egy blokk a kép URL-jét vagy egy azonosítót tárol, hogyan választja ki a felhasználó a képet, és hogyan lesz a tárolt értékből cím, azt az alkalmazás saját képpluginja dönti el (lásd [Képek](writing-plugins.md#képek)).
14
+
15
+ | Eltávolítva | Mi volt |
16
+ |---|---|
17
+ | `kind: 'image'`, `kind: 'images'` | Mezőfajták, amelyek egy kiválasztott médiaelem azonosítóját (vagy azonosítóit) tárolták, a beépített képválasztó felugró ablakkal |
18
+ | Az `Editor` és a `Preview` `media` propja | A képek, amelyek közül a blokkok választhattak, és amelyekre hivatkozhattak |
19
+ | `provideMedia`, `useMedia`, `findMedia`, `MediaItem` | A média-kontextus, a keresője és a típusa |
20
+ | A `toMarkdown` `media` beállítása | A `toMarkdown(blocks, { plugins, media })` helyett most `toMarkdown(blocks, { plugins })` |
21
+ | `ctx.media` egy plugin `markdown(props, ctx)` függvényében | A `MarkdownContext` most `{ inner }` |
22
+
23
+ A `PropField['kind']` most `'text' | 'textarea' | 'color' | 'select' | 'slider'`. A tárolt dokumentumokat ez nem érinti: egy blokk propsa megtartja, amit tárolt, az azonosítókat is.
24
+
25
+ **Teendő:** a képpluginjaidban cseréld le az `image` és `images` mezőket saját kiválasztásra — egy gombra a plugin nézetében, amely megnyitja a médiaablakodat vagy egy feltöltést, és az eredményt a `props`-ba írja, vagy egy egyszerű `text` mezőre egy URL-hez —, és tartsd meg a prop-kulcsokat, hogy a tárolt dokumentumok továbbra is betöltődjenek. Töröld a `media` propot a szerkesztőidről és előnézeteidről, és a `media` beállítást a `toMarkdown`-hívásaidból. Ahol egy nézet, előnézet vagy `markdown` alak a `useMedia`, `findMedia` vagy `ctx.media` segítségével alakított címmé egy azonosítót, ott az alkalmazásod saját adatában keresd ki: az alkalmazásod egy moduljában, vagy egy saját Svelte-kontextusban, amelyet a szerkesztő és az előnézet fölött adsz meg (a `markdown` komponenseken kívül fut, ezért modulbeli adat kell neki). Ha továbbra is szükséged van rá, definiálj egy `MediaItem`-szerű típust az alkalmazásodban.
26
+
27
+ ## A `block-editor` munkaterület-csomagról a 0.0.1-es változatra
28
+
29
+ Ez a szakasz csak azoknak az alkalmazásoknak szól, amelyek már azelőtt használták a szerkesztőt, hogy ebből a csomagból lett volna.
30
+
31
+ A szerkesztő `block-editor` néven, egy alkalmazáson belüli munkaterület-csomagként indult. Ugyanazzal a tárolt alakkal költözött ide `@atom-forge/brickee` néven, így az elmentett dokumentumokat nem kell átalakítani. Az import-azonosítók megváltoztak, a plugin-kit a pluginok belépési pontjából átkerült a magba, az oldalspecifikus pluginok pedig az alkalmazásnál maradtak.
32
+
33
+ ### Csomagnév és belépési pontok
34
+
35
+ | Előtte | Utána |
36
+ |---|---|
37
+ | `block-editor` | `@atom-forge/brickee` |
38
+ | `block-editor/plugins` | `@atom-forge/brickee/plugins` |
39
+ | `block-editor/locales/hu` | `@atom-forge/brickee/locales/hu` |
40
+
41
+ **Teendő:** cseréld le a függőséget (`"block-editor": "workspace:*"` → `"@atom-forge/brickee"`), és nevezd át az importokat. Telepítsd az alkalmazás függőségeként az `@atom-forge/ui`-t és a `@lucide/svelte`-et, ha még nincsenek meg, valamint a `zod`-ot, ha a sémát használod.
42
+
43
+ ### A kit a magban van
44
+
45
+ A `Frame`, az `Empty` és a plugin-segédfüggvények a mag belépési pontjából exportálódnak. A `safeColor`, a `viewValues`, a `baseValue` és a `lines` már nem exportálódik a pluginok belépési pontjából.
46
+
47
+ ```ts
48
+ // Előtte
49
+ import { safeColor, viewValues } from 'block-editor/plugins';
50
+
51
+ // Utána
52
+ import { safeColor, viewValues } from '@atom-forge/brickee';
53
+ ```
54
+
55
+ **Teendő:** a `Frame`, `Empty`, `safeColor`, `viewValues`, `baseValue`, `lines`, `mdText`, `mdJoin` és `mdQuote` elemeket az `@atom-forge/brickee`-ből importáld. A megmaradt mellékelt pluginok segédfüggvényei (`youtubeId`, `mapSrc`, `gapLines`, `tableHeads`…) az `@atom-forge/brickee/plugins`-ban maradnak.
56
+
57
+ ### Az oldalspecifikus pluginok kikerültek a csomagból
58
+
59
+ A csomag mostantól csak négy általános célú plugint tartalmaz: `table`, `youtube`, `map` és `gap` (a `PLUGINS` ezt a négyet tartalmazza). A `block-editor/plugins` többi pluginja és a segédfüggvényeik alkalmazásspecifikusak voltak, és nem részei az `@atom-forge/brickee`-nek:
60
+
61
+ | Eltávolítva | Fajta |
62
+ |---|---|
63
+ | `richBox`, `feature`, `sideBox`, `callout`, `spoiler`, `faq`, `imageText` | tároló pluginok |
64
+ | `image`, `gallery`, `tweet`, `form`, `banner`, `locked` | pluginok |
65
+ | `CALLOUTS`, `calloutTone`, `CalloutTone`, `questions`, `formEmbed`, `formHeight` | a segédfüggvényeik |
66
+ | `postId`, `userName`, `RATIO_MIN`, `RATIO_MAX`, `ratioPercent`, `ratio` | a korábbi `plugins/util` segédfüggvényei |
67
+ | `ImageProps`, `GalleryProps`, `CalloutProps` és e pluginok többi props-típusa | típusok |
68
+
69
+ A tárolt alak nem változott: az ilyen blokktípusokat tartalmazó dokumentumok érvényesek maradnak, amíg az alkalmazás ugyanazokkal a `type` értékekkel ad át pluginokat a szerkesztőnek és az előnézetnek.
70
+
71
+ **Teendő:** ha az alkalmazás ezek közül bármelyik plugint használja, tartsd meg belőlük a saját másolatodat (a nézeteiket, előnézeteiket és segédfüggvényeiket) az alkalmazásban, a mag kitjére építve, és vedd fel őket az alkalmazás pluginlistájába ugyanazzal a `type`-pal. A megmaradt négy plugint és a segédfüggvényeiket a korábbiakhoz hasonlóan az `@atom-forge/brickee/plugins`-ból importáld. A lemásolt pluginok felületi szövegeit add hozzá a `messages`-hez: az `@atom-forge/brickee/locales/hu` már nem tartalmazza őket.
@@ -0,0 +1,405 @@
1
+ # Pluginok írása
2
+
3
+ [← Előző](bundled-plugins.md) · [Tartalom](index.md) · [Következő →](host-contexts.md)
4
+
5
+ Egy plugin egy `BlockPlugin` objektum és két Svelte-komponens: egy **nézet** a szerkesztőhöz és egy **előnézet** az oldalhoz. Mindent, amire egy pluginnak szüksége van, az `@atom-forge/brickee` exportál — a mellékelt pluginok sem használnak mást.
6
+
7
+ ## Felépítés
8
+
9
+ | Tag | Típus | Jelentés |
10
+ |---|---|---|
11
+ | `type` | `string` | A tárolt `type`. Nem kezdődhet `md:`-vel; hogy átmenjen a sémán, illeszkednie kell a `^[a-z][a-z0-9:-]{0,39}$` mintára. |
12
+ | `label`, `hint` | `string` | Név és rövid leírás a perjeles és az „Átalakítás” menüben. |
13
+ | `keys` | `string` | További keresőszavak a perjeles menühöz. |
14
+ | `icon` | `Icon` | Egy `@lucide/svelte` ikonkomponens. |
15
+ | `props` | `() => P` | Egy új blokk propsai. |
16
+ | `fields` | `PropField[]` | A blokk tetején lévő beállítások (opcionális). |
17
+ | `tag` | `(props: P) => string` | A blokk tetején, a mezői előtt álló kis címke. A ` · `-tal elválasztott részeit egyenként fordítja le. |
18
+ | `content` | `'none' \| 'text' \| 'plain' \| 'blocks'` | Mit tartalmaz a blokk. A pluginok `'none'`-t (csak props) vagy `'blocks'`-ot (beágyazott dokumentum) használnak; ez dönti el, mit kínál az „Átalakítás” menü. |
19
+ | `view` | `Component<PluginViewProps>` | A blokk a szerkesztőben. |
20
+ | `preview` | `Component<PluginPreviewProps>` | A blokk az oldalon (opcionális). Nélküle a `Preview` semmit sem renderel a blokkhoz — saját megjelenítővel dolgozó alkalmazásoknak. |
21
+ | `accepts` | `string[]` | Egy tároló blokk megengedett beágyazott blokkjai, palettaazonosítókként; az `'md:*'` az összes beépített blokk. Alapértelmezés: minden, amit az alkalmazás kínál. |
22
+ | `markdown` | `(props: P, ctx: MarkdownContext) => string` | A markdown alakja (opcionális). |
23
+ | `unique` | `boolean` | Dokumentumonként legfeljebb egy, csak a legfelső szinten. |
24
+ | `gate` | `boolean` | Az utána következő blokkok védettek; az előnézet megjelöli őket. |
25
+
26
+ A `P` a plugin props-típusa; `Record<string, unknown>`-t kiterjesztő interfészként deklaráld.
27
+
28
+ ## Plugin tartalom nélkül
29
+
30
+ Egy linkgomb: felirat, link, méret és szín — mind mezőkben állítható, magába a blokkba semmit sem kell gépelni.
31
+
32
+ ```ts
33
+ // safe-url.ts — csak azok a linkek, amelyeket az oldal megjeleníthet
34
+ export const safeUrl = (u: unknown) => {
35
+ const s = String(u ?? '').trim();
36
+ return /^(https?:|mailto:|\/(?!\/))/.test(s) ? s : '';
37
+ };
38
+ ```
39
+
40
+ ```svelte
41
+ <!-- ButtonView.svelte: a blokk a szerkesztőben -->
42
+ <script lang="ts" module>
43
+ export interface ButtonProps extends Record<string, unknown> {
44
+ label: string;
45
+ url: string;
46
+ size: 'normal' | 'large';
47
+ color: string;
48
+ }
49
+ </script>
50
+
51
+ <script lang="ts">
52
+ import { MousePointerClick } from '@lucide/svelte';
53
+ import { Empty, safeColor, type PluginViewProps } from '@atom-forge/brickee';
54
+
55
+ // Mindkettő bindable, akkor is, ha a nézet nem használja őket: a szerkesztő minden plugin-nézeten bindolja őket.
56
+ let { props = $bindable(), blocks = $bindable() }: PluginViewProps<ButtonProps> = $props();
57
+ </script>
58
+
59
+ {#if props.label}
60
+ <span class="button" style:background={safeColor(props.color, '#2563eb')}>{props.label}</span>
61
+ {:else}
62
+ <Empty icon={MousePointerClick} text="Set the label and the link above" />
63
+ {/if}
64
+
65
+ <style>
66
+ .button {
67
+ display: inline-block;
68
+ padding: 0.5em 1.2em;
69
+ border-radius: 8px;
70
+ color: #fff;
71
+ }
72
+ </style>
73
+ ```
74
+
75
+ ```svelte
76
+ <!-- ButtonPreview.svelte: a blokk az oldalon -->
77
+ <script lang="ts">
78
+ import { safeColor, type PluginPreviewProps } from '@atom-forge/brickee';
79
+ import type { ButtonProps } from './ButtonView.svelte';
80
+ import { safeUrl } from './safe-url';
81
+
82
+ let { props }: PluginPreviewProps<ButtonProps> = $props();
83
+ const href = $derived(safeUrl(props.url));
84
+ </script>
85
+
86
+ {#if href && props.label}
87
+ <p>
88
+ <a class="button" class:large={props.size === 'large'} {href} style:background={safeColor(props.color, '#2563eb')}>
89
+ {props.label}
90
+ </a>
91
+ </p>
92
+ {/if}
93
+ ```
94
+
95
+ ```ts
96
+ // button.ts: a plugin
97
+ import { MousePointerClick } from '@lucide/svelte';
98
+ import { mdText, type BlockPlugin } from '@atom-forge/brickee';
99
+ import ButtonView, { type ButtonProps } from './ButtonView.svelte';
100
+ import ButtonPreview from './ButtonPreview.svelte';
101
+ import { safeUrl } from './safe-url';
102
+
103
+ export const button: BlockPlugin<ButtonProps> = {
104
+ type: 'button',
105
+ label: 'Button',
106
+ hint: 'A link as a button',
107
+ keys: 'button link cta',
108
+ icon: MousePointerClick,
109
+ props: () => ({ label: '', url: '', size: 'normal', color: '#2563eb' }),
110
+ fields: [
111
+ { key: 'label', label: 'Label', kind: 'text' },
112
+ { key: 'url', label: 'Link', kind: 'text', placeholder: 'https://…', parse: (s) => s.trim() },
113
+ {
114
+ key: 'size',
115
+ label: 'Size',
116
+ kind: 'select',
117
+ options: [
118
+ { value: 'normal', label: 'Normal' },
119
+ { value: 'large', label: 'Large' }
120
+ ]
121
+ },
122
+ { key: 'color', label: 'Colour', kind: 'color' }
123
+ ],
124
+ tag: (p) => (p.size === 'large' ? 'button · large' : 'button'),
125
+ content: 'none',
126
+ markdown: (p) => {
127
+ const href = safeUrl(p.url);
128
+ return href ? `[${mdText(p.label) || href}](${href})` : '';
129
+ },
130
+ view: ButtonView,
131
+ preview: ButtonPreview
132
+ };
133
+ ```
134
+
135
+ Regisztráld a többi mellé:
136
+
137
+ ```svelte
138
+ <Editor bind:value plugins={[...PLUGINS, button]} />
139
+ <Preview blocks={value} plugins={[...PLUGINS, button]} />
140
+ ```
141
+
142
+ > **A props a felhasználóktól jön**
143
+ >
144
+ > Egy plugin propsa az, ami el lett tárolva — amit egy szerkesztő begépelt, beillesztett, vagy amit bárki, aki dokumentumot menthet, elküldött az API-dnak. A séma csak az alakot ellenőrzi. Minden előnézetnek megbízhatatlanként kell kezelnie a propsot: a színeket a `safeColor`-on keresztül vedd át, ellenőrizd a linkeket és a képek címét, és korlátozd a számokat. A Svelte escape-eli a `{...}`-ban lévő szöveget; propot soha ne tegyél `{@html}`-be.
145
+
146
+ ## Mezők
147
+
148
+ Minden `PropField` egy beállítás a blokk tetején. A szerkesztő rajzolja ki őket, és az értéküket a `props`-ban tárolja.
149
+
150
+ | Tag | Jelentés |
151
+ |---|---|
152
+ | `key` | A prop, amelyet szerkeszt. |
153
+ | `label` | A címkéje (a `messages`-en keresztül fordítva). |
154
+ | `kind` | `text`, `textarea`, `color`, `select`, `slider` vagy `toggles`. |
155
+ | `placeholder` | Üres szövegmező tippje; opcionális színnél az a szín, amelyről a „none” gombra kattintás indul. |
156
+ | `options` | `{ value, label }[]` a `select`-hez, valamint az értékeken lépkedő `slider`-hez. Egy opció címkéje tartalmazhat `{n}`-t, amelyet az értéke tölt ki. A `toggles`-hez `{ value, label, icon }[]`: az `icon` a gomb, a `label` a buboréksúgója. |
157
+ | `min`, `max`, `step`, `unit` | Számcsúszka (`slider`): a tartománya, a lépésköze (alapértelmezés 1) és az érték után mutatott mértékegység. |
158
+ | `none` | A beállítatlan állapot címkéje: számcsúszkánál a `min`-től balra lévő rovátka, opcionális színnél a „none” gomb. |
159
+ | `views` | Nézetenkénti értékek egy `slider`-hez (lásd [Nézetenkénti értékek](host-contexts.md#nézetenkénti-értékek)). |
160
+ | `format` | A mező szövege a tárolt értékből (alapértelmezés: az érték szövegként). |
161
+ | `parse` | A tárolt érték a mező szövegéből (alapértelmezés: maga a szöveg). |
162
+
163
+ Az egyes fajták így tárolják az értéküket:
164
+
165
+ | Fajta | Tárolt érték |
166
+ |---|---|
167
+ | `text`, `textarea` | A szöveg, vagy amit a `parse` visszaad (a `parse: lines` sorok listáját tárolja). |
168
+ | `select` | A kiválasztott opció `value`-ja. |
169
+ | `slider` `options`-szel | A csúszka pozíciójában lévő opció értéke. |
170
+ | `slider` `min`-nel és `max`-szal | A szám szövegként (például `'56'`); a `none` rovátka eltávolítja a kulcsot. |
171
+ | `color` | Egy `#rrggbb` szöveg; `none` esetén a törlése eltávolítja a kulcsot. |
172
+ | `toggles` | A bekapcsolt opciók listája az `options` sorrendjében (ha egy sincs, `[]`); ilyen a beépített táblázat fejléce és lábléce. |
173
+
174
+ Az üres érték (`''`, `null` vagy `undefined`, akkor is, ha a `parse` adja vissza) eltávolítja a kulcsot a `props`-ból, ezért az előnézetnek mindig kezelnie kell a hiányzó propot. A mező `parse`-a jó hely a bemenet normalizálására is: a mellékelt YouTube-mező bármilyen YouTube-linket elfogad, de csak a videó azonosítóját tárolja.
175
+
176
+ ## A nézet
177
+
178
+ A nézet `PluginViewProps`-ot kap:
179
+
180
+ | Prop | Jelentés |
181
+ |---|---|
182
+ | `props` | A blokk propsa (bindable): a nézet helyben is szerkesztheti, ahogy a táblázat teszi. |
183
+ | `blocks` | A beágyazott dokumentum (bindable); tartalom nélküli pluginnál mindig `[]`. |
184
+ | `readonly` | A szerkesztő csak olvasható. |
185
+ | `palette`, `plugins` | Az alkalmazás palettája (már az `accepts`-re szűkítve) és pluginjai, egy beágyazott szerkesztőhöz. |
186
+
187
+ A `props`-ot és a `blocks`-ot akkor is `$bindable()`-lel deklaráld, ha a nézet nem használja őket. A mezők automatikusan a nézet fölé kerülnek; a nézetnek csak a blokkot kell megjelenítenie. Az `Empty` (`icon` és `text` propokkal) az a szaggatott helyőrző, amelyet a mellékelt pluginok mutatnak, amíg a propsuk nincs beállítva; a szövegét lefordítja.
188
+
189
+ ## Az előnézet
190
+
191
+ Az előnézet `PluginPreviewProps`-ot kap: `props`, `blocks` és `plugins`. Ez egy egyszerű Svelte-komponens, amely az oldal számára rendereli a blokkot. A szövegek és a nézet a kontextusokon keresztül érhető el (`useText`, `useView`; lásd [Az alkalmazás kontextusai](host-contexts.md)); a képek a plugin saját ügyei (lásd [Képek](#képek)).
192
+
193
+ ## Tárolók
194
+
195
+ A `content: 'blocks'` beállítású plugin beágyazott dokumentumot tart a `blocks`-ban. A nézete a `Frame`-et használja, egy vékony dobozt, amely a tetején a blokk mezőit, alatta egy beágyazott szerkesztőt rajzol; az előnézete egy beágyazott `Preview`-val rendereli a belső blokkokat.
196
+
197
+ ```svelte
198
+ <!-- NoteView.svelte -->
199
+ <script lang="ts" module>
200
+ export interface NoteProps extends Record<string, unknown> {
201
+ title: string;
202
+ }
203
+ </script>
204
+
205
+ <script lang="ts">
206
+ import { Frame, useText, type PluginViewProps } from '@atom-forge/brickee';
207
+
208
+ let { props = $bindable(), blocks = $bindable(), readonly, palette, plugins }: PluginViewProps<NoteProps> = $props();
209
+ const tr = useText();
210
+ </script>
211
+
212
+ <Frame bind:blocks {readonly} {palette} {plugins} heading={props.title} empty={tr('Note')} placeholder={tr('Write the note…')} />
213
+ ```
214
+
215
+ ```svelte
216
+ <!-- NotePreview.svelte -->
217
+ <script lang="ts">
218
+ import { Preview, type PluginPreviewProps } from '@atom-forge/brickee';
219
+ import type { NoteProps } from './NoteView.svelte';
220
+
221
+ let { props, blocks, plugins }: PluginPreviewProps<NoteProps> = $props();
222
+ </script>
223
+
224
+ <aside class="note">
225
+ {#if props.title}<p class="note-title">{props.title}</p>{/if}
226
+ <Preview {blocks} {plugins} nested />
227
+ </aside>
228
+ ```
229
+
230
+ ```ts
231
+ // note.ts
232
+ import { StickyNote } from '@lucide/svelte';
233
+ import { mdJoin, mdQuote, mdText, type BlockPlugin } from '@atom-forge/brickee';
234
+ import NoteView, { type NoteProps } from './NoteView.svelte';
235
+ import NotePreview from './NotePreview.svelte';
236
+
237
+ export const note: BlockPlugin<NoteProps> = {
238
+ type: 'note',
239
+ label: 'Note',
240
+ hint: 'Boxed text with a title',
241
+ keys: 'note aside box',
242
+ icon: StickyNote,
243
+ props: () => ({ title: '' }),
244
+ fields: [{ key: 'title', label: 'Title', kind: 'text' }],
245
+ tag: () => 'note',
246
+ content: 'blocks',
247
+ accepts: ['md:p', 'md:ul', 'md:ol', 'youtube'],
248
+ markdown: (p, { inner }) => mdQuote(mdJoin(p.title && `**${mdText(p.title)}**`, inner)),
249
+ view: NoteView,
250
+ preview: NotePreview
251
+ };
252
+ ```
253
+
254
+ A `Frame` propsai: `blocks` (bindable), `readonly`, `palette`, `plugins`, és opcionálisan `placeholder` a beágyazott szerkesztőhöz. A `heading`, az `empty` és az `image` (egy URL) címsort hoz létre, amely csak akkor látszik, ha a mezők nem (csak olvasható módban, vagy ha nincsenek mezők).
255
+
256
+ Hogyan működik a beágyazás:
257
+
258
+ - **Egy szint.** A beágyazott szerkesztő soha nem kínál olyan blokkot, amely maga is blokkokat tartalmaz, sem egyedi blokkot. A séma elutasítja a mélyebb dokumentumokat.
259
+ - Az **`accepts`** a felsorolt palettaazonosítókra szűkíti azt, amit a beágyazott szerkesztő kínál, az alkalmazás sorrendjében. Az `'md:*'` az összes beépített blokkot jelenti, így a `['md:*', 'youtube']` jelentése „szöveg és videók”. `accepts` nélkül a beágyazott szerkesztő mindent kínál, amit az alkalmazás (a tárolók és az egyedi blokkok kivételével).
260
+ - Az alkalmazás `palette`-je továbbra is érvényes: az `accepts` csak szűkítheti, soha nem bővítheti.
261
+ - Blokkok tárolóba csomagolását az „Átalakítás” menüből, illetve egy tároló másikká alakítását csak akkor kínálja fel, ha a tároló minden bekerülő blokkot elfogad.
262
+ - A `blocksSchemaFor(plugins)` a szerveren ellenőrzi az `accepts`-et (lásd [A szerveren](server-side.md)).
263
+
264
+ ## Egyedi és kapus blokkok
265
+
266
+ - **`unique: true`** — dokumentumonként legfeljebb egy, a legfelső szinten. Amint a dokumentumban van egy, a plugin eltűnik a perjeles és az „Átalakítás” menüből; beágyazott szerkesztő soha nem kínálja.
267
+ - **`gate: true`** — az első legfelső szintű kapus blokk utáni blokkok védettek. A `Preview` egy „For supporters only” jelölésű, szaggatott vonallal elválasztott szakaszban rendereli őket; a `markGated={false}` kikapcsolja a jelölést. A beágyazott előnézetek soha nem osztódnak ketté.
268
+
269
+ Mindkettőre tipikus példa egy csak támogatóknak szóló elválasztó: dokumentumonként egy, mögötte a védett tartalommal.
270
+
271
+ > **A kapu csak jelöl**
272
+ >
273
+ > A `gate` semmit sem rejt el: az előnézet továbbra is minden blokkot renderel, és a tárolt dokumentum is mindet tartalmazza. Annak eldöntése, hogy ki olvashatja a védett részt — és hogy mindenki más elől kimaradjon az oldalról —, az alkalmazás feladata, a szerveren (lásd [Védett tartalom](server-side.md#védett-tartalom)). A `unique`-ot a séma sem kényszeríti ki.
274
+
275
+ ## Markdown alak
276
+
277
+ A `markdown(props, { inner })` markdownként adja vissza a blokkot; ezt használja a szerkesztő blokkok másolásakor, és a `toMarkdown`. Az `inner` a beágyazott blokkok, már markdownként (`''`, ha nincsenek).
278
+
279
+ `''` visszaadásával kihagyod a blokkot. Azoknak a pluginoknak, amelyek kimenetének szövegként nincs értelme (hirdetéshely, a mellékelt `gap`), egyszerűen nincs markdown alakjuk. Három segédfüggvény gondoskodik a jól formált kimenetről:
280
+
281
+ | Segédfüggvény | Mit csinál |
282
+ |---|---|
283
+ | `mdText(value)` | Markdownban biztonságos egyszerű szöveg: escape-eli a soron belüli jelöléseket és a `#`-et, összevonja a sorokat. |
284
+ | `mdJoin(...parts)` | Különálló blokkokként fűzi össze a részeket (köztük üres sorral), kihagyva az üreseket és a hamis értékűeket. |
285
+ | `mdQuote(md)` | Minden sor elé `> `-t tesz, idézetblokként renderelt dobozhoz. |
286
+
287
+ ## Képek
288
+
289
+ A szerkesztő nem ismeri a kép fogalmát: nincs képmező, nincs képválasztó, nincs médialista. Hogy egy blokk a kép URL-jét vagy az alkalmazás saját médiatárában lévő kép azonosítóját tárolja, hogyan választja ki a felhasználó, és hogyan lesz a tárolt értékből cím, az az alkalmazás döntése, így a képblokk az alkalmazás egy közönséges pluginja.
290
+
291
+ Egy minimális `photo` plugin a kép címét tárolja, amelyet egy szövegmezőbe kell beírni, mellette alternatív szöveggel és magasságcsúszkával:
292
+
293
+ ```svelte
294
+ <!-- PhotoView.svelte -->
295
+ <script lang="ts" module>
296
+ export interface PhotoProps extends Record<string, unknown> {
297
+ /** A kép címe. */
298
+ url: string;
299
+ alt: string;
300
+ /** A magasság a szélesség %-ában (20–150), vagy beállítatlan a kép saját alakjához. */
301
+ ratio?: unknown;
302
+ }
303
+
304
+ /** A tárolt cím, ha http(s) vagy az oldalon belüli, különben ''. */
305
+ export const imageSrc = (u: unknown) => {
306
+ const s = String(u ?? '').trim();
307
+ return /^(https?:|\/(?!\/))/.test(s) ? s : '';
308
+ };
309
+
310
+ /** CSS aspect-ratio egy tárolt magasságból, vagy undefined. */
311
+ export const aspect = (v: unknown) => {
312
+ const n = Number(v);
313
+ return n >= 20 && n <= 150 ? `100 / ${n}` : undefined;
314
+ };
315
+ </script>
316
+
317
+ <script lang="ts">
318
+ import { ImageIcon } from '@lucide/svelte';
319
+ import { Empty, type PluginViewProps } from '@atom-forge/brickee';
320
+
321
+ let { props = $bindable(), blocks = $bindable() }: PluginViewProps<PhotoProps> = $props();
322
+ const src = $derived(imageSrc(props.url));
323
+ </script>
324
+
325
+ {#if src}
326
+ <img {src} alt={props.alt} style="max-width: 100%" />
327
+ {:else}
328
+ <Empty icon={ImageIcon} text="Set the image's address above" />
329
+ {/if}
330
+ ```
331
+
332
+ ```svelte
333
+ <!-- PhotoPreview.svelte -->
334
+ <script lang="ts">
335
+ import { baseValue, type PluginPreviewProps } from '@atom-forge/brickee';
336
+ import { aspect, imageSrc, type PhotoProps } from './PhotoView.svelte';
337
+
338
+ let { props }: PluginPreviewProps<PhotoProps> = $props();
339
+ const src = $derived(imageSrc(props.url));
340
+ </script>
341
+
342
+ {#if src}
343
+ <img {src} alt={props.alt ?? ''} style:aspect-ratio={aspect(baseValue(props.ratio))} />
344
+ {/if}
345
+
346
+ <style>
347
+ img {
348
+ width: 100%;
349
+ object-fit: cover;
350
+ }
351
+ </style>
352
+ ```
353
+
354
+ ```ts
355
+ // photo.ts
356
+ import { ImageIcon } from '@lucide/svelte';
357
+ import { mdText, type BlockPlugin } from '@atom-forge/brickee';
358
+ import PhotoView, { type PhotoProps } from './PhotoView.svelte';
359
+ import PhotoPreview from './PhotoPreview.svelte';
360
+
361
+ export const photo: BlockPlugin<PhotoProps> = {
362
+ type: 'photo',
363
+ label: 'Photo',
364
+ hint: 'One image',
365
+ keys: 'photo image picture',
366
+ icon: ImageIcon,
367
+ props: () => ({ url: '', alt: '' }),
368
+ fields: [
369
+ { key: 'url', label: 'Image', kind: 'text', placeholder: 'https://…', parse: (s) => s.trim() },
370
+ { key: 'alt', label: 'Alt text', kind: 'text', placeholder: 'What the image shows' },
371
+ { key: 'ratio', label: 'Height', kind: 'slider', min: 20, max: 150, unit: '%', none: 'Original' }
372
+ ],
373
+ tag: () => 'photo',
374
+ content: 'none',
375
+ markdown: (p) => {
376
+ const src = imageSrc(p.url);
377
+ return src ? `![${mdText(p.alt)}](${src})` : '';
378
+ },
379
+ view: PhotoView,
380
+ preview: PhotoPreview
381
+ };
382
+ ```
383
+
384
+ A cím ugyanolyan prop, mint a többi, tehát megbízhatatlan: a nézet és az előnézet csak akkor rendereli, ha `http(s)` vagy az oldalon belüli cím, ahogy a gomb is ellenőrzi a linkjét.
385
+
386
+ Más alkalmazások másként döntenek, de mindezt a pluginon belül:
387
+
388
+ - **Médiatár.** A blokk URL helyett az alkalmazás médiatárában lévő kép azonosítóját tárolja. A nézet, az előnézet és a `markdown` az alkalmazás saját keresésével alakítja címmé az azonosítót: az alkalmazás egy moduljával, vagy egy saját Svelte-kontextussal, amelyet a szerkesztő és az előnézet fölött ad meg (a plugin-komponensek ezek alatt renderelődnek). A `markdown` komponenseken kívül fut — blokkok másolásakor és a `toMarkdown`-ban —, ezért csak modulbeli adatot használhat.
389
+ - **Kiválasztás.** Szövegmező helyett a nézet saját választót is mutathat — egy gombot, amely megnyitja az alkalmazás médiaablakát vagy egy feltöltést —, és az eredményt a `props`-ba írja, amely kötött (bindable).
390
+ - **Több kép.** Egy galéria egyetlen propban tárol egy listát (URL-ekből vagy azonosítókból), és ugyanígy a nézetében szerkeszti.
391
+
392
+ ## A kit
393
+
394
+ | Export | Fajta | Használat |
395
+ |---|---|---|
396
+ | `Frame` | komponens | Egy tároló szerkesztőbeli kinézete: felül a mezők, alatta a beágyazott szerkesztő. |
397
+ | `Empty` | komponens | Olyan blokk helyőrzője, amelynek a propsa még nincs beállítva. |
398
+ | `safeColor(value, fallback)` | függvény | Egy `#hex` szín (3–8 számjegy), vagy a tartalék érték. |
399
+ | `lines(text)` | függvény | Levágott, nem üres sorok: a mező `parse`-a „soronként egy” listákhoz. |
400
+ | `baseValue(value)` | függvény | Egy nézetenkénti prop alapértéke. |
401
+ | `viewValues(value, keys)` | függvény | Egy nézetenkénti prop értéke minden nézetre. |
402
+ | `mdText`, `mdJoin`, `mdQuote` | függvények | Markdown alakok (fent). |
403
+ | `useText` | függvény | Lefordítja a plugin saját felületi szövegeit. |
404
+ | `useView` | függvény | Az alkalmazás által éppen mutatott nézet. |
405
+ | `Preview` | komponens | Egy tároló beágyazott blokkjait rendereli (`nested`-del). |
package/package.json CHANGED
@@ -1,6 +1,115 @@
1
1
  {
2
- "name": "@atom-forge/brickee",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@atom-forge/brickee",
3
+ "version": "0.0.2",
4
+ "author": {
5
+ "name": "Gergely Laborci",
6
+ "url": "https://github.com/laborci"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/atom-forge/brickee.git"
11
+ },
12
+ "description": "A Svelte 5 block editor: markdown blocks plus plugins, stored as JSON",
13
+ "license": "MIT",
14
+ "scripts": {
15
+ "dev": "vite dev",
16
+ "build": "vite build && bun run prepack",
17
+ "preview": "vite preview",
18
+ "prepare": "svelte-kit sync || echo ''",
19
+ "prepack": "(svelte-kit sync || true) && svelte-package && publint",
20
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
21
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
22
+ "package": "svelte-package",
23
+ "prepublishOnly": "bun run prepack",
24
+ "pub-check": "ship-it check",
25
+ "pub": "ship-it",
26
+ "pub-validate": "ship-it validate"
27
+ },
28
+ "ship": {
29
+ "verify": [
30
+ "bun run check",
31
+ "bun run prepack",
32
+ "npm pack --dry-run --ignore-scripts"
33
+ ]
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "!dist/**/*.test.*",
38
+ "!dist/**/*.spec.*",
39
+ "README.md",
40
+ "README-AI.md",
41
+ "docs",
42
+ "LICENSE",
43
+ "CHANGELOG.md"
44
+ ],
45
+ "svelte": "./dist/index.js",
46
+ "types": "./dist/index.d.ts",
47
+ "type": "module",
48
+ "sideEffects": [
49
+ "**/*.css"
50
+ ],
51
+ "exports": {
52
+ ".": {
53
+ "types": "./dist/index.d.ts",
54
+ "svelte": "./dist/index.js",
55
+ "default": "./dist/index.js"
56
+ },
57
+ "./plugins": {
58
+ "types": "./dist/plugins/index.d.ts",
59
+ "svelte": "./dist/plugins/index.js",
60
+ "default": "./dist/plugins/index.js"
61
+ },
62
+ "./schema": {
63
+ "types": "./dist/schema.d.ts",
64
+ "default": "./dist/schema.js"
65
+ },
66
+ "./model": {
67
+ "types": "./dist/model.d.ts",
68
+ "default": "./dist/model.js"
69
+ },
70
+ "./locales/*": {
71
+ "types": "./dist/locales/*.d.ts",
72
+ "default": "./dist/locales/*.js"
73
+ }
74
+ },
75
+ "peerDependencies": {
76
+ "@atom-forge/ui": ">=0.0.37",
77
+ "@lucide/svelte": ">=1.0.0",
78
+ "svelte": "^5.0.0",
79
+ "zod": "^4.0.0"
80
+ },
81
+ "peerDependenciesMeta": {
82
+ "zod": {
83
+ "optional": true
84
+ }
85
+ },
86
+ "devDependencies": {
87
+ "@atom-forge/ship": "0.2.0",
88
+ "@atom-forge/ui": "^0.0.37",
89
+ "@lucide/svelte": "^1.59.0",
90
+ "@sveltejs/adapter-static": "^4.0.1",
91
+ "@sveltejs/kit": "^2.50.2",
92
+ "@sveltejs/package": "^2.5.7",
93
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
94
+ "@tailwindcss/forms": "^0.5.11",
95
+ "@tailwindcss/typography": "^0.5.20",
96
+ "@tailwindcss/vite": "^4.3.5",
97
+ "publint": "^0.3.15",
98
+ "svelte": "^5.57.1",
99
+ "svelte-check": "^4.4.2",
100
+ "tailwindcss": "^4.3.5",
101
+ "typescript": "^5.9.3",
102
+ "vite": "^7.3.1",
103
+ "zod": "^4.6.7"
104
+ },
105
+ "keywords": [
106
+ "svelte",
107
+ "block-editor",
108
+ "markdown",
109
+ "editor"
110
+ ],
111
+ "publishConfig": {
112
+ "access": "public",
113
+ "registry": "https://registry.npmjs.org"
114
+ }
115
+ }