@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
package/dist/dom.js ADDED
@@ -0,0 +1,224 @@
1
+ /** Svelte action: sets innerHTML once and only re-syncs when the model differs from the DOM (keeps the caret). */
2
+ export function rich(node, value) {
3
+ node.innerHTML = value;
4
+ return {
5
+ update(v) {
6
+ if (node.innerHTML !== v)
7
+ node.innerHTML = v;
8
+ }
9
+ };
10
+ }
11
+ export function serialize(frag) {
12
+ const d = document.createElement('div');
13
+ d.append(frag);
14
+ return d.innerHTML;
15
+ }
16
+ /** Returns '' for html that has no visible text (e.g. `<b></b>`, `<br>`). */
17
+ export function clean(h) {
18
+ const d = document.createElement('div');
19
+ d.innerHTML = h;
20
+ return d.textContent ? h : '';
21
+ }
22
+ export function textLen(h) {
23
+ const d = document.createElement('div');
24
+ d.innerHTML = h;
25
+ return d.textContent?.length ?? 0;
26
+ }
27
+ function range() {
28
+ const sel = getSelection();
29
+ return sel && sel.rangeCount ? sel.getRangeAt(0) : null;
30
+ }
31
+ export function splitAtCaret(el) {
32
+ const r = range();
33
+ if (!r)
34
+ return null;
35
+ const pre = document.createRange();
36
+ pre.selectNodeContents(el);
37
+ pre.setEnd(r.startContainer, r.startOffset);
38
+ const post = document.createRange();
39
+ post.selectNodeContents(el);
40
+ post.setStart(r.endContainer, r.endOffset);
41
+ return [serialize(pre.cloneContents()), serialize(post.cloneContents())];
42
+ }
43
+ export function atStart(el) {
44
+ const r = range();
45
+ if (!r || !r.collapsed)
46
+ return false;
47
+ const pre = document.createRange();
48
+ pre.selectNodeContents(el);
49
+ pre.setEnd(r.startContainer, r.startOffset);
50
+ return pre.toString() === '';
51
+ }
52
+ export function atEnd(el) {
53
+ const r = range();
54
+ if (!r || !r.collapsed)
55
+ return false;
56
+ const post = document.createRange();
57
+ post.selectNodeContents(el);
58
+ post.setStart(r.endContainer, r.endOffset);
59
+ return post.toString() === '';
60
+ }
61
+ export function caretOffset(el) {
62
+ const r = range();
63
+ if (!r || !el.contains(r.startContainer))
64
+ return 0;
65
+ const pre = document.createRange();
66
+ pre.selectNodeContents(el);
67
+ pre.setEnd(r.startContainer, r.startOffset);
68
+ return pre.toString().length;
69
+ }
70
+ export function setCaret(el, where = 'end') {
71
+ el.focus({ preventScroll: true });
72
+ const sel = getSelection();
73
+ if (!sel)
74
+ return;
75
+ const r = document.createRange();
76
+ r.selectNodeContents(el);
77
+ if (where === 'start')
78
+ r.collapse(true);
79
+ else if (where === 'end')
80
+ r.collapse(false);
81
+ else {
82
+ r.collapse(false);
83
+ const w = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
84
+ let left = where;
85
+ let n;
86
+ while ((n = w.nextNode())) {
87
+ const len = n.textContent?.length ?? 0;
88
+ if (left <= len) {
89
+ r.setStart(n, left);
90
+ r.collapse(true);
91
+ break;
92
+ }
93
+ left -= len;
94
+ }
95
+ }
96
+ sel.removeAllRanges();
97
+ sel.addRange(r);
98
+ el.scrollIntoView({ block: 'nearest' });
99
+ }
100
+ /** Viewport rect of the caret, or null when the browser can't tell (empty block). */
101
+ export function caretRect() {
102
+ const r = range();
103
+ if (!r)
104
+ return null;
105
+ const c = r.cloneRange();
106
+ c.collapse(false);
107
+ const rects = c.getClientRects();
108
+ const rect = rects.length ? rects[rects.length - 1] : c.getBoundingClientRect();
109
+ return rect && (rect.width || rect.height) ? rect : null;
110
+ }
111
+ /** Places the caret in `el` at the viewport point (x, y). Keeps the horizontal position while moving between blocks. */
112
+ export function caretAt(el, x, y) {
113
+ const d = document;
114
+ let r = null;
115
+ if (d.caretPositionFromPoint) {
116
+ const p = d.caretPositionFromPoint(x, y);
117
+ if (p) {
118
+ r = document.createRange();
119
+ r.setStart(p.offsetNode, p.offset);
120
+ }
121
+ }
122
+ else if (d.caretRangeFromPoint) {
123
+ r = d.caretRangeFromPoint(x, y);
124
+ }
125
+ if (!r || !el.contains(r.startContainer))
126
+ return false;
127
+ r.collapse(true);
128
+ el.focus({ preventScroll: true });
129
+ const sel = getSelection();
130
+ sel.removeAllRanges();
131
+ sel.addRange(r);
132
+ el.scrollIntoView({ block: 'nearest' });
133
+ return true;
134
+ }
135
+ /** Deletes `n` characters directly before the caret (inside the caret's text node). */
136
+ export function removeBeforeCaret(n) {
137
+ const r = range();
138
+ if (!r || r.startContainer.nodeType !== Node.TEXT_NODE || r.startOffset < n)
139
+ return false;
140
+ const t = r.startContainer;
141
+ const off = r.startOffset;
142
+ t.deleteData(off - n, n);
143
+ const nr = document.createRange();
144
+ nr.setStart(t, off - n);
145
+ nr.collapse(true);
146
+ const sel = getSelection();
147
+ sel.removeAllRanges();
148
+ sel.addRange(nr);
149
+ return true;
150
+ }
151
+ /** Is the caret on the first (up) or last (down) visual line of the block? */
152
+ export function onEdgeLine(el, up) {
153
+ const sel = getSelection();
154
+ if (!sel || !sel.isCollapsed)
155
+ return false;
156
+ const cr = caretRect();
157
+ if (!cr)
158
+ return true;
159
+ const er = el.getBoundingClientRect();
160
+ const lh = parseFloat(getComputedStyle(el).lineHeight) || 24;
161
+ return up ? cr.top - er.top < lh * 0.5 : er.bottom - cr.bottom < lh * 0.5;
162
+ }
163
+ const MARKS = new Set(['B', 'STRONG', 'I', 'EM', 'U', 'S', 'STRIKE', 'DEL', 'SUB', 'SUP', 'CODE', 'A']);
164
+ /**
165
+ * Plain text: a selection loses every mark and link; a caret steps out of the marks around it, so what is typed next
166
+ * is plain (a zero-width space holds it there; the markdown conversion drops it).
167
+ */
168
+ export function plainText(el) {
169
+ const sel = getSelection();
170
+ const r = range();
171
+ if (!sel || !r || !el.contains(r.commonAncestorContainer))
172
+ return;
173
+ if (!r.collapsed) {
174
+ document.execCommand('removeFormat');
175
+ document.execCommand('unlink');
176
+ return;
177
+ }
178
+ let outer = null;
179
+ for (let n = r.startContainer; n && n !== el; n = n.parentNode)
180
+ if (MARKS.has(n.nodeName))
181
+ outer = n;
182
+ if (!outer)
183
+ return;
184
+ const t = document.createTextNode('\u200B');
185
+ outer.after(t);
186
+ const nr = document.createRange();
187
+ nr.setStart(t, 1);
188
+ nr.collapse(true);
189
+ sel.removeAllRanges();
190
+ sel.addRange(nr);
191
+ }
192
+ export function toggleCode(el) {
193
+ const sel = getSelection();
194
+ const r = range();
195
+ if (!sel || !r || r.collapsed)
196
+ return;
197
+ const anc = r.commonAncestorContainer;
198
+ const host = (anc instanceof Element ? anc : anc.parentElement)?.closest('code');
199
+ if (host && el.contains(host)) {
200
+ const nodes = [...host.childNodes];
201
+ host.replaceWith(...nodes);
202
+ return;
203
+ }
204
+ const c = document.createElement('code');
205
+ c.append(r.extractContents());
206
+ r.insertNode(c);
207
+ const nr = document.createRange();
208
+ nr.selectNodeContents(c);
209
+ sel.removeAllRanges();
210
+ sel.addRange(nr);
211
+ }
212
+ /** Is the selection's moving end (focus) at the very start (up) or end (down) of the block's text? */
213
+ export function focusAtEdge(el, up) {
214
+ const sel = getSelection();
215
+ if (!sel || !sel.focusNode || !el.contains(sel.focusNode))
216
+ return false;
217
+ const r = document.createRange();
218
+ r.selectNodeContents(el);
219
+ if (up)
220
+ r.setEnd(sel.focusNode, sel.focusOffset);
221
+ else
222
+ r.setStart(sel.focusNode, sel.focusOffset);
223
+ return r.toString() === '';
224
+ }
package/dist/i18n.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export type Messages = Record<string, string>;
2
+ export type Translate = (text: string, vars?: Record<string, string | number>) => string;
3
+ export declare const translator: (messages?: Messages) => Translate;
4
+ /** Call in a component's init. Without its own messages, a nested editor or preview keeps its parent's. */
5
+ export declare function provideText(own: () => Messages | undefined): void;
6
+ /** Call in a component's init; the returned function translates with the current messages. */
7
+ export declare function useText(): Translate;
package/dist/i18n.js ADDED
@@ -0,0 +1,18 @@
1
+ // UI text. The editor and the plugins are written in English; a host passes `messages` (English text -> its own) to
2
+ // the top editor or preview, and nested editors and plugin views inherit them. `{name}` placeholders are filled in.
3
+ import { getContext, setContext } from 'svelte';
4
+ const KEY = 'block-editor:text';
5
+ export const translator = (messages) => (text, vars) => {
6
+ const out = messages?.[text] ?? text;
7
+ return vars ? out.replace(/\{(\w+)\}/g, (m, k) => (k in vars ? String(vars[k]) : m)) : out;
8
+ };
9
+ /** Call in a component's init. Without its own messages, a nested editor or preview keeps its parent's. */
10
+ export function provideText(own) {
11
+ const parent = getContext(KEY);
12
+ setContext(KEY, () => own() ?? parent?.());
13
+ }
14
+ /** Call in a component's init; the returned function translates with the current messages. */
15
+ export function useText() {
16
+ const get = getContext(KEY);
17
+ return (text, vars) => translator(get?.())(text, vars);
18
+ }
@@ -0,0 +1,13 @@
1
+ export { default as Editor } from './Editor.svelte';
2
+ export { default as Preview } from './Preview.svelte';
3
+ export { default as Frame } from './kit/Frame.svelte';
4
+ export { default as Empty } from './kit/Empty.svelte';
5
+ export * from './kit/util';
6
+ export * from './model';
7
+ export { inline, fromStored, toStored, fromMarkdown, toMarkdown } from './markdown';
8
+ export { provideView, useView } from './view';
9
+ export { provideCodeLanguages, useCodeLanguages, type CodeLanguage } from './code';
10
+ export { scrollSync, scrollFraction, setScrollFraction, type ScrollSync } from './scroll';
11
+ export { provideText, useText, translator, type Messages, type Translate } from './i18n';
12
+ export { PALETTE_ITEMS, BUILT_IN_IDS, narrowPalette, type Icon, type PaletteItem } from './palette';
13
+ export type { MarkdownContext, Block, BlockType, Content, PluginPreviewProps, PluginViewProps, PropField, Props, BlockPlugin } from './types';
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ // block-editor: a Svelte 5 block editor. The core is markdown (`md:p`, `md:h2`, `md:code`…) plus the kit to write plugins
2
+ // with; every other block is a plugin with an editor view, a site preview and optionally a markdown form. The bundled
3
+ // plugins (block-editor/plugins) are plugins like any other. The stored form is `StoredBlock[]` (see model.ts).
4
+ export { default as Editor } from './Editor.svelte';
5
+ export { default as Preview } from './Preview.svelte';
6
+ // The kit: what plugins are written with (box frame, empty state, helpers).
7
+ export { default as Frame } from './kit/Frame.svelte';
8
+ export { default as Empty } from './kit/Empty.svelte';
9
+ export * from './kit/util';
10
+ export * from './model';
11
+ export { inline, fromStored, toStored, fromMarkdown, toMarkdown } from './markdown';
12
+ export { provideView, useView } from './view';
13
+ export { provideCodeLanguages, useCodeLanguages } from './code';
14
+ export { scrollSync, scrollFraction, setScrollFraction } from './scroll';
15
+ export { provideText, useText, translator } from './i18n';
16
+ export { PALETTE_ITEMS, BUILT_IN_IDS, narrowPalette } from './palette';
@@ -0,0 +1,24 @@
1
+ <!-- What a plugin without content shows until its props are set. -->
2
+ <script lang="ts">
3
+ import { useText } from '../i18n';
4
+ import type { Icon } from '../palette';
5
+
6
+ let { icon: I, text }: { icon: Icon; text: string } = $props();
7
+ const tr = useText();
8
+ </script>
9
+
10
+ <div class="empty"><I size={18} /> {tr(text)}</div>
11
+
12
+ <style>
13
+ .empty {
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ gap: 8px;
18
+ min-height: 96px;
19
+ border: 1px dashed var(--color-frame, #d4d4da);
20
+ border-radius: 10px;
21
+ color: var(--color-muted-contrast, #a0a0a8);
22
+ font: 14px system-ui, sans-serif;
23
+ }
24
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { Icon } from '../palette';
2
+ type $$ComponentProps = {
3
+ icon: Icon;
4
+ text: string;
5
+ };
6
+ declare const Empty: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type Empty = ReturnType<typeof Empty>;
8
+ export default Empty;
@@ -0,0 +1,76 @@
1
+ <!-- The editor look of a plugin with nested blocks: a thin frame with the block's fields on its top (or, read-only, a
2
+ heading line), and the nested editor. -->
3
+ <script lang="ts">
4
+ import { useText } from '../i18n';
5
+ import Editor from '../Editor.svelte';
6
+ import { claimFields } from '../PluginHost.svelte';
7
+ import type { BlockPlugin, StoredBlock } from '../types';
8
+
9
+ interface Props {
10
+ blocks: StoredBlock[];
11
+ readonly: boolean;
12
+ palette: string[];
13
+ plugins: BlockPlugin[];
14
+ /** Summary of the block's props (title…); `empty` is shown when there is none. */
15
+ heading?: string;
16
+ empty?: string;
17
+ image?: string;
18
+ placeholder?: string;
19
+ }
20
+ let {
21
+ blocks = $bindable(),
22
+ readonly,
23
+ palette,
24
+ plugins,
25
+ heading = '',
26
+ empty = '',
27
+ image,
28
+ placeholder
29
+ }: Props = $props();
30
+ const tr = useText();
31
+ const fields = claimFields();
32
+ </script>
33
+
34
+ <div class="frame">
35
+ {#if fields}
36
+ <div class="fields">{@render fields()}</div>
37
+ {:else if heading || empty || image}
38
+ <div class="head">
39
+ {#if image}<img src={image} alt="" draggable="false" />{/if}
40
+ <span class:muted={!heading}>{heading || empty}</span>
41
+ </div>
42
+ {/if}
43
+ <Editor bind:value={blocks} {readonly} {palette} {plugins} nested placeholder={placeholder ?? tr('Write inside…')} />
44
+ </div>
45
+
46
+ <style>
47
+ .frame {
48
+ position: relative;
49
+ border: 1px solid var(--color-frame, #e5e5ea);
50
+ border-radius: 10px;
51
+ /* left: room for the inner blocks' handle bar (22px left of the row, which reaches 8px out) */
52
+ padding: 0.8em 1.1em 0.4em calc(30px + 0.5em);
53
+ }
54
+ /* the fields reach to the frame's edges, the frame's padding taken back */
55
+ .fields {
56
+ margin: calc(-0.8em + 4px) calc(-1.1em + 4px) 0.5em calc(-30px - 0.5em + 4px);
57
+ }
58
+ .head {
59
+ display: flex;
60
+ align-items: center;
61
+ gap: 8px;
62
+ margin: 0 9em 0.3em 0;
63
+ font: 600 13px/1.4 system-ui, sans-serif;
64
+ color: var(--color-surface-contrast, #1d1d1f);
65
+ }
66
+ .head img {
67
+ height: 28px;
68
+ width: 44px;
69
+ object-fit: cover;
70
+ border-radius: 4px;
71
+ }
72
+ .muted {
73
+ font-weight: 400;
74
+ color: var(--color-muted-contrast, #a0a0a8);
75
+ }
76
+ </style>
@@ -0,0 +1,15 @@
1
+ import type { BlockPlugin, StoredBlock } from '../types';
2
+ interface Props {
3
+ blocks: StoredBlock[];
4
+ readonly: boolean;
5
+ palette: string[];
6
+ plugins: BlockPlugin[];
7
+ /** Summary of the block's props (title…); `empty` is shown when there is none. */
8
+ heading?: string;
9
+ empty?: string;
10
+ image?: string;
11
+ placeholder?: string;
12
+ }
13
+ declare const Frame: import("svelte").Component<Props, {}, "blocks">;
14
+ type Frame = ReturnType<typeof Frame>;
15
+ export default Frame;
@@ -0,0 +1,13 @@
1
+ /** A #hex colour, or the fallback. */
2
+ export declare const safeColor: (c: unknown, fallback: string) => string;
3
+ export declare const lines: (s: string) => string[];
4
+ /** The base value of a per-view prop (see PropField.views): the value itself, or the first view's. */
5
+ export declare const baseValue: (v: unknown) => unknown;
6
+ /** A per-view prop's value for each of these views (the first is the base); a view without its own takes the one before. */
7
+ export declare function viewValues(v: unknown, keys: string[]): unknown[];
8
+ /** Plain text made safe inside markdown (no inline marks, no block start). */
9
+ export declare const mdText: (s: unknown) => string;
10
+ /** Markdown lines quoted (`> `), for a box rendered as a blockquote. */
11
+ export declare const mdQuote: (md: string) => string;
12
+ /** Markdown parts joined as separate blocks, empty ones left out. */
13
+ export declare const mdJoin: (...parts: (string | false | null | undefined)[]) => string;
@@ -0,0 +1,28 @@
1
+ // Helpers for writing plugins: values come from the block fields, so the previews only use safe ones.
2
+ /** A #hex colour, or the fallback. */
3
+ export const safeColor = (c, fallback) => (/^#[0-9a-f]{3,8}$/i.test(String(c ?? '')) ? String(c) : fallback);
4
+ export const lines = (s) => s
5
+ .split('\n')
6
+ .map((l) => l.trim())
7
+ .filter(Boolean);
8
+ const byView = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
9
+ /** The base value of a per-view prop (see PropField.views): the value itself, or the first view's. */
10
+ export const baseValue = (v) => (byView(v) ? Object.values(v)[0] : v);
11
+ /** A per-view prop's value for each of these views (the first is the base); a view without its own takes the one before. */
12
+ export function viewValues(v, keys) {
13
+ const own = byView(v) ? v : { [keys[0]]: v };
14
+ let last;
15
+ return keys.map((k) => (last = own[k] ?? last));
16
+ }
17
+ /** Plain text made safe inside markdown (no inline marks, no block start). */
18
+ export const mdText = (s) => String(s ?? '')
19
+ .replace(/[\\`*~[\]<_#]/g, '\\$&')
20
+ .replace(/\n+/g, ' ')
21
+ .trim();
22
+ /** Markdown lines quoted (`> `), for a box rendered as a blockquote. */
23
+ export const mdQuote = (md) => md
24
+ .split('\n')
25
+ .map((l) => (l ? `> ${l}` : '>'))
26
+ .join('\n');
27
+ /** Markdown parts joined as separate blocks, empty ones left out. */
28
+ export const mdJoin = (...parts) => parts.filter(Boolean).join('\n\n');
@@ -0,0 +1,2 @@
1
+ import type { Messages } from '../i18n';
2
+ export declare const hu: Messages;
@@ -0,0 +1,142 @@
1
+ export const hu = {
2
+ // szerkesztő
3
+ "Write something, or press '/' for blocks…": 'Írj valamit, vagy nyomj „/”-t a blokkokért…',
4
+ 'Add a block below': 'Új blokk alá',
5
+ 'Drag to move, click to select': 'Húzd az áthelyezéshez, kattints a kijelöléshez',
6
+ Done: 'Kész',
7
+ inherited: 'örökölt',
8
+ Settings: 'Beállítások',
9
+ 'Hide settings': 'Beállítások elrejtése',
10
+ None: 'Nincs',
11
+ 'New block at the end': 'Új blokk a végére',
12
+ Formatting: 'Formázás',
13
+ 'Turn into': 'Átalakítás',
14
+ 'Block actions': 'Blokkműveletek',
15
+ 'Block type': 'Blokktípus',
16
+ Delete: 'Törlés',
17
+ '{n} blocks': '{n} blokk',
18
+ 'Remove this block': 'A blokk törlése',
19
+ Unwrap: 'Kicsomagolás',
20
+ 'Move the inner blocks out': 'A belső blokkok kikerülnek',
21
+ 'Put inside': 'Bele',
22
+ 'Put {n} blocks inside': '{n} blokk bele',
23
+ 'Loses formatting': 'A formázás elvész',
24
+ OK: 'OK',
25
+ Remove: 'Eltávolítás',
26
+ 'Bold (Ctrl+B)': 'Félkövér (Ctrl+B)',
27
+ 'Italic (Ctrl+I)': 'Dőlt (Ctrl+I)',
28
+ 'Underline (Ctrl+U)': 'Aláhúzott (Ctrl+U)',
29
+ 'Strikethrough (Ctrl+Shift+X)': 'Áthúzott (Ctrl+Shift+X)',
30
+ 'Subscript (NO₂)': 'Alsó index (NO₂)',
31
+ 'Superscript (m²)': 'Felső index (m²)',
32
+ 'Code (Ctrl+E)': 'Kód (Ctrl+E)',
33
+ 'Link (Ctrl+K)': 'Link (Ctrl+K)',
34
+ 'Plain text: remove the formatting (Ctrl+\\)': 'Sima szöveg: a formázás törlése (Ctrl+\\)',
35
+ 'List item': 'Listaelem',
36
+ Language: 'Nyelv',
37
+ 'e.g. ts, py, css': 'pl. ts, py, css',
38
+ code: 'kód',
39
+ // beépített blokkok
40
+ Text: 'Szöveg',
41
+ 'Plain paragraph': 'Sima bekezdés',
42
+ 'text paragraph': 'szöveg bekezdés',
43
+ 'Heading 1': 'Címsor 1',
44
+ 'Heading 2': 'Címsor 2',
45
+ 'Heading 3': 'Címsor 3',
46
+ 'Heading 4': 'Címsor 4',
47
+ 'Heading 5': 'Címsor 5',
48
+ 'Heading 6': 'Címsor 6',
49
+ 'Large heading': 'Nagy cím',
50
+ 'Medium heading': 'Közepes cím',
51
+ 'Small heading': 'Kis cím',
52
+ 'Sub-heading': 'Alcím',
53
+ 'Minor heading': 'Kisebb alcím',
54
+ 'Smallest heading': 'Legkisebb alcím',
55
+ 'heading h1 title': 'cím címsor',
56
+ 'heading h2 title': 'cím címsor alcím',
57
+ 'heading h3 title': 'cím címsor alcím',
58
+ 'heading h4 title': 'alcím címsor',
59
+ 'heading h5 title': 'alcím címsor',
60
+ 'heading h6 title': 'alcím címsor',
61
+ 'Bulleted list': 'Felsorolás',
62
+ 'Simple bullets': 'Pontokba szedve',
63
+ 'bullet list ul': 'lista felsorolás pont',
64
+ 'Numbered list': 'Számozott lista',
65
+ 'Ordered items': 'Sorszámozott elemek',
66
+ 'numbered ordered list ol': 'számozott lista sorszám',
67
+ 'To-do': 'Teendő',
68
+ 'Checkable item': 'Kipipálható elem',
69
+ 'todo task checkbox': 'teendő feladat pipa',
70
+ Quote: 'Idézet',
71
+ 'Highlighted quote': 'Kiemelt idézet',
72
+ 'quote blockquote': 'idézet',
73
+ Code: 'Kód',
74
+ 'Monospaced block': 'Fix szélességű betűkkel',
75
+ 'code snippet pre': 'kód forráskód',
76
+ Divider: 'Elválasztó',
77
+ 'Horizontal line': 'Vízszintes vonal',
78
+ 'divider hr line separator': 'elválasztó vonal',
79
+ // bővítmények: név, leírás, kulcsszavak
80
+ YouTube: 'YouTube',
81
+ 'Video from a link': 'Videó linkből',
82
+ 'youtube video embed': 'youtube videó beágyazás',
83
+ Table: 'Táblázat',
84
+ 'Rows and columns': 'Sorok és oszlopok',
85
+ 'table grid rows columns price list': 'táblázat sor oszlop árlista',
86
+ Map: 'Térkép',
87
+ Gap: 'Térköz',
88
+ 'Empty space': 'Üres hely',
89
+ 'gap space spacer whitespace': 'térköz hely üres távolság',
90
+ 'Google Maps for a place': 'Google Térkép egy helyről',
91
+ 'map google location address place': 'térkép hely cím',
92
+ // mezők
93
+ Title: 'Cím',
94
+ Height: 'Magasság',
95
+ '1 line': '1 sor',
96
+ '{n} lines': '{n} sor',
97
+ Original: 'Eredeti',
98
+ 'Video link or id': 'A videó linkje vagy azonosítója',
99
+ 'Place or address': 'Hely vagy cím',
100
+ 'e.g. Pécs, Széchenyi tér': 'pl. Pécs, Széchenyi tér',
101
+ Zoom: 'Nagyítás',
102
+ // a blokkok címkéi (a blokk tetején, a mezők előtt)
103
+ left: 'bal',
104
+ right: 'jobb',
105
+ danger: 'vigyázat',
106
+ image: 'kép',
107
+ youtube: 'youtube',
108
+ table: 'táblázat',
109
+ map: 'térkép',
110
+ gap: 'térköz',
111
+ // a bővítmények nézetei
112
+ 'Write inside…': 'Írj bele…',
113
+ 'Paste a YouTube link above': 'Illeszd be fent a YouTube-linket',
114
+ 'Set a place or an address above': 'Adj meg fent egy helyet vagy címet',
115
+ Row: 'Sor',
116
+ Column: 'Oszlop',
117
+ 'Insert a row above': 'Új sor fölé',
118
+ 'Insert a row below': 'Új sor alá',
119
+ 'Delete this row': 'A sor törlése',
120
+ 'Insert a column left': 'Új oszlop balra',
121
+ 'Insert a column right': 'Új oszlop jobbra',
122
+ 'Delete this column': 'Az oszlop törlése',
123
+ 'Align column left': 'Az oszlop balra zárva',
124
+ 'Center column': 'Az oszlop középre zárva',
125
+ 'Align column right': 'Az oszlop jobbra zárva',
126
+ 'Header row': 'Fejléc (első sor)',
127
+ 'Footer row (e.g. sums)': 'Lábléc (utolsó sor, pl. összesítő)',
128
+ 'Header and footer': 'Fejléc és lábléc',
129
+ 'Click a cell to edit its row and column': 'Kattints egy cellába a sora és oszlopa szerkesztéséhez',
130
+ 'Drag to move the row': 'Húzd a sor áthelyezéséhez',
131
+ 'Drag to move the column': 'Húzd az oszlop áthelyezéséhez',
132
+ Heading: 'Fejléc',
133
+ 'Row {r}, column {c}': '{r}. sor, {c}. oszlop',
134
+ 'Add a column': 'Új oszlop',
135
+ 'Add a row': 'Új sor',
136
+ // előnézet
137
+ 'For supporters only': 'Csak támogatóknak',
138
+ 'YouTube video': 'YouTube-videó',
139
+ video: 'videó',
140
+ 'Play {title}': 'Lejátszás: {title}',
141
+ 'Map: {place}': 'Térkép: {place}'
142
+ };
@@ -0,0 +1,23 @@
1
+ import { type Block, type BlockPlugin, type StoredBlock } from './types';
2
+ /** Inline markdown -> inline html (b, i, u, sub, sup, strike, code, a, br). Backslash escapes are honoured; links are
3
+ * http(s), mailto, tel or site-relative only. */
4
+ export declare function inline(md: string): string;
5
+ /** Inline html -> inline markdown (browser only). `code` keeps the text verbatim, as in a code block. */
6
+ export declare function htmlToMarkdown(html: string, code?: boolean): string;
7
+ /** One block -> its markdown snippet, without indentation. `n` is the ordinal of a numbered item. */
8
+ export declare function blockToMd(b: Block, n?: number): string;
9
+ /** One markdown snippet -> block fields. */
10
+ export declare function mdToBlock(md: string): Pick<Block, 'type' | 'html' | 'checked' | 'props'>;
11
+ /** Inline html -> the same letters without formatting (line breaks kept), as a code block holds them. */
12
+ export declare function plainHtml(html: string): string;
13
+ /** Editor block -> stored block: `md:<kind>` with inline markdown text, or the plugin's own type. */
14
+ export declare function toStored(b: Block): StoredBlock;
15
+ /** Stored block -> editor block with a fresh id. Unknown types (md: or plugin) are kept as they are. */
16
+ export declare function fromStored(s: StoredBlock): Block;
17
+ /** Markdown text -> blocks. Supports headings, lists, todos, quotes, fenced code, hr and basic inline marks. */
18
+ export declare function fromMarkdown(src: string): Block[];
19
+ /** Blocks -> markdown text (browser only: uses the DOM to read the inline html). A plugin block is written by its
20
+ * plugin's `markdown`; one without it is left out. */
21
+ export declare function toMarkdown(blocks: Block[], opts?: {
22
+ plugins?: BlockPlugin[];
23
+ }): string;