@atom-forge/brickee 0.0.1 → 0.0.3
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/CHANGELOG.md +59 -1
- package/README-AI.md +34 -28
- package/dist/Editor.svelte +713 -867
- package/dist/Editor.svelte.d.ts +0 -3
- package/dist/LinkPrompt.svelte +1 -14
- package/dist/Preview.svelte +27 -58
- package/dist/Preview.svelte.d.ts +2 -7
- package/dist/PropsBar.svelte +75 -249
- package/dist/PropsBar.svelte.d.ts +0 -3
- package/dist/code.d.ts +12 -0
- package/dist/code.js +15 -0
- package/dist/dom.d.ts +8 -0
- package/dist/dom.js +9 -0
- package/dist/editor/BlockMenu.svelte +102 -0
- package/dist/editor/BlockMenu.svelte.d.ts +32 -0
- package/dist/editor/FormatBar.svelte +41 -0
- package/dist/editor/FormatBar.svelte.d.ts +16 -0
- package/dist/editor/drag.svelte.d.ts +63 -0
- package/dist/editor/drag.svelte.js +95 -0
- package/dist/editor/history.d.ts +24 -0
- package/dist/editor/history.js +86 -0
- package/dist/editor/sweep.svelte.d.ts +21 -0
- package/dist/editor/sweep.svelte.js +56 -0
- package/dist/html.d.ts +12 -0
- package/dist/html.js +206 -0
- package/dist/i18n.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -3
- package/dist/kit/Empty.svelte +4 -15
- package/dist/kit/Frame.svelte +7 -37
- package/dist/locales/hu.js +6 -8
- package/dist/markdown.d.ts +20 -6
- package/dist/markdown.js +380 -85
- package/dist/model.js +2 -3
- package/dist/plugins/Gap.svelte +5 -8
- package/dist/plugins/GapPreview.svelte +4 -6
- package/dist/plugins/Map.svelte +2 -17
- package/dist/plugins/MapPreview.svelte +8 -10
- package/dist/plugins/Table.svelte +233 -282
- package/dist/plugins/TablePreview.svelte +28 -28
- package/dist/plugins/YouTube.svelte +3 -31
- package/dist/plugins/YouTubePreview.svelte +45 -43
- package/dist/plugins/index.js +15 -2
- package/dist/schema.js +1 -1
- package/dist/types.d.ts +27 -10
- package/dist/types.js +8 -1
- package/dist/view.js +1 -1
- package/docs/en/bundled-plugins.md +1 -1
- package/docs/en/documents-and-markdown.md +24 -19
- package/docs/en/editor-and-preview.md +7 -10
- package/docs/en/getting-started.md +14 -2
- package/docs/en/host-contexts.md +26 -132
- package/docs/en/index.md +5 -5
- package/docs/en/recipes-and-pitfalls.md +12 -11
- package/docs/en/server-side.md +0 -17
- package/docs/en/upgrading.md +34 -4
- package/docs/en/writing-plugins.md +147 -21
- package/docs/hu/bundled-plugins.md +1 -1
- package/docs/hu/documents-and-markdown.md +24 -19
- package/docs/hu/editor-and-preview.md +7 -10
- package/docs/hu/getting-started.md +14 -2
- package/docs/hu/host-contexts.md +26 -132
- package/docs/hu/index.md +5 -5
- package/docs/hu/recipes-and-pitfalls.md +12 -11
- package/docs/hu/server-side.md +0 -17
- package/docs/hu/upgrading.md +34 -4
- package/docs/hu/writing-plugins.md +147 -21
- package/package.json +14 -2
- package/dist/ImagePicker.svelte +0 -131
- package/dist/ImagePicker.svelte.d.ts +0 -13
- package/dist/media.d.ts +0 -17
- package/dist/media.js +0 -12
|
@@ -21,7 +21,6 @@ Egy plugin egy `BlockPlugin` objektum és két Svelte-komponens: egy **nézet**
|
|
|
21
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
22
|
| `markdown` | `(props: P, ctx: MarkdownContext) => string` | A markdown alakja (opcionális). |
|
|
23
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
24
|
|
|
26
25
|
A `P` a plugin props-típusa; `Record<string, unknown>`-t kiterjesztő interfészként deklaráld.
|
|
27
26
|
|
|
@@ -141,7 +140,7 @@ Regisztráld a többi mellé:
|
|
|
141
140
|
|
|
142
141
|
> **A props a felhasználóktól jön**
|
|
143
142
|
>
|
|
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
|
|
143
|
+
> 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
144
|
|
|
146
145
|
## Mezők
|
|
147
146
|
|
|
@@ -151,9 +150,9 @@ Minden `PropField` egy beállítás a blokk tetején. A szerkesztő rajzolja ki
|
|
|
151
150
|
|---|---|
|
|
152
151
|
| `key` | A prop, amelyet szerkeszt. |
|
|
153
152
|
| `label` | A címkéje (a `messages`-en keresztül fordítva). |
|
|
154
|
-
| `kind` | `text`, `textarea`, `color`, `select`, `slider
|
|
153
|
+
| `kind` | `text`, `textarea`, `color`, `select`, `slider` vagy `toggles`. |
|
|
155
154
|
| `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. |
|
|
155
|
+
| `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
156
|
| `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
157
|
| `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
158
|
| `views` | Nézetenkénti értékek egy `slider`-hez (lásd [Nézetenkénti értékek](host-contexts.md#nézetenkénti-értékek)). |
|
|
@@ -169,8 +168,7 @@ Az egyes fajták így tárolják az értéküket:
|
|
|
169
168
|
| `slider` `options`-szel | A csúszka pozíciójában lévő opció értéke. |
|
|
170
169
|
| `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
170
|
| `color` | Egy `#rrggbb` szöveg; `none` esetén a törlése eltávolítja a kulcsot. |
|
|
172
|
-
| `
|
|
173
|
-
| `images` | Azonosítók tömbje, kiválasztási sorrendben; egy már kiválasztott elem újbóli kiválasztása kiveszi. |
|
|
171
|
+
| `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. |
|
|
174
172
|
|
|
175
173
|
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.
|
|
176
174
|
|
|
@@ -189,7 +187,7 @@ A `props`-ot és a `blocks`-ot akkor is `$bindable()`-lel deklaráld, ha a néze
|
|
|
189
187
|
|
|
190
188
|
## Az előnézet
|
|
191
189
|
|
|
192
|
-
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
|
|
190
|
+
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)).
|
|
193
191
|
|
|
194
192
|
## Tárolók
|
|
195
193
|
|
|
@@ -262,23 +260,13 @@ Hogyan működik a beágyazás:
|
|
|
262
260
|
- 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.
|
|
263
261
|
- A `blocksSchemaFor(plugins)` a szerveren ellenőrzi az `accepts`-et (lásd [A szerveren](server-side.md)).
|
|
264
262
|
|
|
265
|
-
## Egyedi
|
|
263
|
+
## Egyedi blokkok
|
|
266
264
|
|
|
267
|
-
|
|
268
|
-
- **`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é.
|
|
269
|
-
|
|
270
|
-
Mindkettőre tipikus példa egy csak támogatóknak szóló elválasztó: dokumentumonként egy, mögötte a védett tartalommal.
|
|
271
|
-
|
|
272
|
-
> **A kapu csak jelöl**
|
|
273
|
-
>
|
|
274
|
-
> 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.
|
|
265
|
+
**`unique: true`** — dokumentumonként legfeljebb egy, a legfelső szinten (például egy tartalomjegyzék). Amint a dokumentumban van egy, a plugin eltűnik a perjeles és az „Átalakítás” menüből, a beillesztett második példány a markdown alakjára esik vissza, és beágyazott szerkesztő soha nem kínálja. A séma nem kényszeríti ki.
|
|
275
266
|
|
|
276
267
|
## Markdown alak
|
|
277
268
|
|
|
278
|
-
A `markdown(props, { inner
|
|
279
|
-
|
|
280
|
-
- `inner` — a beágyazott blokkok, már markdownként (`''`, ha nincsenek);
|
|
281
|
-
- `media` — a dokumentum médiája, hogy egy kép azonosítóját a `findMedia(media, id)?.url`-lel URL-lé alakíthasd.
|
|
269
|
+
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).
|
|
282
270
|
|
|
283
271
|
`''` 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:
|
|
284
272
|
|
|
@@ -288,6 +276,145 @@ A `markdown(props, { inner, media })` markdownként adja vissza a blokkot; ezt h
|
|
|
288
276
|
| `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. |
|
|
289
277
|
| `mdQuote(md)` | Minden sor elé `> `-t tesz, idézetblokként renderelt dobozhoz. |
|
|
290
278
|
|
|
279
|
+
## Beillesztett táblázatok fogadása
|
|
280
|
+
|
|
281
|
+
Ha GFM-táblázatot tartalmazó markdownt illesztenek be a szerkesztőbe, vagy a `fromMarkdown(text, { plugins })` olvassa be, a táblázatot az első olyan plugin kapja, amelynek van `fromTable` függvénye. Megkapja a táblázatot, és visszaadja az új blokk propjait:
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
interface MarkdownTable {
|
|
285
|
+
rows: string[][]; // a cellák inline markdownként, soronként; az első sor a fejléc
|
|
286
|
+
align: ('left' | 'center' | 'right')[]; // oszloponként, az elválasztó sorból (:--, :-:, --:)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
fromTable?(table: MarkdownTable): P;
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
A beépített `table` pluginnak van ilyen (`fromTable: ({ rows, align }) => ({ rows, heads: ['top'], align })`), így egy olyan szerkesztőbe beillesztett táblázat, amely felkínálja, táblázat blokk lesz. Ilyen plugin nélkül a táblázat szöveg marad.
|
|
293
|
+
|
|
294
|
+
## Fájlok fogadása
|
|
295
|
+
|
|
296
|
+
Ha fájlokat illesztenek be a szerkesztőbe (egy kimásolt kép, egy képernyőkép), vagy az oldalon kívülről dobnak rá, a szerkesztő által felkínált pluginok közül az első, amelynek van `fromFiles` függvénye és propokat ad vissza, kap egy saját blokkot:
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
fromFiles?(files: File[]): P | null | Promise<P | null>;
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
`null`-lal a következő pluginra hagyja a fájlokat. A feltöltés és a tárolás a plugin dolga, ezért visszaadhat promise-t is – feltölt, majd a tárolt URL-lel vagy azonosítóval adja vissza a propokat. A beillesztett fájl a kurzort tartalmazó blokk után kerül (vagy egy üres blokk helyére), a bedobott oda, ahol a húzásjelző vonal mutatja.
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
fromFiles: async (files) => {
|
|
306
|
+
const file = files.find((f) => f.type.startsWith('image/'));
|
|
307
|
+
if (!file) return null;
|
|
308
|
+
const url = await uploadToMyStorage(file); // az alkalmazásod feltöltése
|
|
309
|
+
return { url, alt: file.name };
|
|
310
|
+
},
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Képek
|
|
314
|
+
|
|
315
|
+
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.
|
|
316
|
+
|
|
317
|
+
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:
|
|
318
|
+
|
|
319
|
+
```svelte
|
|
320
|
+
<!-- PhotoView.svelte -->
|
|
321
|
+
<script lang="ts" module>
|
|
322
|
+
export interface PhotoProps extends Record<string, unknown> {
|
|
323
|
+
/** A kép címe. */
|
|
324
|
+
url: string;
|
|
325
|
+
alt: string;
|
|
326
|
+
/** A magasság a szélesség %-ában (20–150), vagy beállítatlan a kép saját alakjához. */
|
|
327
|
+
ratio?: unknown;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** A tárolt cím, ha http(s) vagy az oldalon belüli, különben ''. */
|
|
331
|
+
export const imageSrc = (u: unknown) => {
|
|
332
|
+
const s = String(u ?? '').trim();
|
|
333
|
+
return /^(https?:|\/(?!\/))/.test(s) ? s : '';
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
/** CSS aspect-ratio egy tárolt magasságból, vagy undefined. */
|
|
337
|
+
export const aspect = (v: unknown) => {
|
|
338
|
+
const n = Number(v);
|
|
339
|
+
return n >= 20 && n <= 150 ? `100 / ${n}` : undefined;
|
|
340
|
+
};
|
|
341
|
+
</script>
|
|
342
|
+
|
|
343
|
+
<script lang="ts">
|
|
344
|
+
import { ImageIcon } from '@lucide/svelte';
|
|
345
|
+
import { Empty, type PluginViewProps } from '@atom-forge/brickee';
|
|
346
|
+
|
|
347
|
+
let { props = $bindable(), blocks = $bindable() }: PluginViewProps<PhotoProps> = $props();
|
|
348
|
+
const src = $derived(imageSrc(props.url));
|
|
349
|
+
</script>
|
|
350
|
+
|
|
351
|
+
{#if src}
|
|
352
|
+
<img {src} alt={props.alt} style="max-width: 100%" />
|
|
353
|
+
{:else}
|
|
354
|
+
<Empty icon={ImageIcon} text="Set the image's address above" />
|
|
355
|
+
{/if}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
```svelte
|
|
359
|
+
<!-- PhotoPreview.svelte -->
|
|
360
|
+
<script lang="ts">
|
|
361
|
+
import { baseValue, type PluginPreviewProps } from '@atom-forge/brickee';
|
|
362
|
+
import { aspect, imageSrc, type PhotoProps } from './PhotoView.svelte';
|
|
363
|
+
|
|
364
|
+
let { props }: PluginPreviewProps<PhotoProps> = $props();
|
|
365
|
+
const src = $derived(imageSrc(props.url));
|
|
366
|
+
</script>
|
|
367
|
+
|
|
368
|
+
{#if src}
|
|
369
|
+
<img {src} alt={props.alt ?? ''} style:aspect-ratio={aspect(baseValue(props.ratio))} />
|
|
370
|
+
{/if}
|
|
371
|
+
|
|
372
|
+
<style>
|
|
373
|
+
img {
|
|
374
|
+
width: 100%;
|
|
375
|
+
object-fit: cover;
|
|
376
|
+
}
|
|
377
|
+
</style>
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
// photo.ts
|
|
382
|
+
import { ImageIcon } from '@lucide/svelte';
|
|
383
|
+
import { mdText, type BlockPlugin } from '@atom-forge/brickee';
|
|
384
|
+
import PhotoView, { type PhotoProps } from './PhotoView.svelte';
|
|
385
|
+
import PhotoPreview from './PhotoPreview.svelte';
|
|
386
|
+
|
|
387
|
+
export const photo: BlockPlugin<PhotoProps> = {
|
|
388
|
+
type: 'photo',
|
|
389
|
+
label: 'Photo',
|
|
390
|
+
hint: 'One image',
|
|
391
|
+
keys: 'photo image picture',
|
|
392
|
+
icon: ImageIcon,
|
|
393
|
+
props: () => ({ url: '', alt: '' }),
|
|
394
|
+
fields: [
|
|
395
|
+
{ key: 'url', label: 'Image', kind: 'text', placeholder: 'https://…', parse: (s) => s.trim() },
|
|
396
|
+
{ key: 'alt', label: 'Alt text', kind: 'text', placeholder: 'What the image shows' },
|
|
397
|
+
{ key: 'ratio', label: 'Height', kind: 'slider', min: 20, max: 150, unit: '%', none: 'Original' }
|
|
398
|
+
],
|
|
399
|
+
tag: () => 'photo',
|
|
400
|
+
content: 'none',
|
|
401
|
+
markdown: (p) => {
|
|
402
|
+
const src = imageSrc(p.url);
|
|
403
|
+
return src ? `` : '';
|
|
404
|
+
},
|
|
405
|
+
view: PhotoView,
|
|
406
|
+
preview: PhotoPreview
|
|
407
|
+
};
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
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.
|
|
411
|
+
|
|
412
|
+
Más alkalmazások másként döntenek, de mindezt a pluginon belül:
|
|
413
|
+
|
|
414
|
+
- **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.
|
|
415
|
+
- **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).
|
|
416
|
+
- **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.
|
|
417
|
+
|
|
291
418
|
## A kit
|
|
292
419
|
|
|
293
420
|
| Export | Fajta | Használat |
|
|
@@ -299,7 +426,6 @@ A `markdown(props, { inner, media })` markdownként adja vissza a blokkot; ezt h
|
|
|
299
426
|
| `baseValue(value)` | függvény | Egy nézetenkénti prop alapértéke. |
|
|
300
427
|
| `viewValues(value, keys)` | függvény | Egy nézetenkénti prop értéke minden nézetre. |
|
|
301
428
|
| `mdText`, `mdJoin`, `mdQuote` | függvények | Markdown alakok (fent). |
|
|
302
|
-
| `useMedia`, `findMedia` | függvények | A dokumentum képei (lásd [Az alkalmazás kontextusai](host-contexts.md)). |
|
|
303
429
|
| `useText` | függvény | Lefordítja a plugin saját felületi szövegeit. |
|
|
304
430
|
| `useView` | függvény | Az alkalmazás által éppen mutatott nézet. |
|
|
305
431
|
| `Preview` | komponens | Egy tároló beágyazott blokkjait rendereli (`nested`-del). |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atom-forge/brickee",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Gergely Laborci",
|
|
6
6
|
"url": "https://github.com/laborci"
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"prepare": "svelte-kit sync || echo ''",
|
|
19
19
|
"prepack": "(svelte-kit sync || true) && svelte-package && publint",
|
|
20
20
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
21
|
+
"test": "vitest run",
|
|
21
22
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
22
23
|
"package": "svelte-package",
|
|
23
24
|
"prepublishOnly": "bun run prepack",
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
"ship": {
|
|
29
30
|
"verify": [
|
|
30
31
|
"bun run check",
|
|
32
|
+
"bun run test",
|
|
31
33
|
"bun run prepack",
|
|
32
34
|
"npm pack --dry-run --ignore-scripts"
|
|
33
35
|
]
|
|
@@ -87,15 +89,25 @@
|
|
|
87
89
|
"@atom-forge/ship": "0.2.0",
|
|
88
90
|
"@atom-forge/ui": "^0.0.37",
|
|
89
91
|
"@lucide/svelte": "^1.59.0",
|
|
90
|
-
"@sveltejs/adapter-
|
|
92
|
+
"@sveltejs/adapter-static": "^4.0.1",
|
|
91
93
|
"@sveltejs/kit": "^2.50.2",
|
|
92
94
|
"@sveltejs/package": "^2.5.7",
|
|
93
95
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
96
|
+
"@tailwindcss/forms": "^0.5.11",
|
|
97
|
+
"@tailwindcss/typography": "^0.5.20",
|
|
98
|
+
"@tailwindcss/vite": "^4.3.5",
|
|
99
|
+
"happy-dom": "^21.0.4",
|
|
100
|
+
"pixelmatch": "^8.0.0",
|
|
101
|
+
"playwright-core": "^1.65.0",
|
|
102
|
+
"pngjs": "^7.0.0",
|
|
94
103
|
"publint": "^0.3.15",
|
|
104
|
+
"sass": "^1.108.2",
|
|
95
105
|
"svelte": "^5.57.1",
|
|
96
106
|
"svelte-check": "^4.4.2",
|
|
107
|
+
"tailwindcss": "^4.3.5",
|
|
97
108
|
"typescript": "^5.9.3",
|
|
98
109
|
"vite": "^7.3.1",
|
|
110
|
+
"vitest": "^5.1.0",
|
|
99
111
|
"zod": "^4.6.7"
|
|
100
112
|
},
|
|
101
113
|
"keywords": [
|
package/dist/ImagePicker.svelte
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
<!-- The image picker popup of an image field (see PropsBar): every image attached to the document, grouped. One image:
|
|
2
|
-
a click picks it and closes; several: clicks add and remove (the numbers show the order), Done or Escape closes. -->
|
|
3
|
-
<script lang="ts">
|
|
4
|
-
import { Button, getPopupManager } from '@atom-forge/ui';
|
|
5
|
-
import type { Translate } from './i18n';
|
|
6
|
-
import type { MediaItem } from './media';
|
|
7
|
-
|
|
8
|
-
interface Props {
|
|
9
|
-
media: MediaItem[];
|
|
10
|
-
multiple: boolean;
|
|
11
|
-
/** The picked ids as they are now, in order. */
|
|
12
|
-
getPicked: () => string[];
|
|
13
|
-
pick: (id: string) => void;
|
|
14
|
-
tr: Translate;
|
|
15
|
-
}
|
|
16
|
-
let { media, multiple, getPicked, pick, tr }: Props = $props();
|
|
17
|
-
const popups = getPopupManager();
|
|
18
|
-
const picked = $derived(getPicked());
|
|
19
|
-
|
|
20
|
-
function choose(id: string) {
|
|
21
|
-
pick(id);
|
|
22
|
-
if (!multiple) popups.resolve('focus');
|
|
23
|
-
}
|
|
24
|
-
function onkeydown(e: KeyboardEvent) {
|
|
25
|
-
if (e.key === 'Escape') {
|
|
26
|
-
e.preventDefault();
|
|
27
|
-
popups.resolve('focus');
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
let root = $state<HTMLElement>();
|
|
31
|
-
$effect(() => root?.focus());
|
|
32
|
-
</script>
|
|
33
|
-
|
|
34
|
-
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
35
|
-
<div class="light picker" role="dialog" aria-label={tr('Images')} tabindex="-1" {onkeydown} bind:this={root}>
|
|
36
|
-
{#if media.length}
|
|
37
|
-
<div class="grid">
|
|
38
|
-
{#each media as m, n (m.id)}
|
|
39
|
-
{@const at = picked.indexOf(m.id)}
|
|
40
|
-
{#if m.group && m.group !== media[n - 1]?.group}<span class="group">{m.group}</span>{/if}
|
|
41
|
-
<button type="button" class="thumb" class:on={at >= 0} title={m.name} aria-pressed={at >= 0} onclick={() => choose(m.id)}>
|
|
42
|
-
<img src={m.url} alt={m.name} />
|
|
43
|
-
{#if at >= 0}<span class="n">{multiple ? at + 1 : '✓'}</span>{/if}
|
|
44
|
-
</button>
|
|
45
|
-
{/each}
|
|
46
|
-
</div>
|
|
47
|
-
{#if multiple}
|
|
48
|
-
<div class="foot">
|
|
49
|
-
<span>{tr('{n} picked', { n: picked.length })}</span>
|
|
50
|
-
<Button compact onclick={() => popups.resolve('focus')}>{tr('Done')}</Button>
|
|
51
|
-
</div>
|
|
52
|
-
{/if}
|
|
53
|
-
{:else}
|
|
54
|
-
<p class="none">{tr('No images attached to this page yet.')}</p>
|
|
55
|
-
{/if}
|
|
56
|
-
</div>
|
|
57
|
-
|
|
58
|
-
<style>
|
|
59
|
-
.picker {
|
|
60
|
-
width: 360px;
|
|
61
|
-
max-height: min(440px, calc(100vh - 24px));
|
|
62
|
-
overflow: auto;
|
|
63
|
-
padding: 10px;
|
|
64
|
-
display: grid;
|
|
65
|
-
gap: 10px;
|
|
66
|
-
background: #fff;
|
|
67
|
-
color: #1d1d1f;
|
|
68
|
-
border: 1px solid #e5e5ea;
|
|
69
|
-
border-radius: 12px;
|
|
70
|
-
box-shadow: 0 10px 36px rgba(0, 0, 0, 0.2);
|
|
71
|
-
font: 13px/1.4 system-ui, sans-serif;
|
|
72
|
-
outline: none;
|
|
73
|
-
}
|
|
74
|
-
.grid {
|
|
75
|
-
display: grid;
|
|
76
|
-
grid-template-columns: repeat(5, 1fr);
|
|
77
|
-
gap: 4px;
|
|
78
|
-
}
|
|
79
|
-
.group {
|
|
80
|
-
grid-column: 1 / -1;
|
|
81
|
-
margin-top: 4px;
|
|
82
|
-
color: #8e8e96;
|
|
83
|
-
font-size: 11px;
|
|
84
|
-
}
|
|
85
|
-
.thumb {
|
|
86
|
-
position: relative;
|
|
87
|
-
padding: 0;
|
|
88
|
-
border: 2px solid transparent;
|
|
89
|
-
border-radius: 7px;
|
|
90
|
-
background: none;
|
|
91
|
-
cursor: pointer;
|
|
92
|
-
}
|
|
93
|
-
.thumb img {
|
|
94
|
-
display: block;
|
|
95
|
-
width: 100%;
|
|
96
|
-
aspect-ratio: 1;
|
|
97
|
-
object-fit: cover;
|
|
98
|
-
border-radius: 5px;
|
|
99
|
-
}
|
|
100
|
-
.thumb.on {
|
|
101
|
-
border-color: var(--color-accent, #ea580c);
|
|
102
|
-
}
|
|
103
|
-
.n {
|
|
104
|
-
position: absolute;
|
|
105
|
-
top: 2px;
|
|
106
|
-
right: 2px;
|
|
107
|
-
min-width: 16px;
|
|
108
|
-
height: 16px;
|
|
109
|
-
padding: 0 3px;
|
|
110
|
-
border-radius: 8px;
|
|
111
|
-
background: var(--color-accent, #ea580c);
|
|
112
|
-
color: #fff;
|
|
113
|
-
font: 600 10px/16px system-ui, sans-serif;
|
|
114
|
-
}
|
|
115
|
-
.foot {
|
|
116
|
-
position: sticky;
|
|
117
|
-
bottom: -10px;
|
|
118
|
-
margin: 0 -10px -10px;
|
|
119
|
-
padding: 8px 10px;
|
|
120
|
-
display: flex;
|
|
121
|
-
align-items: center;
|
|
122
|
-
justify-content: space-between;
|
|
123
|
-
background: #fff;
|
|
124
|
-
border-top: 1px solid #e5e5ea;
|
|
125
|
-
color: #8e8e96;
|
|
126
|
-
}
|
|
127
|
-
.none {
|
|
128
|
-
margin: 0;
|
|
129
|
-
color: #8e8e96;
|
|
130
|
-
}
|
|
131
|
-
</style>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { Translate } from './i18n';
|
|
2
|
-
import type { MediaItem } from './media';
|
|
3
|
-
interface Props {
|
|
4
|
-
media: MediaItem[];
|
|
5
|
-
multiple: boolean;
|
|
6
|
-
/** The picked ids as they are now, in order. */
|
|
7
|
-
getPicked: () => string[];
|
|
8
|
-
pick: (id: string) => void;
|
|
9
|
-
tr: Translate;
|
|
10
|
-
}
|
|
11
|
-
declare const ImagePicker: import("svelte").Component<Props, {}, "">;
|
|
12
|
-
type ImagePicker = ReturnType<typeof ImagePicker>;
|
|
13
|
-
export default ImagePicker;
|
package/dist/media.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export interface MediaItem {
|
|
2
|
-
/** Stored in the block's props. */
|
|
3
|
-
id: string;
|
|
4
|
-
/** File name or title, shown in the picker. */
|
|
5
|
-
name: string;
|
|
6
|
-
/** Address of the image (a thumbnail size is fine for the editor). */
|
|
7
|
-
url: string;
|
|
8
|
-
/** Where it comes from (e.g. the attachment category), shown as a heading in the picker. */
|
|
9
|
-
group?: string;
|
|
10
|
-
}
|
|
11
|
-
type Get = () => MediaItem[];
|
|
12
|
-
/** Call in a component's init. Without its own list, a nested editor or preview keeps its parent's. */
|
|
13
|
-
export declare function provideMedia(own: () => MediaItem[] | undefined): void;
|
|
14
|
-
/** Call in a component's init; the returned function reads the current list. */
|
|
15
|
-
export declare const useMedia: () => Get;
|
|
16
|
-
export declare const findMedia: (list: MediaItem[], id: unknown) => MediaItem | undefined;
|
|
17
|
-
export {};
|
package/dist/media.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// The images a document can use: the host's attachments (on a CMS page, the page's uploaded images). Blocks store
|
|
2
|
-
// an attachment's id; the editor, the preview and the plugins look it up here.
|
|
3
|
-
import { getContext, setContext } from 'svelte';
|
|
4
|
-
const KEY = 'block-editor:media';
|
|
5
|
-
/** Call in a component's init. Without its own list, a nested editor or preview keeps its parent's. */
|
|
6
|
-
export function provideMedia(own) {
|
|
7
|
-
const parent = getContext(KEY);
|
|
8
|
-
setContext(KEY, () => own() ?? parent?.() ?? []);
|
|
9
|
-
}
|
|
10
|
-
/** Call in a component's init; the returned function reads the current list. */
|
|
11
|
-
export const useMedia = () => getContext(KEY) ?? (() => []);
|
|
12
|
-
export const findMedia = (list, id) => list.find((m) => m.id === id);
|