@atom-forge/brickee 0.0.1 → 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 (49) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/README-AI.md +24 -21
  3. package/dist/Editor.svelte +52 -64
  4. package/dist/Editor.svelte.d.ts +0 -3
  5. package/dist/LinkPrompt.svelte +3 -3
  6. package/dist/Preview.svelte +1 -5
  7. package/dist/Preview.svelte.d.ts +0 -3
  8. package/dist/PropsBar.svelte +120 -110
  9. package/dist/PropsBar.svelte.d.ts +0 -3
  10. package/dist/code.d.ts +12 -0
  11. package/dist/code.js +15 -0
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +1 -1
  14. package/dist/kit/Empty.svelte +2 -2
  15. package/dist/kit/Frame.svelte +3 -3
  16. package/dist/locales/hu.js +2 -7
  17. package/dist/markdown.d.ts +0 -2
  18. package/dist/markdown.js +1 -1
  19. package/dist/plugins/Gap.svelte +1 -1
  20. package/dist/plugins/Map.svelte +1 -1
  21. package/dist/plugins/Table.svelte +36 -62
  22. package/dist/plugins/TablePreview.svelte +2 -2
  23. package/dist/plugins/YouTubePreview.svelte +1 -1
  24. package/dist/plugins/index.js +12 -1
  25. package/dist/schema.js +1 -1
  26. package/dist/types.d.ts +6 -7
  27. package/docs/en/bundled-plugins.md +1 -1
  28. package/docs/en/documents-and-markdown.md +4 -5
  29. package/docs/en/editor-and-preview.md +1 -3
  30. package/docs/en/getting-started.md +6 -2
  31. package/docs/en/host-contexts.md +26 -132
  32. package/docs/en/index.md +4 -4
  33. package/docs/en/recipes-and-pitfalls.md +10 -9
  34. package/docs/en/upgrading.md +20 -4
  35. package/docs/en/writing-plugins.md +111 -11
  36. package/docs/hu/bundled-plugins.md +1 -1
  37. package/docs/hu/documents-and-markdown.md +4 -5
  38. package/docs/hu/editor-and-preview.md +1 -3
  39. package/docs/hu/getting-started.md +6 -2
  40. package/docs/hu/host-contexts.md +26 -132
  41. package/docs/hu/index.md +4 -4
  42. package/docs/hu/recipes-and-pitfalls.md +10 -9
  43. package/docs/hu/upgrading.md +20 -4
  44. package/docs/hu/writing-plugins.md +111 -11
  45. package/package.json +6 -2
  46. package/dist/ImagePicker.svelte +0 -131
  47. package/dist/ImagePicker.svelte.d.ts +0 -13
  48. package/dist/media.d.ts +0 -17
  49. package/dist/media.js +0 -12
package/docs/en/index.md CHANGED
@@ -19,7 +19,7 @@ flowchart TB
19
19
  - **Bundled plugins** (`@atom-forge/brickee/plugins`) — plugins like any other, written only with what the core exports. You opt in to the ones you need.
20
20
  - **Your plugins** — the same `BlockPlugin` shape, living in your application.
21
21
 
22
- The editor itself knows nothing about your application. Whatever an app needs — its images, its page widths, its language — reaches the plugins through **contexts** (media, view, text) or through plugin **fields**, never through special editor props.
22
+ The editor itself knows nothing about your application. Whatever an app needs — its page widths, its code languages, its language — reaches the plugins through **contexts** (view, code languages, text) or through plugin **fields**, never through special editor props. Images are no exception: an image block is a plugin of your own, which stores, picks and resolves its images the way your application does.
23
23
 
24
24
  ## Contents
25
25
 
@@ -27,12 +27,12 @@ The editor itself knows nothing about your application. Whatever an app needs
27
27
  2. [The editor and the preview](editor-and-preview.md) — the `Editor` and `Preview` props, the palette, the editing features, and scroll sync.
28
28
  3. [Documents and markdown](documents-and-markdown.md) — the stored form (`StoredBlock`), the built-in blocks, and converting from and to markdown.
29
29
  4. [Bundled plugins](bundled-plugins.md) — what `@atom-forge/brickee/plugins` contains, and how to override a preview or add fields.
30
- 5. [Writing plugins](writing-plugins.md) — the `BlockPlugin` anatomy, fields, views and previews, containers, markdown forms, and the kit.
31
- 6. [Host contexts](host-contexts.md) — media, per-view values, and UI text.
30
+ 5. [Writing plugins](writing-plugins.md) — the `BlockPlugin` anatomy, fields, views and previews, containers, markdown forms, image blocks, and the kit.
31
+ 6. [Host contexts](host-contexts.md) — per-view values, code languages, and UI text.
32
32
  7. [On the server](server-side.md) — schema validation, plain text, and gated content.
33
33
  8. [Recipes and pitfalls](recipes-and-pitfalls.md) — complete patterns and the mistakes to avoid.
34
34
  9. [Upgrading](upgrading.md) — what to change in your code when you move to a newer version.
35
35
 
36
36
  The book also has an AI Notes chapter: instructions to copy into the agent instruction file of a project that uses the package.
37
37
 
