@kubex/zinc 1.1.100 → 1.1.102
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/dist/custom-elements.json +260 -81
- package/dist/vscode.html-custom-data.json +13 -13
- package/dist/web-types.json +25 -25
- package/dist/zn.d.ts +124 -4
- package/dist/zn.min.js +359 -282
- package/docs/pages/components/remarkd-editor.md +59 -0
- package/package.json +1 -1
- package/src/components/menu/menu.component.ts +4 -1
- package/src/components/menu/menu.scss +3 -0
- package/src/components/page/page.scss +14 -2
- package/src/components/remarkd-editor/actions.ts +159 -0
- package/src/components/remarkd-editor/feature-keys.ts +17 -0
- package/src/components/remarkd-editor/remarkd-editor.component.ts +509 -49
- package/src/components/remarkd-editor/remarkd-editor.scss +68 -1
- package/src/components/remarkd-editor/remarkd-editor.test.ts +675 -9
- package/src/components/slash-menu/slash-menu.scss +2 -0
- package/src/internal/toolbar-overflow.ts +100 -0
|
@@ -157,6 +157,65 @@ outgrows the 20rem cap set here."
|
|
|
157
157
|
></zn-remarkd-editor>
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
+
### Actions
|
|
161
|
+
|
|
162
|
+
The toolbar and the `/` slash menu are driven by one shared registry of actions, grouped
|
|
163
|
+
under ten headings: **Text**, **Inline**, **Lists**, **Admonitions**, **Blocks**,
|
|
164
|
+
**Structured**, **Media**, **Objects**, **Breaks**, and **Logic**. The toolbar shows as
|
|
165
|
+
many groups as fit its width and folds the rest into an overflow menu; the slash menu
|
|
166
|
+
carries every action regardless of width, and typing narrows it by label or keyword — so
|
|
167
|
+
a folded toolbar group is never actually out of reach.
|
|
168
|
+
|
|
169
|
+
{% raw %}
|
|
170
|
+
|
|
171
|
+
```html:preview
|
|
172
|
+
<zn-remarkd-editor value='{{button:action text="Get started" href=/}}
|
|
173
|
+
|
|
174
|
+
{{video:dQw4w9WgXcQ source=youtube}}
|
|
175
|
+
|
|
176
|
+
{{reflist}}'></zn-remarkd-editor>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
{% endraw %}
|
|
180
|
+
|
|
181
|
+
### Inline Formatting
|
|
182
|
+
|
|
183
|
+
Inline actions — Strong, Emphasis, Code, Underline, Strikethrough, Highlight, and the
|
|
184
|
+
rest of the Inline group — apply to the current selection inside the block being edited,
|
|
185
|
+
so they only make sense while a block is open: the toolbar disables them until one is
|
|
186
|
+
(the slash menu lists them regardless, since it can only ever open inside a block in the
|
|
187
|
+
first place). Choosing one from the toolbar wraps the selection in its mark, or — with
|
|
188
|
+
nothing selected — inserts a placeholder inside the mark with the placeholder selected so
|
|
189
|
+
typing replaces it; from the slash menu it always inserts the full construct with the
|
|
190
|
+
caret collapsed at the start of the placeholder, since there is no selection to wrap.
|
|
191
|
+
Choosing the same action again on a selection that already carries the mark unwraps it
|
|
192
|
+
rather than nesting a second one.
|
|
193
|
+
|
|
194
|
+
```html:preview
|
|
195
|
+
<zn-remarkd-editor value="Click into this block, select a word, then choose Strong or Emphasis from the toolbar."></zn-remarkd-editor>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Variables and Conditionals
|
|
199
|
+
|
|
200
|
+
The Logic group's actions insert document attributes (`:name: value`) and remarkd's
|
|
201
|
+
conditional directives (`ifdef`, `ifndef`, `ifeval`) — but the editor only ever *marks*
|
|
202
|
+
them, it never evaluates them: an attribute line renders as a chip, a `{name}` reference
|
|
203
|
+
in surrounding text renders as a token, and an `ifdef`/`ifndef`/`ifeval` range renders as
|
|
204
|
+
a labelled wrapper whose content stays visible regardless of whether the flag is defined.
|
|
205
|
+
Evaluating conditionals belongs to the renderer the finished document is published
|
|
206
|
+
through, not to this editor — showing both branches of an if/else here means neither one
|
|
207
|
+
ever disappears while you're still editing it.
|
|
208
|
+
|
|
209
|
+
```html:preview
|
|
210
|
+
<zn-remarkd-editor value=":product: Remarkd
|
|
211
|
+
|
|
212
|
+
This documents {product}.
|
|
213
|
+
|
|
214
|
+
ifdef::beta[]
|
|
215
|
+
Only for the beta.
|
|
216
|
+
endif::[]"></zn-remarkd-editor>
|
|
217
|
+
```
|
|
218
|
+
|
|
160
219
|
### Form Integration
|
|
161
220
|
|
|
162
221
|
`zn-remarkd-editor` is a [form control](/getting-started/form-controls); its
|
package/package.json
CHANGED
|
@@ -8,8 +8,8 @@ import ZnIcon from "../icon";
|
|
|
8
8
|
import ZnMenuItem from "../menu-item";
|
|
9
9
|
import ZnTooltip from "../tooltip";
|
|
10
10
|
|
|
11
|
-
import styles from './menu.scss';
|
|
12
11
|
import { classMap } from "lit/directives/class-map.js";
|
|
12
|
+
import styles from './menu.scss';
|
|
13
13
|
|
|
14
14
|
interface NavItem {
|
|
15
15
|
title: string;
|
|
@@ -44,6 +44,8 @@ interface NavItem {
|
|
|
44
44
|
* @csspart base - The component's base wrapper.
|
|
45
45
|
*
|
|
46
46
|
* @cssproperty --example - An example CSS custom property.
|
|
47
|
+
* @cssproperty --zn-menu-max-height - Caps the menu's height and makes it scroll internally.
|
|
48
|
+
* Unset (`none`) by default, so the menu grows to fit its items unless a consumer sets this.
|
|
47
49
|
*/
|
|
48
50
|
export default class ZnMenu extends ZincElement {
|
|
49
51
|
static styles: CSSResultGroup = unsafeCSS(styles);
|
|
@@ -101,6 +103,7 @@ export default class ZnMenu extends ZincElement {
|
|
|
101
103
|
render() {
|
|
102
104
|
return html`
|
|
103
105
|
<div
|
|
106
|
+
part="base"
|
|
104
107
|
class="${classMap({
|
|
105
108
|
'menu': true,
|
|
106
109
|
'menu--shell': this.variant === 'shell',
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
border-radius: var(--zn-border-radius);
|
|
9
9
|
width: fit-content;
|
|
10
10
|
min-width: var(--zn-size-spanel);
|
|
11
|
+
// Unconstrained by default — a consumer that needs the menu to scroll inside a bounded
|
|
12
|
+
// popup (rather than growing to fit all its items) sets this custom property.
|
|
13
|
+
max-height: var(--zn-menu-max-height, none);
|
|
11
14
|
overflow: auto;
|
|
12
15
|
overscroll-behavior: none;
|
|
13
16
|
box-shadow: 0 4px 8px -4px rgba(33, 33, 33, 0.1);
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
--zn-page-texture-size: auto;
|
|
15
15
|
--zn-page-content-background: var(--zn-body);
|
|
16
16
|
--zn-page-header-background: var(--zn-page-surface-background, var(--zn-page-background, var(--zn-body)));
|
|
17
|
+
// A modal shell overlays its own close button at --zn-base-gap from the page edge;
|
|
18
|
+
// these describe it so the header can keep clear of it.
|
|
19
|
+
--zn-page-closer-size: 36px;
|
|
20
|
+
--zn-page-closer-gap: var(--zn-spacing-x-small);
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
:host([panel]) {
|
|
@@ -151,6 +155,7 @@
|
|
|
151
155
|
justify-content: space-between;
|
|
152
156
|
gap: 2px;
|
|
153
157
|
max-width: 100%;
|
|
158
|
+
min-width: 0;
|
|
154
159
|
min-height: 36px;
|
|
155
160
|
|
|
156
161
|
a {
|
|
@@ -168,14 +173,14 @@
|
|
|
168
173
|
gap: var(--zn-spacing-x-small);
|
|
169
174
|
min-height: 36px;
|
|
170
175
|
flex-shrink: 1;
|
|
176
|
+
min-width: 0;
|
|
171
177
|
max-width: 100%;
|
|
172
178
|
}
|
|
173
179
|
|
|
174
180
|
&__right {
|
|
175
181
|
flex-grow: 1;
|
|
176
|
-
flex-shrink:
|
|
182
|
+
flex-shrink: 0;
|
|
177
183
|
overflow: hidden;
|
|
178
|
-
min-width: 20px;
|
|
179
184
|
}
|
|
180
185
|
|
|
181
186
|
&__caption {
|
|
@@ -198,6 +203,7 @@
|
|
|
198
203
|
&__description {
|
|
199
204
|
@include wc.text-style(subheading);
|
|
200
205
|
display: block;
|
|
206
|
+
overflow-wrap: anywhere;
|
|
201
207
|
}
|
|
202
208
|
|
|
203
209
|
.breadcrumb {
|
|
@@ -367,6 +373,12 @@ zn-navbar {
|
|
|
367
373
|
padding-inline: calc(var(--zn-gutter, 0) + var(--zn-base-gap, 0));
|
|
368
374
|
}
|
|
369
375
|
|
|
376
|
+
// The gutter shrinks to 0 on narrow containers, which would run the header actions
|
|
377
|
+
// under the closer. Reserving the closer's own footprint holds the gap steady instead.
|
|
378
|
+
:host([modal]) .header {
|
|
379
|
+
padding-inline-end: calc(var(--zn-base-gap, 0) + var(--zn-page-closer-size) + var(--zn-page-closer-gap));
|
|
380
|
+
}
|
|
381
|
+
|
|
370
382
|
:host zn-navbar {
|
|
371
383
|
&::part(navbar) {
|
|
372
384
|
padding-inline: calc(var(--zn-gutter, 0) + var(--zn-base-gap, 0));
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type {SlashMenuItem} from '../slash-menu';
|
|
2
|
+
|
|
3
|
+
export type ActionGroup =
|
|
4
|
+
'text' | 'lists' | 'admonitions' | 'blocks' | 'structured' | 'media' | 'objects' | 'breaks' | 'logic' | 'inline';
|
|
5
|
+
|
|
6
|
+
/** What an inline action wraps the selection in. `after` defaults to `before`. */
|
|
7
|
+
export interface InlineMark {
|
|
8
|
+
before: string;
|
|
9
|
+
after?: string;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface EditorAction {
|
|
14
|
+
/** Stable id, used for toolbar keys and tests. */
|
|
15
|
+
key: string;
|
|
16
|
+
label: string;
|
|
17
|
+
icon: string;
|
|
18
|
+
group: ActionGroup;
|
|
19
|
+
/** Extra search terms for the slash menu, e.g. "bold" finds Strong. */
|
|
20
|
+
keywords?: string[];
|
|
21
|
+
/** Block actions: spliced in as a new block. */
|
|
22
|
+
prefix?: string;
|
|
23
|
+
/** Inline actions: applied to the selection in the open block. */
|
|
24
|
+
inline?: InlineMark;
|
|
25
|
+
/** Where the caret lands within `prefix`. Defaults to the end. */
|
|
26
|
+
caretOffset?: number;
|
|
27
|
+
/** Actions that open their own picker instead of inserting text. */
|
|
28
|
+
opens?: 'image' | 'include';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Toolbar order, most-used first — the last groups are the first to collapse. */
|
|
32
|
+
export const ACTION_GROUPS: { id: ActionGroup; label: string }[] = [
|
|
33
|
+
{id: 'text', label: 'Text'},
|
|
34
|
+
// Bold/italic/code are the most-reached actions in any editor — kept right after Text so
|
|
35
|
+
// they are among the last groups to collapse, not the first.
|
|
36
|
+
{id: 'inline', label: 'Inline'},
|
|
37
|
+
{id: 'lists', label: 'Lists'},
|
|
38
|
+
{id: 'admonitions', label: 'Admonitions'},
|
|
39
|
+
{id: 'blocks', label: 'Blocks'},
|
|
40
|
+
{id: 'structured', label: 'Structured'},
|
|
41
|
+
{id: 'media', label: 'Media'},
|
|
42
|
+
{id: 'objects', label: 'Objects'},
|
|
43
|
+
{id: 'breaks', label: 'Breaks'},
|
|
44
|
+
{id: 'logic', label: 'Logic'},
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
export const EDITOR_ACTIONS: EditorAction[] = [
|
|
48
|
+
// Text
|
|
49
|
+
{key: 'text', label: 'Text', icon: 'text@lu', group: 'text', prefix: ''},
|
|
50
|
+
{key: 'paragraphs', label: 'Paragraph', icon: 'pilcrow@lu', group: 'text', prefix: ''},
|
|
51
|
+
{key: 'heading', label: 'Heading 1', icon: 'heading-1@lu', group: 'text', prefix: '# '},
|
|
52
|
+
{key: 'h2', label: 'Heading 2', icon: 'heading-2@lu', group: 'text', prefix: '## '},
|
|
53
|
+
{key: 'h3', label: 'Heading 3', icon: 'heading-3@lu', group: 'text', prefix: '### '},
|
|
54
|
+
{key: 'asciidoc-section', label: 'Section', icon: 'section@lu', group: 'text', prefix: '== '},
|
|
55
|
+
{key: 'attributes-title', label: 'Title', icon: 'captions@lu', group: 'text', prefix: '.'},
|
|
56
|
+
|
|
57
|
+
// Lists
|
|
58
|
+
{key: 'unordered-list', label: 'Bullet list', icon: 'list@lu', group: 'lists', prefix: '- ', keywords: ['bullet', 'ul']},
|
|
59
|
+
{key: 'ordered-list', label: 'Numbered list', icon: 'list-ordered@lu', group: 'lists', prefix: '1. ', keywords: ['ol']},
|
|
60
|
+
{key: 'checkboxes', label: 'Checklist', icon: 'list-todo@lu', group: 'lists', prefix: '- [ ] ', keywords: ['task', 'todo']},
|
|
61
|
+
{key: 'definition-list', label: 'Definition list', icon: 'book-a@lu', group: 'lists', prefix: 'Term:: Definition'},
|
|
62
|
+
|
|
63
|
+
// Admonitions
|
|
64
|
+
{key: 'admonition', label: 'Note', icon: 'info@lu', group: 'admonitions', prefix: 'NOTE: '},
|
|
65
|
+
{key: 'tip', label: 'Tip', icon: 'lightbulb@lu', group: 'admonitions', prefix: 'TIP: '},
|
|
66
|
+
{key: 'warning', label: 'Warning', icon: 'triangle-alert@lu', group: 'admonitions', prefix: 'WARNING: '},
|
|
67
|
+
{key: 'important', label: 'Important', icon: 'circle-alert@lu', group: 'admonitions', prefix: 'IMPORTANT: '},
|
|
68
|
+
{key: 'caution', label: 'Caution', icon: 'shield-alert@lu', group: 'admonitions', prefix: 'CAUTION: '},
|
|
69
|
+
{key: 'danger', label: 'Danger', icon: 'octagon-alert@lu', group: 'admonitions', prefix: 'DANGER: '},
|
|
70
|
+
{key: 'success', label: 'Success', icon: 'circle-check@lu', group: 'admonitions', prefix: 'SUCCESS: '},
|
|
71
|
+
{key: 'notice', label: 'Notice', icon: 'megaphone@lu', group: 'admonitions', prefix: 'NOTICE: '},
|
|
72
|
+
|
|
73
|
+
// Blocks — caretOffset lands inside the delimiters
|
|
74
|
+
{key: 'code-fence', label: 'Code', icon: 'code@lu', group: 'blocks', prefix: '```\n\n```', caretOffset: 4},
|
|
75
|
+
{key: 'blockquotes', label: 'Quote', icon: 'quote@lu', group: 'blocks', prefix: '____\n\n____', caretOffset: 5},
|
|
76
|
+
{key: 'verse', label: 'Verse', icon: 'feather@lu', group: 'blocks', prefix: '[verse]\n____\n\n____', caretOffset: 13},
|
|
77
|
+
{key: 'sidebar-block', label: 'Sidebar', icon: 'panel-right@lu', group: 'blocks', prefix: '****\n\n****', caretOffset: 5},
|
|
78
|
+
{key: 'example-block', label: 'Example', icon: 'square-dashed@lu', group: 'blocks', prefix: '====\n\n====', caretOffset: 5},
|
|
79
|
+
{key: 'literal-block', label: 'Literal', icon: 'file-code@lu', group: 'blocks', prefix: '....\n\n....', caretOffset: 5},
|
|
80
|
+
{key: 'listing-block', label: 'Listing', icon: 'terminal@lu', group: 'blocks', prefix: '----\n\n----', caretOffset: 5},
|
|
81
|
+
{key: 'generic-container', label: 'Container', icon: 'box@lu', group: 'blocks', prefix: '!!!!\n\n!!!!', caretOffset: 5},
|
|
82
|
+
{key: 'comments', label: 'Comment', icon: 'message-square-dashed@lu', group: 'blocks', prefix: '////\n\n////', caretOffset: 5},
|
|
83
|
+
{key: 'hardbreaks', label: 'Hard breaks', icon: 'wrap-text@lu', group: 'blocks', prefix: '[%hardbreaks]\n', keywords: ['line breaks']},
|
|
84
|
+
|
|
85
|
+
// Structured
|
|
86
|
+
{key: 'tabs', label: 'Tabs', icon: 'app-window@lu', group: 'structured', prefix: '_|_#first [name=First]\nFirst content', caretOffset: 16},
|
|
87
|
+
{key: 'steps', label: 'Steps', icon: 'list-checks@lu', group: 'structured', prefix: '_|- First step\nDo first', caretOffset: 4},
|
|
88
|
+
{key: 'callout-block', label: 'Callout', icon: 'message-square-code@lu', group: 'structured', prefix: '<1> '},
|
|
89
|
+
|
|
90
|
+
// Media
|
|
91
|
+
{key: 'asciidoc-image', label: 'Image', icon: 'image@lu', group: 'media', opens: 'image'},
|
|
92
|
+
{key: 'image-alignment', label: 'Aligned image', icon: 'image-plus@lu', group: 'media', prefix: '[.align-center]\nimage::file.png[Alt,640,480]', caretOffset: 23},
|
|
93
|
+
{key: 'video', label: 'Video', icon: 'video@lu', group: 'media', prefix: '{{video:ID source=youtube}}', caretOffset: 8},
|
|
94
|
+
{key: 'include', label: 'Include', icon: 'blocks@lu', group: 'media', opens: 'include'},
|
|
95
|
+
// The path placeholder is load-bearing: `t::partial::` with an empty path makes parse() throw.
|
|
96
|
+
{key: 'partial', label: 'Partial', icon: 'file-stack@lu', group: 'media', prefix: 't::partial::path/to/file.remarkd', caretOffset: 12},
|
|
97
|
+
{key: 'reference-list', label: 'Reference list', icon: 'book-marked@lu', group: 'media', prefix: '{{reflist}}'},
|
|
98
|
+
|
|
99
|
+
// Objects
|
|
100
|
+
{key: 'button-object', label: 'Button', icon: 'mouse-pointer-click@lu', group: 'objects', prefix: '{{button:action text="Label" href=/}}', caretOffset: 9},
|
|
101
|
+
{key: 'object-macros', label: 'Link object', icon: 'external-link@lu', group: 'objects', prefix: '{{link:https:// text=Label}}', caretOffset: 7},
|
|
102
|
+
{key: 'anchor', label: 'Anchor', icon: 'anchor@lu', group: 'objects', prefix: '{{anchor name=top}}', caretOffset: 14},
|
|
103
|
+
{key: 'meter', label: 'Meter', icon: 'gauge@lu', group: 'objects', prefix: '{{meter id=progress min=0 max=10 value=5}}', caretOffset: 11},
|
|
104
|
+
{key: 'object-attributes', label: 'Break object', icon: 'separator-horizontal@lu', group: 'objects', prefix: '{{br}}'},
|
|
105
|
+
|
|
106
|
+
// Breaks
|
|
107
|
+
{key: 'horizontal-rule', label: 'Divider', icon: 'minus@lu', group: 'breaks', prefix: '---', keywords: ['rule', 'hr']},
|
|
108
|
+
{key: 'page-break', label: 'Page break', icon: 'scissors-line-dashed@lu', group: 'breaks', prefix: '<<<'},
|
|
109
|
+
{key: 'line-continuation', label: 'Line continuation', icon: 'corner-down-left@lu', group: 'breaks', prefix: ' \\'},
|
|
110
|
+
|
|
111
|
+
// Logic — marked in the preview, never evaluated
|
|
112
|
+
{key: 'document-attributes', label: 'Variable', icon: 'variable@lu', group: 'logic', prefix: ':name: value', caretOffset: 1, keywords: ['attribute', 'variable']},
|
|
113
|
+
{key: 'conditionals', label: 'If defined', icon: 'git-branch@lu', group: 'logic', prefix: 'ifdef::flag[]\n\nendif::[]', caretOffset: 7},
|
|
114
|
+
{key: 'ifndef', label: 'If not defined', icon: 'git-branch-minus@lu', group: 'logic', prefix: 'ifndef::flag[]\n\nendif::[]', caretOffset: 8},
|
|
115
|
+
{key: 'iftrue', label: 'If true', icon: 'toggle-right@lu', group: 'logic', prefix: 'iftrue::flag[Shown]', caretOffset: 8},
|
|
116
|
+
{key: 'iffalse', label: 'If false', icon: 'toggle-left@lu', group: 'logic', prefix: 'iffalse::flag[Shown]', caretOffset: 9},
|
|
117
|
+
{key: 'ifempty', label: 'If empty', icon: 'square@lu', group: 'logic', prefix: 'ifempty::flag[Shown]', caretOffset: 9},
|
|
118
|
+
{key: 'ifnempty', label: 'If not empty', icon: 'square-check@lu', group: 'logic', prefix: 'ifnempty::flag[Shown]', caretOffset: 10},
|
|
119
|
+
{key: 'ifeval', label: 'If expression', icon: 'equal@lu', group: 'logic', prefix: 'ifeval::[1 > 0]\n\nendif::[]', caretOffset: 9},
|
|
120
|
+
|
|
121
|
+
// Inline — applied to the selection in the open block
|
|
122
|
+
{key: 'inline-formatting', label: 'Strong', icon: 'bold@lu', group: 'inline', keywords: ['bold'], inline: {before: '**', placeholder: 'text'}},
|
|
123
|
+
{key: 'emphasis', label: 'Emphasis', icon: 'italic@lu', group: 'inline', keywords: ['italic'], inline: {before: '__', placeholder: 'text'}},
|
|
124
|
+
{key: 'inline-code', label: 'Code', icon: 'square-code@lu', group: 'inline', inline: {before: '`', placeholder: 'code'}},
|
|
125
|
+
{key: 'underline', label: 'Underline', icon: 'underline@lu', group: 'inline', inline: {before: '___', placeholder: 'text'}},
|
|
126
|
+
{key: 'strike', label: 'Strikethrough', icon: 'strikethrough@lu', group: 'inline', keywords: ['delete'], inline: {before: '~~', placeholder: 'text'}},
|
|
127
|
+
{key: 'highlight', label: 'Highlight', icon: 'highlighter@lu', group: 'inline', keywords: ['mark'], inline: {before: '#', placeholder: 'text'}},
|
|
128
|
+
{key: 'subscript', label: 'Subscript', icon: 'subscript@lu', group: 'inline', inline: {before: '~', placeholder: '2'}},
|
|
129
|
+
{key: 'superscript', label: 'Superscript', icon: 'superscript@lu', group: 'inline', inline: {before: '^', placeholder: '2'}},
|
|
130
|
+
{key: 'keyboard', label: 'Keyboard', icon: 'keyboard@lu', group: 'inline', keywords: ['kbd', 'shortcut'], inline: {before: 'kbd:[', after: ']', placeholder: 'Ctrl+C'}},
|
|
131
|
+
{key: 'footnote', label: 'Footnote', icon: 'asterisk@lu', group: 'inline', inline: {before: 'footnote:[', after: ']', placeholder: 'Note'}},
|
|
132
|
+
{key: 'tooltip', label: 'Tooltip', icon: 'message-circle-question-mark@lu', group: 'inline', keywords: ['term'], inline: {before: '{', after: '}(Explanation)', placeholder: 'Term'}},
|
|
133
|
+
{key: 'cross-reference', label: 'Cross reference', icon: 'link-2@lu', group: 'inline', keywords: ['xref'], inline: {before: '<<', after: '>>', placeholder: 'section,Label'}},
|
|
134
|
+
{key: 'links-and-images', label: 'Link', icon: 'link@lu', group: 'inline', inline: {before: '[', after: '](https://)', placeholder: 'Label'}},
|
|
135
|
+
{key: 'passthrough', label: 'Passthrough', icon: 'shield@lu', group: 'inline', keywords: ['raw', 'literal'], inline: {before: 'pass:[', after: ']', placeholder: 'raw'}},
|
|
136
|
+
{key: 'curly-bang-passthrough', label: 'Literal braces', icon: 'braces@lu', group: 'inline', inline: {before: '{!', after: '!}', placeholder: 'raw'}},
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
/** The registry as slash menu entries. Picker actions insert nothing; the menu emits their action id. */
|
|
140
|
+
export function slashItems(actions: EditorAction[]): SlashMenuItem[] {
|
|
141
|
+
const label = (id: ActionGroup) => ACTION_GROUPS.find(group => group.id === id)?.label;
|
|
142
|
+
return actions.map(action => {
|
|
143
|
+
if (action.opens) {
|
|
144
|
+
return {label: action.label, icon: action.icon, action: action.opens, group: label(action.group), keywords: action.keywords};
|
|
145
|
+
}
|
|
146
|
+
// Inline actions have no textarea selection to wrap from the slash menu, so insert the
|
|
147
|
+
// whole construct with the caret at the placeholder — a bare `before` would leave the
|
|
148
|
+
// mark unclosed (`**` with nothing selected to wrap).
|
|
149
|
+
const {before, after = before, placeholder = ''} = action.inline ?? {before: action.prefix ?? ''};
|
|
150
|
+
return {
|
|
151
|
+
label: action.label,
|
|
152
|
+
icon: action.icon,
|
|
153
|
+
group: label(action.group),
|
|
154
|
+
keywords: action.keywords,
|
|
155
|
+
value: action.inline ? before + placeholder + after : before,
|
|
156
|
+
caretOffset: action.inline ? before.length : action.caretOffset,
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The remarkd feature fixtures, from `node_modules/remarkd-js/requirements/features/`.
|
|
3
|
+
* Regenerate with: ls node_modules/remarkd-js/requirements/features
|
|
4
|
+
*/
|
|
5
|
+
export const FEATURE_KEYS: string[] = [
|
|
6
|
+
'accordion', 'admonition', 'asciidoc-image', 'asciidoc-section', 'attributes-title', 'autolink',
|
|
7
|
+
'blockquotes', 'button-object', 'callout-block', 'checkboxes', 'code-fence', 'comments',
|
|
8
|
+
'conditionals', 'conditionals-advanced', 'cross-reference', 'curly-bang-passthrough',
|
|
9
|
+
'definition-list', 'document-attributes', 'emoji-aliases', 'example-block', 'footnote',
|
|
10
|
+
'generic-container', 'hardbreaks', 'heading', 'highlight', 'horizontal-rule', 'id-block',
|
|
11
|
+
'image-alignment', 'include', 'inline-advanced', 'inline-formatting', 'keyboard',
|
|
12
|
+
'line-continuation', 'links-and-images', 'listing-block', 'literal-block', 'object-attributes',
|
|
13
|
+
'object-fallbacks', 'object-macros', 'ordered-list', 'page-break', 'paragraphs', 'partial',
|
|
14
|
+
'passthrough', 'pros-cons', 'reference-list', 'sidebar-block', 'smart-quotes', 'steps',
|
|
15
|
+
'subscript', 'superscript', 'table', 'tabs', 'tooltip', 'typographic-symbols',
|
|
16
|
+
'unordered-list', 'url-formatting-chars', 'verse', 'video',
|
|
17
|
+
];
|