38
- Svelte examples are complete components unless they say otherwise. Application values such as the list of images or the current page width stand for whatever your application provides.
38
+ Svelte examples are complete components unless they say otherwise. Application values such as the current page width stand for whatever your application provides.
@@ -38,23 +38,22 @@ export const messages: Messages = { ...hu, Button: 'Gomb', 'A link as a button':
38
38
  ```svelte
39
39
  <!-- lib/BlockEditor.svelte -->
40
40
  <script lang="ts">
41
- import { Editor, provideView, type MediaItem, type StoredBlock } from '@atom-forge/brickee';
41
+ import { Editor, provideView, type StoredBlock } from '@atom-forge/brickee';
42
42
  import { messages, PAGE_PALETTE, plugins } from './blocks';
43
43
 
44
44
  let {
45
45
  value = $bindable(),
46
- media = [],
47
46
  palette = PAGE_PALETTE,
48
47
  view
49
- }: { value: StoredBlock[]; media?: MediaItem[]; palette?: string[]; view?: string } = $props();
48
+ }: { value: StoredBlock[]; palette?: string[]; view?: string } = $props();
50
49
 
51
50
  provideView(() => view);
52
51
  </script>
53
52
 
54
- <Editor bind:value {plugins} {palette} {media} {messages} />
53
+ <Editor bind:value {plugins} {palette} {messages} />
55
54
  ```
56
55
 
57
- Render the published page with `<Preview blocks={doc} {plugins} {media} {messages} />` and the same `plugins`, so a block looks the same in the editor's preview and on the site.
56
+ Render the published page with `<Preview blocks={doc} {plugins} {messages} />` and the same `plugins`, so a block looks the same in the editor's preview and on the site.
58
57
 
59
58
  ## Copying the whole document as markdown
60
59
 
@@ -62,13 +61,13 @@ Copying selected blocks already puts markdown on the clipboard. For a "Copy as m
62
61
 
63
62
  ```svelte
64
63
  <script lang="ts">
65
- import { fromStored, toMarkdown, type MediaItem, type StoredBlock } from '@atom-forge/brickee';
64
+ import { fromStored, toMarkdown, type StoredBlock } from '@atom-forge/brickee';
66
65
  import { plugins } from './blocks';
67
66
 
68
- let { doc, media = [] }: { doc: StoredBlock[]; media?: MediaItem[] } = $props();
67
+ let { doc }: { doc: StoredBlock[] } = $props();
69
68
 
70
69
  // Runs in a click handler, so the browser-only toMarkdown is safe here.
71
- const copy = () => navigator.clipboard.writeText(toMarkdown(doc.map(fromStored), { plugins, media }));
70
+ const copy = () => navigator.clipboard.writeText(toMarkdown(doc.map(fromStored), { plugins }));
72
71
  </script>
73
72
 
74
73
  <button type="button" onclick={copy}>Copy as markdown</button>
@@ -106,10 +105,12 @@ The application reads the package's built `dist/`, so rebuild the package after
106
105
  | Symptom | Cause | Fix |
107
106
  |---|---|---|
108
107
  | Block fields and popups look unstyled | The `@atom-forge/ui` theme CSS or its Tailwind `@source` is missing | Set up `@atom-forge/ui` as its documentation describes and render the app beneath its `Root`. |
108
+ | Block handles stick out on the left | The handles are drawn left of the editor's box | Give the element around the editor about 22 px of left padding. |
109
109
  | Popups do not open, or "context not found" errors with a linked package | Two copies of `svelte` or `@atom-forge/ui` | Add them to Vite's `resolve.dedupe`. |
110
110
  | A plugin block shows "Unknown block" | Its plugin is missing from `plugins` | Pass the same plugins to every editor and preview of a document type. |
111
111
  | A block is missing from `Preview` | Its plugin has no `preview` | Give the plugin a `preview` (the bundled ones have a default), or render the block with your own renderer. |
112
- | A plugin block is missing from the site | The preview got other `plugins`, or the image id is not in `media` | Use the same plugin list and media for the editor and the preview. |
112
+ | A plugin block is missing from the site | The preview got other `plugins` | Use the same plugin list for the editor and the preview. |
113
+ | An image block renders nothing | Its preview rejects the stored address, or the app's own lookup does not know the stored id | Store addresses the preview accepts (`http(s)` or site-relative); resolve ids from the same data in the editor and on the site. |
113
114
  | A box cannot be put inside another box | Nested editors go one level deep | Design containers to hold content blocks, not other containers. |
114
115
  | A block is not offered inside a container | Its `accepts` or the host `palette` leaves it out | Add the palette id to `accepts` (or use `'md:*'` for every built-in). |
115
116
  | A unique block appears twice | The schema does not enforce `unique` | Check it on the server if your application relies on it. |
@@ -6,11 +6,27 @@ This chapter lists the changes that may require action when you move to a newer
6
6
 
7
7
  Changes that need no action from you — new exports, fixes, documentation updates — are listed only in the changelog (`CHANGELOG.md`, shipped with the package).
8
8
 
9
- > **No migration needed yet**
10
- >
11
- > `@atom-forge/brickee` has not had a release that requires changes in code using it. The section below is only for applications that used the editor before it became this package.
9
+ ## From 0.0.1 to 0.0.2
12
10
 
13
- ## From the `block-editor` workspace package to Unreleased
11
+ ### Images and media are no longer part of the package
12
+
13
+ The package has no notion of images any more. Whether a block stores an image's URL or an id, how the user picks the image, and how the stored value becomes an address are decided by the application's own image plugin (see [Images](writing-plugins.md#images)).
14
+
15
+ | Removed | Was |
16
+ |---|---|
17
+ | `kind: 'image'`, `kind: 'images'` | Field kinds that stored the id (or ids) of a picked media item, with the built-in image picker popup |
18
+ | `media` prop of `Editor` and `Preview` | The images blocks could pick from and refer to |
19
+ | `provideMedia`, `useMedia`, `findMedia`, `MediaItem` | The media context, its lookup and its type |
20
+ | `media` option of `toMarkdown` | `toMarkdown(blocks, { plugins, media })` is now `toMarkdown(blocks, { plugins })` |
21
+ | `ctx.media` in a plugin's `markdown(props, ctx)` | `MarkdownContext` is now `{ inner }` |
22
+
23
+ `PropField['kind']` is now `'text' | 'textarea' | 'color' | 'select' | 'slider'`. Stored documents are unaffected: a block's props keep whatever they stored, ids included.
24
+
25
+ **What to do:** in your image plugins, replace each `image` or `images` field with your own picking — a button in the plugin's view that opens your media dialog or an upload and writes the result into `props`, or a plain `text` field for a URL — and keep the prop keys, so stored documents still load. Remove the `media` prop from your editors and previews, and the `media` option from your `toMarkdown` calls. Where a view, a preview or a `markdown` form turned an id into an address with `useMedia`, `findMedia` or `ctx.media`, look it up in your application's own data instead: a module of your app, or a Svelte context of your own provided above the editor and the preview (`markdown` runs outside components, so it needs module data). Define a `MediaItem`-like type in your app if you still need one.
26
+
27
+ ## From the `block-editor` workspace package to 0.0.1
28
+
29
+ This section is only for applications that used the editor before it became this package.
14
30
 
15
31
  The editor started as `block-editor`, a workspace package inside an application. It moved here as `@atom-forge/brickee` with the same stored form, so saved documents need no conversion. The import specifiers changed, the plugin kit moved from the plugins entry point into the core, and the site-specific plugins stayed with the application.
16
32
 
@@ -141,7 +141,7 @@ Register it beside the others:
141
141
 
142
142
  > **Props come from users**
143
143
  >
144
- > A plugin's props are whatever was stored — typed by an editor, pasted, or sent to your API by anyone who can save a document. The schema only checks the shape. Every preview must treat props as untrusted: take colours through `safeColor`, check links, clamp numbers, and look images up in the media list instead of using stored URLs. Svelte escapes text in `{...}`; never put a prop into `{@html}`.
144
+ > A plugin's props are whatever was stored — typed by an editor, pasted, or sent to your API by anyone who can save a document. The schema only checks the shape. Every preview must treat props as untrusted: take colours through `safeColor`, check links and image addresses, and clamp numbers. Svelte escapes text in `{...}`; never put a prop into `{@html}`.
145
145
 
146
146
  ## Fields
147
147
 
@@ -151,9 +151,9 @@ Each `PropField` is one setting on the top of the block. The editor draws them a
151
151
  |---|---|
152
152
  | `key` | The prop it edits. |
153
153
  | `label` | Its label (translated through `messages`). |
154
- | `kind` | `text`, `textarea`, `color`, `select`, `slider`, `image` or `images`. |
154
+ | `kind` | `text`, `textarea`, `color`, `select`, `slider` or `toggles`. |
155
155
  | `placeholder` | Hint of an empty text field; for an optional colour, the colour a click on "none" starts from. |
156
- | `options` | `{ value, label }[]` for `select`, and for a `slider` that steps through values. An option label may use `{n}`, filled with its value. |
156
+ | `options` | `{ value, label }[]` for `select`, and for a `slider` that steps through values. An option label may use `{n}`, filled with its value. For `toggles`, `{ value, label, icon }[]`: `icon` is the button, `label` its tooltip. |
157
157
  | `min`, `max`, `step`, `unit` | A number `slider`: its range, step (default 1) and the unit shown after the value. |
158
158
  | `none` | The label of the unset state: a number slider's notch left of `min`, or an optional colour's "none" button. |
159
159
  | `views` | Per-view values for a `slider` (see [Per-view values](host-contexts.md#per-view-values)). |
@@ -169,8 +169,7 @@ How each kind stores its value:
169
169
  | `slider` with `options` | The value of the option at the slider's position. |
170
170
  | `slider` with `min` and `max` | The number as a string (for example `'56'`); the `none` notch removes the key. |
171
171
  | `color` | A `#rrggbb` string; with `none`, clearing it removes the key. |
172
- | `image` | The picked media item's `id`; picking it again removes it. |
173
- | `images` | An array of ids, in picking order; picking a picked one takes it out. |
172
+ | `toggles` | The list of the options switched on, in `options` order (`[]` when none); the bundled table's header and footer are one. |
174
173
 
175
174
  An empty value (`''`, `null` or `undefined`, also when returned by `parse`) removes the key from `props`, so a preview must always handle a missing prop. A field's `parse` is also a good place to normalize input: the bundled YouTube field accepts any YouTube link and stores only the video id.
176
175
 
@@ -189,7 +188,7 @@ Declare `props` and `blocks` with `$bindable()` even when the view does not use
189
188
 
190
189
  ## The preview
191
190
 
192
- The preview receives `PluginPreviewProps`: `props`, `blocks` and `plugins`. It is a plain Svelte component that renders the block for the site. Media, messages and the view are available through the contexts (`useMedia`, `useText`, `useView`; see [Host contexts](host-contexts.md)).
191
+ The preview receives `PluginPreviewProps`: `props`, `blocks` and `plugins`. It is a plain Svelte component that renders the block for the site. Messages and the view are available through the contexts (`useText`, `useView`; see [Host contexts](host-contexts.md)); images are the plugin's own business (see [Images](#images)).
193
192
 
194
193
  ## Containers
195
194
 
@@ -275,10 +274,7 @@ A supporters-only divider is a typical example of both: one per document, with t
275
274
 
276
275
  ## Markdown form
277
276
 
278
- `markdown(props, { inner, media })` returns the block as markdown, used when blocks are copied in the editor and by `toMarkdown`:
279
-
280
- - `inner` — the nested blocks already as markdown (`''` without any);
281
- - `media` — the document's media, to turn an image id into its URL with `findMedia(media, id)?.url`.
277
+ `markdown(props, { inner })` returns the block as markdown, used when blocks are copied in the editor and by `toMarkdown`. `inner` is the nested blocks already as markdown (`''` without any).
282
278
 
283
279
  Return `''` to leave the block out. Plugins whose output has no meaning as text (an ad slot, the bundled `gap`) simply have no markdown form. Three helpers keep the output well-formed:
284
280
 
@@ -288,6 +284,111 @@ Return `''` to leave the block out. Plugins whose output has no meaning as text
288
284
  | `mdJoin(...parts)` | Joins parts as separate blocks (blank line between), leaving out empty and falsy ones. |
289
285
  | `mdQuote(md)` | Prefixes every line with `> `, for a box rendered as a blockquote. |
290
286
 
287
+ ## Images
288
+
289
+ The editor has no notion of images: no image field, no picker, no list of media. Whether a block stores an image's URL or the id of an image in the app's own media library, how the user picks it, and how the stored value becomes an address are the application's decisions, so an image block is an ordinary plugin of the app.
290
+
291
+ A minimal `photo` plugin stores the image's address, typed into a text field, with alt text and a height slider:
292
+
293
+ ```svelte
294
+ <!-- PhotoView.svelte -->
295
+ <script lang="ts" module>
296
+ export interface PhotoProps extends Record<string, unknown> {
297
+ /** The image's address. */
298
+ url: string;
299
+ alt: string;
300
+ /** The height in % of the width (20–150), or unset for the image's own shape. */
301
+ ratio?: unknown;
302
+ }
303
+
304
+ /** The stored address if it is http(s) or site-relative, else ''. */
305
+ export const imageSrc = (u: unknown) => {
306
+ const s = String(u ?? '').trim();
307
+ return /^(https?:|\/(?!\/))/.test(s) ? s : '';
308
+ };
309
+
310
+ /** A CSS aspect-ratio from a stored height, or 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
+ The address is a prop like any other, so it is untrusted: the view and the preview render it only when it is an `http(s)` or site-relative address, as the button checks its link.
385
+
386
+ Other apps choose differently, all within the plugin:
387
+
388
+ - **A media library.** The block stores the id of an image in the app's library instead of a URL. The view, the preview and `markdown` turn the id into an address with the app's own lookup: a module of the app, or the app's own Svelte context provided above the editor and the preview (plugin components are rendered beneath them). `markdown` runs outside components, when blocks are copied and in `toMarkdown`, so it can only use module data.
389
+ - **Picking.** Instead of a text field, the view can show its own picker — a button that opens the app's media dialog or an upload — and write the result into `props`, which is bindable.
390
+ - **Several images.** A gallery stores a list (of URLs or ids) in one prop and edits it in its view the same way.
391
+
291
392
  ## The kit
292
393
 
293
394
  | Export | Kind | Use |
@@ -299,7 +400,6 @@ Return `''` to leave the block out. Plugins whose output has no meaning as text
299
400
  | `baseValue(value)` | function | The base value of a per-view prop. |
300
401
  | `viewValues(value, keys)` | function | A per-view prop's value for each view. |
301
402
  | `mdText`, `mdJoin`, `mdQuote` | functions | Markdown forms (above). |
302
- | `useMedia`, `findMedia` | functions | The document's images (see [Host contexts](host-contexts.md)). |
303
403
  | `useText` | function | Translates the plugin's own UI text. |
304
404
  | `useView` | function | The view the host is showing. |
305
405
  | `Preview` | component | Renders a container's nested blocks (with `nested`). |
@@ -13,7 +13,7 @@ import { table, youtube } from '@atom-forge/brickee/plugins'; // vagy néhány
13
13
 
14
14
  | Export | `type` | Mezők | Markdown alak | Mi ez |
15
15
  |---|---|---|---|---|
16
- | `table` | `table` | — (helyben szerkesztve) | GFM-táblázat | Inline formázott cellák sorai és oszlopai; a sorokat és az oszlopokat a fogantyújuknál fogva húzhatod. Az első sor lehet fejléc, az utolsó lábléc. |
16
+ | `table` | `table` | fejléc és lábléc (a cellák helyben szerkeszthetők) | GFM-táblázat | Inline formázott cellák sorai és oszlopai; egy cellába kattintva megjelennek a sora és oszlopa eszközei (beszúrás, törlés, igazítás), a sorokat és az oszlopokat a fogantyújuknál fogva húzhatod. Az első sor lehet fejléc, az utolsó lábléc. |
17
17
  | `youtube` | `youtube` | videóhivatkozás vagy -azonosító, cím | hivatkozás | YouTube-videó, kattintásra betöltve; bármely YouTube-hivatkozásból csak a videó azonosítója marad meg. |
18
18
  | `map` | `map` | hely vagy cím, nagyításcsúszka | hivatkozás | Egy hely vagy cím beágyazott Google Maps-térképe. |
19
19
  | `gap` | `gap` | magasságcsúszka | nincs | Üres hely a blokkok között, 1–8 sor magas. |
@@ -79,18 +79,17 @@ A markdown teljes értékű formátum: a vágólap ezt hordozza, és a befogadó
79
79
  | `fromStored(block)` | `StoredBlock` → `Block` | bárhol |
80
80
  | `toStored(block)` | `Block` → `StoredBlock` | csak böngészőben |
81
81
  | `fromMarkdown(text)` | markdown → `Block[]` | bárhol |
82
- | `toMarkdown(blocks, { plugins, media })` | `Block[]` → markdown | csak böngészőben |
82
+ | `toMarkdown(blocks, { plugins })` | `Block[]` → markdown | csak böngészőben |
83
83
  | `inline(md)` | inline markdown → inline HTML | bárhol |
84
84
 
85
85
  A markdown-függvények a szerkesztő alakján (`Block[]`) dolgoznak, ezért a tárolt dokumentumok a `fromStored` és a `toStored` függvényen mennek át:
86
86
 
87
87
  ```ts
88
- import { fromMarkdown, fromStored, toMarkdown, toStored, type MediaItem, type StoredBlock } from '@atom-forge/brickee';
88
+ import { fromMarkdown, fromStored, toMarkdown, toStored, type StoredBlock } from '@atom-forge/brickee';
89
89
  import { PLUGINS } from '@atom-forge/brickee/plugins';
90
90
 
91
91
  // Tárolt dokumentum -> markdown (csak böngészőben).
92
- export const exportMarkdown = (doc: StoredBlock[], media: MediaItem[]) =>
93
- toMarkdown(doc.map(fromStored), { plugins: PLUGINS, media });
92
+ export const exportMarkdown = (doc: StoredBlock[]) => toMarkdown(doc.map(fromStored), { plugins: PLUGINS });
94
93
 
95
94
  // Markdown -> tárolt dokumentum (csak böngészőben).
96
95
  export const importMarkdown = (text: string): StoredBlock[] => fromMarkdown(text).map(toStored);
@@ -110,6 +109,6 @@ Amit a `toMarkdown` kiír:
110
109
 
111
110
  - az egymást követő listaelemeket egymást követő sorokba, minden más blokkot üres sorral elválasztva;
112
111
  - a listaelemeket és a bekezdéseket szintenként két szóközzel behúzva;
113
- - a plugin-blokkot a pluginja `markdown(props, { inner, media })` függvényével, ahol az `inner` a már átalakított beágyazott blokkjai; a markdown alak nélküli plugin, illetve az, amelyik `''`-t ad vissza, kimarad.
112
+ - a plugin-blokkot a pluginja `markdown(props, { inner })` függvényével, ahol az `inner` a már átalakított beágyazott blokkjai; a markdown alak nélküli plugin, illetve az, amelyik `''`-t ad vissza, kimarad.
114
113
 
115
114
  Saját pluginod markdown alakjának megírásáról a [Pluginok írása](writing-plugins.md#markdown-alak) fejezet szól.
@@ -9,7 +9,6 @@
9
9
  | `value` | `StoredBlock[]` (bindable) | `[]` | A dokumentum. Minden változáskor visszaíródik; a kívülről lecserélt dokumentum újra betöltődik. |
10
10
  | `plugins` | `BlockPlugin[]` | `[]` | A pluginblokkok, amelyeket ez a szerkesztő megjeleníthet és felkínálhat. |
11
11
  | `palette` | `string[]` | minden beépített blokk, utána minden plugin | A perjeles menü által felkínált blokktípusok, ebben a sorrendben (lásd lent). |
12
- | `media` | `MediaItem[]` | — | A képek, amelyek közül a blokkok választhatnak (lásd [Az alkalmazás kontextusai](host-contexts.md)). |
13
12
  | `messages` | `Messages` | — | Felületi szövegek az alkalmazás nyelvén: angol szöveg → fordítás. |
14
13
  | `readonly` | `boolean` | `false` | Szerkesztés nélkül mutatja a dokumentumot; a pluginmezők rejtve vannak. |
15
14
  | `placeholder` | `string` | `Write something, or press '/' for blocks…` | Az üres blokk tippszövege. |
@@ -23,7 +22,6 @@ Az üres dokumentum egyetlen üres bekezdésként jelenik meg, így a szerkeszt
23
22
  |---|---|---|---|
24
23
  | `blocks` | `StoredBlock[]` | — | A megjelenítendő dokumentum. |
25
24
  | `plugins` | `BlockPlugin[]` | — | A pluginok, amelyek előnézetei a pluginblokkokat renderelik. |
26
- | `media` | `MediaItem[]` | — | A képek, amelyekre a blokkok azonosítóval hivatkoznak. |
27
25
  | `messages` | `Messages` | — | Felületi szövegek az alkalmazás nyelvén. |
28
26
  | `markGated` | `boolean` | `true` | Megjelöli a kapus blokk utáni blokkokat (lásd [Egyedi és kapus blokkok](writing-plugins.md#egyedi-és-kapus-blokkok)). |
29
27
  | `nested` | `boolean` | `false` | A beágyazott blokkjaikat renderelő plugin-előnézetek állítják be. |
@@ -32,7 +30,7 @@ Az előnézet a beépített blokkokat sima HTML-ként rendereli (`p`, `h1`…`h6
32
30
 
33
31
  Hogy használod-e egyáltalán a `Preview`-t, rajtad múlik: a szerkesztő nem függ tőle. Egy saját megjelenítővel dolgozó alkalmazás maga is renderelheti a tárolt blokkokat, és a pluginjai elhagyhatják a `preview`-t.
34
32
 
35
- A legfelső szintű szerkesztőnek vagy előnézetnek átadott `media` és `messages` öröklődik minden alatta lévő beágyazott szerkesztőre, beágyazott előnézetre és pluginkomponensre.
33
+ A legfelső szintű szerkesztőnek vagy előnézetnek átadott `messages` öröklődik minden alatta lévő beágyazott szerkesztőre, beágyazott előnézetre és pluginkomponensre.
36
34
 
37
35
  ## A paletta
38
36
 
@@ -10,7 +10,7 @@ Minden más plugin. A plugin egy egyszerű objektum (`BlockPlugin`), amely megne
10
10
 
11
11
  - egy **nézetet** — a szerkesztőben megjelenő Svelte-komponenst;
12
12
  - egy **előnézetet** — a blokkot az oldalon megjelenítő Svelte-komponenst;
13
- - **mezőket** — a blokk tetején megjelenő beállításokat (szöveg, szín, választó, csúszka, kép…);
13
+ - **mezőket** — a blokk tetején megjelenő beállításokat (szöveg, szín, választó, csúszka…);
14
14
  - opcionálisan egy **markdown alakot**, amelyet a dokumentum markdownként való másolásakor vagy exportálásakor használ.
15
15
 
16
16
  A csomag négy általános plugint is tartalmaz — táblázat, YouTube-videó, térkép és térköz —, de ezek is ugyanolyan pluginok, mint bármelyik más: a magban semmi sem függ tőlük, és csak azokat adod át, amelyeket szeretnél. A képblokkokat, dobozokat és más, az oldalra jellemző blokkokat te írod meg pluginként.
@@ -24,7 +24,7 @@ bun add @atom-forge/brickee @atom-forge/ui @lucide/svelte
24
24
  | Peer dependency | Miért |
25
25
  |---|---|
26
26
  | `svelte` `^5.0.0` | A szerkesztő, az előnézet és a pluginok Svelte 5 komponensek. |
27
- | `@atom-forge/ui` `>=0.0.37` | A felugró ablakok (linkbekérő, képválasztó) és a blokkmezők űrlapvezérlői. |
27
+ | `@atom-forge/ui` `>=0.0.37` | A felugró ablakok (linkbekérő) és a blokkmezők űrlapvezérlői. |
28
28
  | `@lucide/svelte` `>=1.0.0` | A blokkok ikonjai. |
29
29
  | `zod` `^4.0.0` (opcionális) | Csak az `@atom-forge/brickee/schema` szerveroldali sémájához. |
30
30
 
@@ -37,6 +37,10 @@ A blokkmezők és a felugró ablakok `@atom-forge/ui` komponensek, ezért az alk
37
37
 
38
38
  A legfelső szintű `Editor` saját felugróablak-kezelőt hoz létre, és saját felugróablak-tárolót renderel; a pluginblokkokba ágyazott szerkesztők ezt használják közösen.
39
39
 
40
+ A szerkesztő a színeit az `@atom-forge/ui` téma tokenjeiből veszi (`--color-surface`, `--color-frame`, `--color-muted-contrast`…), így követi a befogadó alkalmazás világos és sötét módját. Az előnézet keretei és képaláírásai az oldal szövegszínéből számolódnak.
41
+
42
+ A blokkok fogantyúi a **szerkesztő dobozától balra**, egy kb. 22 px széles sávban jelennek meg: a szerkesztőt körülvevő elemnek legyen ennyi bal oldali paddingje, különben a fogantyúk kilógnak belőle.
43
+
40
44
  > **Hiányzó téma**
41
45
  >
42
46
  > Az `@atom-forge/ui` téma-CSS nélkül a szerkesztő továbbra is működik, de a blokkmezők, a színválasztó, a csúszka és a felugró ablakok stílus nélkül jelennek meg.
@@ -2,143 +2,21 @@
2
2
 
3
3
  [← Előző](writing-plugins.md) · [Tartalom](index.md) · [Következő →](server-side.md)
4
4
 
5
- A szerkesztőnek nincsenek propjai az alkalmazás fogalmaihoz. Amit a befogadó alkalmazás tud — a képeit, az éppen mutatott oldalszélességet, a nyelvét —, az három Svelte-kontextuson keresztül jut el a pluginokhoz. Mindegyikhez tartozik egy `provide…` függvény, amelyet a szerkesztő vagy az előnézet fölötti komponens inicializálásakor hívsz meg, és egy `use…` függvény a plugin-komponensek számára.
5
+ A szerkesztőnek nincsenek propjai az alkalmazás fogalmaihoz. Amit a befogadó alkalmazás tud — az éppen mutatott oldalszélességet, a kiemelhető kódnyelveket, a nyelvét —, az három Svelte-kontextuson keresztül jut el a pluginokhoz. Mindegyikhez tartozik egy `provide…` függvény, amelyet a szerkesztő vagy az előnézet fölötti komponens inicializálásakor hívsz meg, és egy `use…` függvény a plugin-komponensek számára.
6
6
 
7
7
  | Kontextus | Megadás | Olvasás | Mit hordoz |
8
8
  |---|---|---|---|
9
- | Média | `media` prop, vagy `provideMedia(() => list)` | `useMedia()` | A képek, amelyek közül a blokkok választhatnak. |
10
9
  | Nézet | `provideView(() => key)` | `useView()` | A nézet, amelyet a nézetenkénti mezők szerkesztenek. |
10
+ | Kódnyelvek | `provideCodeLanguages(() => list)` | `useCodeLanguages()` | A nyelvek, amelyeket a kódblokk felkínál. |
11
11
  | Szöveg | `messages` prop, vagy `provideText(() => messages)` | `useText()` | Felületi szövegek az alkalmazás nyelvén. |
12
12
 
13
13
  A beágyazott szerkesztők és előnézetek mindhármat a szülőjüktől öröklik.
14
14
 
15
- ## Média
16
-
17
- A blokkok soha nem tárolnak kép-URL-t. A képmező egy `MediaItem` **azonosítóját** tárolja, a szerkesztő, az előnézet és a pluginok pedig a dokumentum médiájában keresik ki az elemet:
18
-
19
- ```ts
20
- interface MediaItem {
21
- id: string; // a blokk propsában tárolva
22
- name: string; // fájlnév vagy cím, a választóban jelenik meg
23
- url: string; // a kép címe
24
- group?: string; // címsor a választóban (pl. a melléklet kategóriája)
25
- }
26
- ```
27
-
28
- A mellékelt pluginok nem használnak képeket; a képblokkokat neked kell megírnod, mert az elrendezésük az oldalé. Egy minimális `photo` plugin — képmező, alternatív szöveg és magasságcsúszka:
29
-
30
- ```svelte
31
- <!-- PhotoView.svelte -->
32
- <script lang="ts" module>
33
- export interface PhotoProps extends Record<string, unknown> {
34
- /** Egy MediaItem azonosítója. */
35
- image: string;
36
- alt: string;
37
- /** A magasság a szélesség %-ában (20–150), vagy beállítatlan a kép saját alakjához. */
38
- ratio?: unknown;
39
- }
40
-
41
- /** CSS aspect-ratio egy tárolt magasságból, vagy undefined. */
42
- export const aspect = (v: unknown) => {
43
- const n = Number(v);
44
- return n >= 20 && n <= 150 ? `100 / ${n}` : undefined;
45
- };
46
- </script>
47
-
48
- <script lang="ts">
49
- import { ImageIcon } from '@lucide/svelte';
50
- import { Empty, findMedia, useMedia, type PluginViewProps } from '@atom-forge/brickee';
51
-
52
- let { props = $bindable(), blocks = $bindable() }: PluginViewProps<PhotoProps> = $props();
53
- const media = useMedia();
54
- const item = $derived(findMedia(media(), props.image));
55
- </script>
56
-
57
- {#if item}
58
- <img src={item.url} alt={props.alt} style="max-width: 100%" />
59
- {:else}
60
- <Empty icon={ImageIcon} text="Pick an image above" />
61
- {/if}
62
- ```
63
-
64
- ```svelte
65
- <!-- PhotoPreview.svelte -->
66
- <script lang="ts">
67
- import { baseValue, findMedia, useMedia, type PluginPreviewProps } from '@atom-forge/brickee';
68
- import { aspect, type PhotoProps } from './PhotoView.svelte';
69
-
70
- let { props }: PluginPreviewProps<PhotoProps> = $props();
71
- const media = useMedia();
72
- const item = $derived(findMedia(media(), props.image));
73
- </script>
74
-
75
- {#if item}
76
- <img src={item.url} alt={props.alt ?? ''} style:aspect-ratio={aspect(baseValue(props.ratio))} />
77
- {/if}
78
-
79
- <style>
80
- img {
81
- width: 100%;
82
- object-fit: cover;
83
- }
84
- </style>
85
- ```
86
-
87
- ```ts
88
- // photo.ts
89
- import { ImageIcon } from '@lucide/svelte';
90
- import { findMedia, mdText, type BlockPlugin } from '@atom-forge/brickee';
91
- import PhotoView, { type PhotoProps } from './PhotoView.svelte';
92
- import PhotoPreview from './PhotoPreview.svelte';
93
-
94
- export const photo: BlockPlugin<PhotoProps> = {
95
- type: 'photo',
96
- label: 'Photo',
97
- hint: 'One picked image',
98
- keys: 'photo image picture',
99
- icon: ImageIcon,
100
- props: () => ({ image: '', alt: '' }),
101
- fields: [
102
- { key: 'image', label: 'Image', kind: 'image' },
103
- { key: 'alt', label: 'Alt text', kind: 'text', placeholder: 'What the image shows' },
104
- { key: 'ratio', label: 'Height', kind: 'slider', min: 20, max: 150, unit: '%', none: 'Original' }
105
- ],
106
- tag: () => 'photo',
107
- content: 'none',
108
- markdown: (p, { media }) => {
109
- const url = findMedia(media, p.image)?.url;
110
- return url ? `![${mdText(p.alt)}](${url})` : '';
111
- },
112
- view: PhotoView,
113
- preview: PhotoPreview
114
- };
115
- ```
116
-
117
- A szerkesztőnek és az előnézetnek ugyanazt a médiát add:
118
-
119
- ```svelte
120
- <script lang="ts">
121
- import { Editor, Preview, type MediaItem, type StoredBlock } from '@atom-forge/brickee';
122
- import { photo } from './photo';
123
-
124
- let { value = $bindable(), media }: { value: StoredBlock[]; media: MediaItem[] } = $props();
125
- </script>
126
-
127
- <Editor bind:value plugins={[photo]} {media} />
128
- <Preview blocks={value} plugins={[photo]} {media} />
129
- ```
130
-
131
- Plugin-komponensben a listát a `useMedia()`-val olvasd ki — függvényt ad vissza, így a lista mindig friss marad —, egy elemet pedig a `findMedia(list, id)`-vel keress meg, ahogy a photo nézete és előnézete is teszi.
132
-
133
- > **Bélyegképek a szerkesztőben**
134
- >
135
- > A szerkesztőnek csak kis képekre van szüksége. Mivel a blokkok azonosítókat tárolnak, a szerkesztőnek adhatsz egy bélyegkép-URL-ekből álló listát, az oldalon lévő előnézetnek pedig egy teljes méretű URL-ekből állót, ugyanazokkal az azonosítókkal.
136
-
137
- Tipikus forrás egy oldal feltöltött mellékletei vagy egy médiatár. Minden képnek, amelyre egy dokumentum hivatkozik, szerepelnie kell a listában, különben a blokkja semmit sem renderel.
15
+ A képek nem kontextuson keresztül érkeznek: hogy egy képblokk mit tárol, és honnan veszi a képeit, azt az alkalmazás saját pluginja dönti el (lásd [Képek](writing-plugins.md#képek)).
138
16
 
139
17
  ## Nézetenkénti értékek
140
18
 
141
- Egyes beállítások attól függnek, hogyan jelenik meg az oldal: egy képnek telefonon más képarány kellhet, mint széles képernyőn. A `views` beállítással rendelkező `slider` mező nézetenként tárol értéket:
19
+ Egyes beállítások attól függnek, hogyan jelenik meg az oldal: egy képnek telefonon más képarány kellhet, mint széles képernyőn. A `views` beállítással rendelkező `slider` mező nézetenként tárol értéket. Itt ez a [Pluginok írása](writing-plugins.md#képek) fejezet `photo` pluginjának magassága és a mellékelt `gap`:
142
20
 
143
21
  ```ts
144
22
  import type { BlockPlugin } from '@atom-forge/brickee';
@@ -184,18 +62,17 @@ Az előnézet a `viewValues(value, keys)`-szel olvassa ki őket — minden néze
184
62
 
185
63
  ```svelte
186
64
  <script lang="ts">
187
- import { findMedia, useMedia, viewValues, type PluginPreviewProps } from '@atom-forge/brickee';
188
- import { aspect, type PhotoProps } from './PhotoView.svelte';
65
+ import { viewValues, type PluginPreviewProps } from '@atom-forge/brickee';
66
+ import { aspect, imageSrc, type PhotoProps } from './PhotoView.svelte';
189
67
 
190
68
  let { props }: PluginPreviewProps<PhotoProps> = $props();
191
- const media = useMedia();
192
- const item = $derived(findMedia(media(), props.image));
69
+ const src = $derived(imageSrc(props.url));
193
70
  // desktop, tablet, phone: mindegyikhez egy CSS aspect-ratio ('auto': a kép saját alakja)
194
71
  const r = $derived(viewValues(props.ratio, ['desktop', 'tablet', 'phone']).map((v) => aspect(v) ?? 'auto'));
195
72
  </script>
196
73
 
197
- {#if item}
198
- <img src={item.url} alt={props.alt ?? ''} style:--r-desktop={r[0]} style:--r-tablet={r[1]} style:--r-phone={r[2]} />
74
+ {#if src}
75
+ <img {src} alt={props.alt ?? ''} style:--r-desktop={r[0]} style:--r-tablet={r[1]} style:--r-phone={r[2]} />
199
76
  {/if}
200
77
 
201
78
  <style>
@@ -221,6 +98,23 @@ Az előnézet a `viewValues(value, keys)`-szel olvassa ki őket — minden néze
221
98
  >
222
99
  > A `views` a `slider` mezőkön támogatott. A többi mezőfajta figyelmen kívül hagyja.
223
100
 
101
+ ## Kódnyelvek
102
+
103
+ A kódblokk nyelve (`props.lang`, a markdown-kerítés info-szövege) alapból szabad szöveg. Ha az alkalmazás tudja, mely nyelveket tud kiemelni az oldala, megadja a listát, és a kódblokk mezősávjában a mező legördülő lista lesz, elején a *Nincs* opcióval:
104
+
105
+ ```svelte
106
+ <script lang="ts">
107
+ import { Editor, provideCodeLanguages, type StoredBlock } from '@atom-forge/brickee';
108
+
109
+ let { value = $bindable() }: { value: StoredBlock[] } = $props();
110
+ provideCodeLanguages(() => [{ value: 'ts', label: 'TypeScript' }, { value: 'svelte', label: 'Svelte' }, 'css', 'bash']);
111
+ </script>
112
+
113
+ <Editor bind:value />
114
+ ```
115
+
116
+ Egy elem string vagy `{ value, label? }`; a `value` kerül tárolásra. A beágyazott szerkesztők öröklik a listát. Egy tárolt nyelv, amely nincs a listában, opció marad, így egy régebbi dokumentum megnyitásakor semmi sem vész el.
117
+
224
118
  ## Felületi szövegek
225
119
 
226
120
  A szerkesztő és a pluginok angolul íródtak, és az angol szöveg a fordításának kulcsa. Add át a `messages`-t — egy objektumot, amely az angol szövegeket a te nyelvedre képezi le — a legfelső szintű szerkesztőnek és előnézetnek:
package/docs/hu/index.md CHANGED
@@ -19,7 +19,7 @@ flowchart TB
19
19
  - **Mellékelt pluginok** (`@atom-forge/brickee/plugins`) — ugyanolyan pluginok, mint bármelyik más, és csak abból készültek, amit a mag exportál. Csak azokat kapcsolod be, amelyekre szükséged van.
20
20
  - **A saját pluginjaid** — ugyanaz a `BlockPlugin` alak, az alkalmazásodban.
21
21
 
22
- Maga a szerkesztő semmit sem tud az alkalmazásodról. Amire egy alkalmazásnak szüksége van — a képei, az oldalszélességei, a nyelve —, az **kontextusokon** (média, nézet, szöveg) vagy a pluginok **mezőin** keresztül jut el a pluginokhoz, soha nem külön szerkesztő-propokon át.
22
+ Maga a szerkesztő semmit sem tud az alkalmazásodról. Amire egy alkalmazásnak szüksége van — az oldalszélességei, a kódnyelvei, a nyelve —, az **kontextusokon** (nézet, kódnyelvek, szöveg) vagy a pluginok **mezőin** keresztül jut el a pluginokhoz, soha nem külön szerkesztő-propokon át. A képek sem kivételek: a képblokk a saját pluginod, amely úgy tárolja, választja ki és oldja fel a képeit, ahogy az alkalmazásod teszi.
23
23
 
24
24
  ## Tartalom
25
25
 
@@ -27,12 +27,12 @@ Maga a szerkesztő semmit sem tud az alkalmazásodról. Amire egy alkalmazásnak
27
27
  2. [A szerkesztő és az előnézet](editor-and-preview.md) — az `Editor` és a `Preview` propjai, a paletta, a szerkesztési funkciók és a görgetés-szinkron.
28
28
  3. [Dokumentumok és markdown](documents-and-markdown.md) — a tárolt alak (`StoredBlock`), a beépített blokkok, valamint az átalakítás markdownból és markdownba.
29
29
  4. [Mellékelt pluginok](bundled-plugins.md) — mit tartalmaz az `@atom-forge/brickee/plugins`, és hogyan írhatsz felül egy előnézetet vagy adhatsz hozzá mezőket.
30
- 5. [Pluginok írása](writing-plugins.md) — a `BlockPlugin` felépítése, mezők, nézetek és előnézetek, tárolók, markdown alakok és a kit.
31
- 6. [Az alkalmazás kontextusai](host-contexts.md) — média, nézetenkénti értékek és felületi szövegek.
30
+ 5. [Pluginok írása](writing-plugins.md) — a `BlockPlugin` felépítése, mezők, nézetek és előnézetek, tárolók, markdown alakok, képblokkok és a kit.
31
+ 6. [Az alkalmazás kontextusai](host-contexts.md) — nézetenkénti értékek, kódnyelvek és felületi szövegek.
32
32
  7. [A szerveren](server-side.md) — sémavalidálás, sima szöveg és védett tartalom.
33
33
  8. [Receptek és buktatók](recipes-and-pitfalls.md) — teljes minták és az elkerülendő hibák.
34
34
  9. [Frissítés](upgrading.md) — mit kell módosítanod a kódodban, amikor újabb verzióra váltasz.
35
35
 
36
36
  A könyvben van egy AI Notes fejezet is: utasítások, amelyeket a csomagot használó projekt ágens-utasításfájljába másolhatsz.
37
37
 
38
- A Svelte-példák teljes komponensek, hacsak másként nem jelzik. Az alkalmazásbeli értékek, például a képek listája vagy az aktuális oldalszélesség, azt jelölik, amit az alkalmazásod biztosít.
38
+ A Svelte-példák teljes komponensek, hacsak másként nem jelzik. Az alkalmazásbeli értékek, például az aktuális oldalszélesség, azt jelölik, amit az alkalmazásod biztosít.