@threadlabs/looma 0.7.1 → 0.7.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.
@@ -1,14 +1,79 @@
1
1
  import { n as TABLE_CELL_BACKGROUND_PRESETS } from "./table-backgrounds.js";
2
2
  import { Extension, Mark, Node, callOrReturn, canInsertNode, combineTransactionSteps, escapeForRegEx, findChildren, findChildrenInRange, findParentNodeClosestToPos, getAttributes, getChangedRanges, getExtensionField, getMarksBetween, isNodeEmpty, isNodeSelection, markInputRule, markPasteRule, mergeAttributes, nodeInputRule, textInputRule, textblockTypeInputRule, wrappingInputRule } from "@tiptap/core";
3
3
  import { NodeSelection, Plugin, PluginKey, Selection, TextSelection } from "@tiptap/pm/state";
4
+ import { Decoration, DecorationSet } from "@tiptap/pm/view";
4
5
  import { history, redo, undo } from "@tiptap/pm/history";
5
6
  import { dropCursor } from "@tiptap/pm/dropcursor";
6
7
  import { gapCursor } from "@tiptap/pm/gapcursor";
7
- import { Decoration, DecorationSet } from "@tiptap/pm/view";
8
8
  import { common, createLowlight } from "lowlight";
9
9
  import { DOMParser, Node as Node$1 } from "@tiptap/pm/model";
10
10
  import MarkdownIt from "markdown-it";
11
11
  import { CellSelection, TableMap, addColumn, addColumnAfter, addColumnBefore, addRow, addRowAfter, addRowBefore, cellAround, columnResizing, deleteCellSelection, deleteColumn, deleteRow, deleteTable, fixTables, goToNextCell, mergeCells, setCellAttr, splitCell, tableEditing, toggleHeader, toggleHeaderCell } from "@tiptap/pm/tables";
12
+ //#region src/editor/extensions/active-block.ts
13
+ /**
14
+ * Marks the top-level block holding the caret so a stylesheet can show where
15
+ * editing is happening without drawing chrome inside the reading column.
16
+ *
17
+ * The decoration lands on the document's direct child, not on the textblock the
18
+ * caret sits in: a marker that tracked the caret's own line would move on every
19
+ * wrap and every arrow key, and one that tracked a nested list item would step
20
+ * in and out of the list's inset. The outermost block is the stable answer to
21
+ * "where am I", which is the question the marker exists to answer.
22
+ */
23
+ /** Class applied to the decorated block; `looma-editor.css` owns its appearance. */
24
+ var LOOMA_ACTIVE_BLOCK_CLASS = "looma-active-block";
25
+ /** Plugin state is the editor's focus, so the marker is absent while reading. */
26
+ var LoomaActiveBlockPluginKey = new PluginKey("loomaActiveBlock");
27
+ /**
28
+ * Decorates the outermost block containing the selection head while the editor
29
+ * has focus.
30
+ *
31
+ * @contract Adds `LOOMA_ACTIVE_BLOCK_CLASS` to at most one top-level block, and
32
+ * nothing at all when the editor is unfocused or not editable. The document is
33
+ * never modified.
34
+ * @invariant Focus is tracked in plugin state rather than read from the editor,
35
+ * so the decoration recomputes on focus and blur even when neither changes the
36
+ * selection.
37
+ * @ownership The consuming stylesheet owns every visual property; this extension
38
+ * owns only which block carries the class.
39
+ */
40
+ var LoomaActiveBlock = Extension.create({
41
+ name: "loomaActiveBlock",
42
+ addProseMirrorPlugins() {
43
+ return [new Plugin({
44
+ key: LoomaActiveBlockPluginKey,
45
+ state: {
46
+ init: () => false,
47
+ apply: (transaction, focused) => {
48
+ const next = transaction.getMeta(LoomaActiveBlockPluginKey);
49
+ return typeof next === "boolean" ? next : focused;
50
+ }
51
+ },
52
+ props: {
53
+ handleDOMEvents: {
54
+ focus: (view) => {
55
+ view.dispatch(view.state.tr.setMeta(LoomaActiveBlockPluginKey, true));
56
+ return false;
57
+ },
58
+ blur: (view) => {
59
+ view.dispatch(view.state.tr.setMeta(LoomaActiveBlockPluginKey, false));
60
+ return false;
61
+ }
62
+ },
63
+ decorations: (state) => {
64
+ if (!LoomaActiveBlockPluginKey.getState(state)) return null;
65
+ const { $head } = state.selection;
66
+ if ($head.depth === 0) return null;
67
+ const from = $head.before(1);
68
+ const block = state.doc.nodeAt(from);
69
+ if (!block) return null;
70
+ return DecorationSet.create(state.doc, [Decoration.node(from, from + block.nodeSize, { class: LOOMA_ACTIVE_BLOCK_CLASS })]);
71
+ }
72
+ }
73
+ })];
74
+ }
75
+ });
76
+ //#endregion
12
77
  //#region src/editor/extensions/callout.ts
13
78
  /** Canonical tone values that may be persisted in a Looma callout node. */
14
79
  var LOOMA_CALLOUT_TONES = [
@@ -6631,7 +6696,8 @@ function getDefaultEditorExtensions(options = {}) {
6631
6696
  }),
6632
6697
  ...mention ? [mention] : [],
6633
6698
  LoomaTableKit,
6634
- LoomaListBehavior
6699
+ LoomaListBehavior,
6700
+ LoomaActiveBlock
6635
6701
  ];
6636
6702
  }
6637
6703
  //#endregion
@@ -7264,4 +7330,4 @@ function createLoomaSlashCommandExtension(options = {}) {
7264
7330
  });
7265
7331
  }
7266
7332
  //#endregion
7267
- export { DEFAULT_MENTION_RESULT_LIMIT as C, LOOMA_CALLOUT_TONES as D, normalizeMentionResultLimit as E, LoomaCallout as O, createLoomaMentionExtension as S, filterLoomaMentionItems as T, getActiveTableCellBackground as _, handleTableAction as a, LoomaSmartPaste as b, normalizeActiveTableColumnWidths as c, getDefaultEditorExtensions as d, getLoomaTableExtensions as f, getActiveTableCellAlignment as g, LoomaTableHeader as h, getActiveTableUiState as i, shouldShowTextFormattingToolbar as l, LoomaTableCell as m, createLoomaSlashCommandExtension as n, handleTableOverlayAction as o, LoomaTable as p, getDefaultSlashCommands as r, insertTableAtRange as s, LoomaSlashCommand as t, LoomaTableKit as u, setActiveTableCellAlignment as v, MAX_MENTION_RESULT_LIMIT as w, LoomaMentionSuggestionPluginKey as x, setActiveTableCellBackground as y };
7333
+ export { LoomaActiveBlock as A, DEFAULT_MENTION_RESULT_LIMIT as C, LOOMA_CALLOUT_TONES as D, normalizeMentionResultLimit as E, LoomaCallout as O, createLoomaMentionExtension as S, filterLoomaMentionItems as T, getActiveTableCellBackground as _, handleTableAction as a, LoomaSmartPaste as b, normalizeActiveTableColumnWidths as c, getDefaultEditorExtensions as d, getLoomaTableExtensions as f, getActiveTableCellAlignment as g, LoomaTableHeader as h, getActiveTableUiState as i, LoomaActiveBlockPluginKey as j, LOOMA_ACTIVE_BLOCK_CLASS as k, shouldShowTextFormattingToolbar as l, LoomaTableCell as m, createLoomaSlashCommandExtension as n, handleTableOverlayAction as o, LoomaTable as p, getDefaultSlashCommands as r, insertTableAtRange as s, LoomaSlashCommand as t, LoomaTableKit as u, setActiveTableCellAlignment as v, MAX_MENTION_RESULT_LIMIT as w, LoomaMentionSuggestionPluginKey as x, setActiveTableCellBackground as y };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Marks the top-level block holding the caret so a stylesheet can show where
3
+ * editing is happening without drawing chrome inside the reading column.
4
+ *
5
+ * The decoration lands on the document's direct child, not on the textblock the
6
+ * caret sits in: a marker that tracked the caret's own line would move on every
7
+ * wrap and every arrow key, and one that tracked a nested list item would step
8
+ * in and out of the list's inset. The outermost block is the stable answer to
9
+ * "where am I", which is the question the marker exists to answer.
10
+ */
11
+ import { Extension } from "@tiptap/core";
12
+ import { PluginKey } from "@tiptap/pm/state";
13
+ /** Class applied to the decorated block; `looma-editor.css` owns its appearance. */
14
+ export declare const LOOMA_ACTIVE_BLOCK_CLASS = "looma-active-block";
15
+ /** Plugin state is the editor's focus, so the marker is absent while reading. */
16
+ export declare const LoomaActiveBlockPluginKey: PluginKey<boolean>;
17
+ /**
18
+ * Decorates the outermost block containing the selection head while the editor
19
+ * has focus.
20
+ *
21
+ * @contract Adds `LOOMA_ACTIVE_BLOCK_CLASS` to at most one top-level block, and
22
+ * nothing at all when the editor is unfocused or not editable. The document is
23
+ * never modified.
24
+ * @invariant Focus is tracked in plugin state rather than read from the editor,
25
+ * so the decoration recomputes on focus and blur even when neither changes the
26
+ * selection.
27
+ * @ownership The consuming stylesheet owns every visual property; this extension
28
+ * owns only which block carries the class.
29
+ */
30
+ export declare const LoomaActiveBlock: Extension<any, any>;
@@ -6,6 +6,7 @@
6
6
  * application owns wiring and editor-instance lifetime. Tiptap remains a peer
7
7
  * dependency so Looma and the host execute against the same editor runtime.
8
8
  */
9
+ export { LOOMA_ACTIVE_BLOCK_CLASS, LoomaActiveBlock, LoomaActiveBlockPluginKey, } from "./active-block.js";
9
10
  export { LoomaCallout, LOOMA_CALLOUT_TONES, type LoomaCalloutTone, } from "./callout.js";
10
11
  export { getDefaultEditorExtensions, getLoomaTableExtensions, LoomaTableKit, type DefaultEditorExtensionsOptions, } from "./preset.js";
11
12
  export { LoomaSmartPaste } from "./smart-paste.js";
@@ -1,3 +1,3 @@
1
1
  import { n as TABLE_CELL_BACKGROUND_PRESETS } from "../chunks/table-backgrounds.js";
2
- import { C as DEFAULT_MENTION_RESULT_LIMIT, D as LOOMA_CALLOUT_TONES, E as normalizeMentionResultLimit, O as LoomaCallout, S as createLoomaMentionExtension, T as filterLoomaMentionItems, _ as getActiveTableCellBackground, a as handleTableAction, b as LoomaSmartPaste, c as normalizeActiveTableColumnWidths, d as getDefaultEditorExtensions, f as getLoomaTableExtensions, g as getActiveTableCellAlignment, h as LoomaTableHeader, i as getActiveTableUiState, l as shouldShowTextFormattingToolbar, m as LoomaTableCell, n as createLoomaSlashCommandExtension, o as handleTableOverlayAction, p as LoomaTable, r as getDefaultSlashCommands, s as insertTableAtRange, t as LoomaSlashCommand, u as LoomaTableKit, v as setActiveTableCellAlignment, w as MAX_MENTION_RESULT_LIMIT, x as LoomaMentionSuggestionPluginKey, y as setActiveTableCellBackground } from "../chunks/extensions.js";
3
- export { DEFAULT_MENTION_RESULT_LIMIT, LOOMA_CALLOUT_TONES, LoomaCallout, LoomaMentionSuggestionPluginKey, LoomaSlashCommand, LoomaSmartPaste, LoomaTable, LoomaTableCell, LoomaTableHeader, LoomaTableKit, MAX_MENTION_RESULT_LIMIT, TABLE_CELL_BACKGROUND_PRESETS, createLoomaMentionExtension, createLoomaSlashCommandExtension, filterLoomaMentionItems, getActiveTableCellAlignment, getActiveTableCellBackground, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getLoomaTableExtensions, handleTableAction, handleTableOverlayAction, insertTableAtRange, normalizeActiveTableColumnWidths, normalizeMentionResultLimit, setActiveTableCellAlignment, setActiveTableCellBackground, shouldShowTextFormattingToolbar };
2
+ import { A as LoomaActiveBlock, C as DEFAULT_MENTION_RESULT_LIMIT, D as LOOMA_CALLOUT_TONES, E as normalizeMentionResultLimit, O as LoomaCallout, S as createLoomaMentionExtension, T as filterLoomaMentionItems, _ as getActiveTableCellBackground, a as handleTableAction, b as LoomaSmartPaste, c as normalizeActiveTableColumnWidths, d as getDefaultEditorExtensions, f as getLoomaTableExtensions, g as getActiveTableCellAlignment, h as LoomaTableHeader, i as getActiveTableUiState, j as LoomaActiveBlockPluginKey, k as LOOMA_ACTIVE_BLOCK_CLASS, l as shouldShowTextFormattingToolbar, m as LoomaTableCell, n as createLoomaSlashCommandExtension, o as handleTableOverlayAction, p as LoomaTable, r as getDefaultSlashCommands, s as insertTableAtRange, t as LoomaSlashCommand, u as LoomaTableKit, v as setActiveTableCellAlignment, w as MAX_MENTION_RESULT_LIMIT, x as LoomaMentionSuggestionPluginKey, y as setActiveTableCellBackground } from "../chunks/extensions.js";
3
+ export { DEFAULT_MENTION_RESULT_LIMIT, LOOMA_ACTIVE_BLOCK_CLASS, LOOMA_CALLOUT_TONES, LoomaActiveBlock, LoomaActiveBlockPluginKey, LoomaCallout, LoomaMentionSuggestionPluginKey, LoomaSlashCommand, LoomaSmartPaste, LoomaTable, LoomaTableCell, LoomaTableHeader, LoomaTableKit, MAX_MENTION_RESULT_LIMIT, TABLE_CELL_BACKGROUND_PRESETS, createLoomaMentionExtension, createLoomaSlashCommandExtension, filterLoomaMentionItems, getActiveTableCellAlignment, getActiveTableCellBackground, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getLoomaTableExtensions, handleTableAction, handleTableOverlayAction, insertTableAtRange, normalizeActiveTableColumnWidths, normalizeMentionResultLimit, setActiveTableCellAlignment, setActiveTableCellBackground, shouldShowTextFormattingToolbar };
package/editor/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { n as TABLE_CELL_BACKGROUND_PRESETS, t as TABLE_CELL_BACKGROUND_OPTIONS } from "./chunks/table-backgrounds.js";
2
2
  import { n as resolveTableCellAt, t as measureTableOverlayGeometry } from "./chunks/ui.js";
3
- import { C as DEFAULT_MENTION_RESULT_LIMIT, D as LOOMA_CALLOUT_TONES, E as normalizeMentionResultLimit, O as LoomaCallout, S as createLoomaMentionExtension, T as filterLoomaMentionItems, _ as getActiveTableCellBackground, a as handleTableAction, b as LoomaSmartPaste, c as normalizeActiveTableColumnWidths, d as getDefaultEditorExtensions, f as getLoomaTableExtensions, g as getActiveTableCellAlignment, h as LoomaTableHeader, i as getActiveTableUiState, l as shouldShowTextFormattingToolbar, m as LoomaTableCell, n as createLoomaSlashCommandExtension, o as handleTableOverlayAction, p as LoomaTable, r as getDefaultSlashCommands, s as insertTableAtRange, t as LoomaSlashCommand, u as LoomaTableKit, v as setActiveTableCellAlignment, w as MAX_MENTION_RESULT_LIMIT, x as LoomaMentionSuggestionPluginKey, y as setActiveTableCellBackground } from "./chunks/extensions.js";
3
+ import { A as LoomaActiveBlock, C as DEFAULT_MENTION_RESULT_LIMIT, D as LOOMA_CALLOUT_TONES, E as normalizeMentionResultLimit, O as LoomaCallout, S as createLoomaMentionExtension, T as filterLoomaMentionItems, _ as getActiveTableCellBackground, a as handleTableAction, b as LoomaSmartPaste, c as normalizeActiveTableColumnWidths, d as getDefaultEditorExtensions, f as getLoomaTableExtensions, g as getActiveTableCellAlignment, h as LoomaTableHeader, i as getActiveTableUiState, j as LoomaActiveBlockPluginKey, k as LOOMA_ACTIVE_BLOCK_CLASS, l as shouldShowTextFormattingToolbar, m as LoomaTableCell, n as createLoomaSlashCommandExtension, o as handleTableOverlayAction, p as LoomaTable, r as getDefaultSlashCommands, s as insertTableAtRange, t as LoomaSlashCommand, u as LoomaTableKit, v as setActiveTableCellAlignment, w as MAX_MENTION_RESULT_LIMIT, x as LoomaMentionSuggestionPluginKey, y as setActiveTableCellBackground } from "./chunks/extensions.js";
4
4
  import { AlignCenter, AlignLeft, AlignRight, Bold, Braces, ChevronDown, ChevronLeft, CircleCheck, CircleX, CodeXml, Columns3, Ellipsis, Eraser, GripHorizontal, GripVertical, Heading1, Heading2, Heading3, Highlighter, Image, Info, Italic, List, ListOrdered, ListTodo, Merge, Minus, NotebookPen, PaintBucket, PanelBottom, PanelLeft, PanelRight, PanelTop, Pilcrow, Plus, Quote, Redo2, Rows3, Split, Strikethrough, Table2, Trash2, TriangleAlert, Underline, Undo2 } from "lucide";
5
5
  //#region src/editor/icons.ts
6
6
  /** The opinionated Lucide set used by Looma's shipped interaction surfaces. */
@@ -94,4 +94,4 @@ function getVisualViewportRect(owner = window) {
94
94
  };
95
95
  }
96
96
  //#endregion
97
- export { DEFAULT_MENTION_RESULT_LIMIT, LOOMA_CALLOUT_TONES, LOOMA_ICONS, LoomaCallout, LoomaMentionSuggestionPluginKey, LoomaSlashCommand, LoomaSmartPaste, LoomaTable, LoomaTableCell, LoomaTableHeader, LoomaTableKit, MAX_MENTION_RESULT_LIMIT, TABLE_CELL_BACKGROUND_OPTIONS, TABLE_CELL_BACKGROUND_PRESETS, createLoomaMentionExtension, createLoomaSlashCommandExtension, filterLoomaMentionItems, getActiveTableCellAlignment, getActiveTableCellBackground, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getLoomaTableExtensions, getVisualViewportRect, handleTableAction, handleTableOverlayAction, insertTableAtRange, loomaIconMarkup, measureTableOverlayGeometry, normalizeActiveTableColumnWidths, normalizeMentionResultLimit, resolveTableCellAt, setActiveTableCellAlignment, setActiveTableCellBackground, shouldShowTextFormattingToolbar };
97
+ export { DEFAULT_MENTION_RESULT_LIMIT, LOOMA_ACTIVE_BLOCK_CLASS, LOOMA_CALLOUT_TONES, LOOMA_ICONS, LoomaActiveBlock, LoomaActiveBlockPluginKey, LoomaCallout, LoomaMentionSuggestionPluginKey, LoomaSlashCommand, LoomaSmartPaste, LoomaTable, LoomaTableCell, LoomaTableHeader, LoomaTableKit, MAX_MENTION_RESULT_LIMIT, TABLE_CELL_BACKGROUND_OPTIONS, TABLE_CELL_BACKGROUND_PRESETS, createLoomaMentionExtension, createLoomaSlashCommandExtension, filterLoomaMentionItems, getActiveTableCellAlignment, getActiveTableCellBackground, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getLoomaTableExtensions, getVisualViewportRect, handleTableAction, handleTableOverlayAction, insertTableAtRange, loomaIconMarkup, measureTableOverlayGeometry, normalizeActiveTableColumnWidths, normalizeMentionResultLimit, resolveTableCellAt, setActiveTableCellAlignment, setActiveTableCellBackground, shouldShowTextFormattingToolbar };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threadlabs/looma",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "type": "module",
5
5
  "description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
6
6
  "license": "MIT",
@@ -180,7 +180,7 @@
180
180
  }
181
181
 
182
182
  /* Disabled drops the tone rather than the shape: every variant keeps its own structure, so a
183
- disabled outline is still an outline and a disabled ghost is still text. Intent goes — an
183
+ disabled outline is still an outline and a disabled ghost still has no edge. Intent goes — an
184
184
  unavailable action says "unavailable", not "unavailable, and destructive" — and so do the
185
185
  highlight and shadow, because nothing that cannot be pressed should look raised. */
186
186
  :scope:disabled {
@@ -205,9 +205,11 @@
205
205
  }
206
206
 
207
207
  /* Ghost has no hover to fall back on, so disabled is the one state it has to state outright:
208
- it takes a surface, light and tinted, rather than reading as plain text. */
208
+ it takes a surface rather than reading as plain text. The surface is a wash of the faded tone,
209
+ the way hover washes the live one, so it stays light in every tone. An opaque mix toward the
210
+ hue came out a mid-grey slab for neutral, whose hue is the ink: louder than the enabled button. */
209
211
  :scope[data-ui-button-state~="variant=ghost"]:disabled {
210
- background-color: color-mix(in oklab, var(--_hue) 28%, var(--ui-surface));
212
+ background-color: oklch(from var(--_tone) l c h / 14%);
211
213
  border-color: transparent;
212
214
  }
213
215
 
@@ -280,14 +282,16 @@
280
282
  /* A soft halo that transitions in with the other box-shadow changes, layered on the raised shadow. */
281
283
 
282
284
  /* Disabled drops the tone rather than the shape: every variant keeps its own structure, so a
283
- disabled outline is still an outline and a disabled ghost is still text. Intent goes — an
285
+ disabled outline is still an outline and a disabled ghost still has no edge. Intent goes — an
284
286
  unavailable action says "unavailable", not "unavailable, and destructive" — and so do the
285
287
  highlight and shadow, because nothing that cannot be pressed should look raised. */
286
288
 
287
289
  /* A filled button keeps its fill, faded further so its text stays legible on it. */
288
290
 
289
291
  /* Ghost has no hover to fall back on, so disabled is the one state it has to state outright:
290
- it takes a surface, light and tinted, rather than reading as plain text. */
292
+ it takes a surface rather than reading as plain text. The surface is a wash of the faded tone,
293
+ the way hover washes the live one, so it stays light in every tone. An opaque mix toward the
294
+ hue came out a mid-grey slab for neutral, whose hue is the ink: louder than the enabled button. */
291
295
 
292
296
  @media (pointer: coarse) {
293
297
  }
package/vue/UiButton.vue CHANGED
@@ -230,7 +230,7 @@ const hostState = computed(() =>
230
230
  }
231
231
 
232
232
  /* Disabled drops the tone rather than the shape: every variant keeps its own structure, so a
233
- disabled outline is still an outline and a disabled ghost is still text. Intent goes — an
233
+ disabled outline is still an outline and a disabled ghost still has no edge. Intent goes — an
234
234
  unavailable action says "unavailable", not "unavailable, and destructive" — and so do the
235
235
  highlight and shadow, because nothing that cannot be pressed should look raised. */
236
236
  [data-component~="ui-button"]:disabled {
@@ -255,9 +255,11 @@ const hostState = computed(() =>
255
255
  }
256
256
 
257
257
  /* Ghost has no hover to fall back on, so disabled is the one state it has to state outright:
258
- it takes a surface, light and tinted, rather than reading as plain text. */
258
+ it takes a surface rather than reading as plain text. The surface is a wash of the faded tone,
259
+ the way hover washes the live one, so it stays light in every tone. An opaque mix toward the
260
+ hue came out a mid-grey slab for neutral, whose hue is the ink: louder than the enabled button. */
259
261
  [data-component~="ui-button"][data-ui-button-state~="variant=ghost"]:disabled {
260
- background-color: color-mix(in oklab, var(--_hue) 28%, var(--ui-surface));
262
+ background-color: oklch(from var(--_tone) l c h / 14%);
261
263
  border-color: transparent;
262
264
  }
263
265
 
@@ -41,6 +41,6 @@ var UiButton_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
41
41
  }), [renderSlot(_ctx.$slots, "default", {}, void 0, true)], 16, _hoisted_1);
42
42
  };
43
43
  }
44
- }), [["__scopeId", "data-v-8513eb5d"]]);
44
+ }), [["__scopeId", "data-v-c6ef4679"]]);
45
45
  //#endregion
46
46
  export { UiButton_default as t };
@@ -204,7 +204,7 @@
204
204
  display: none;
205
205
  }
206
206
 
207
- [data-component~="ui-button"][data-v-8513eb5d] {
207
+ [data-component~="ui-button"][data-v-c6ef4679] {
208
208
  --_hue: var(--ui-accent);
209
209
  --_hue-hover: var(--ui-accent-hover);
210
210
  --_hue-active: var(--ui-accent-active);
@@ -223,15 +223,15 @@
223
223
  vertical-align: middle;
224
224
  color: var(--_tone-text);
225
225
  }
226
- [data-component~="ui-button"][data-ui-button-state~="align=start"][data-v-8513eb5d] {
226
+ [data-component~="ui-button"][data-ui-button-state~="align=start"][data-v-c6ef4679] {
227
227
  justify-content: flex-start;
228
228
  text-align: start;
229
229
  }
230
- [data-component~="ui-button"][data-ui-button-state~="stretch"][data-v-8513eb5d] {
230
+ [data-component~="ui-button"][data-ui-button-state~="stretch"][data-v-c6ef4679] {
231
231
  display: flex;
232
232
  inline-size: 100%;
233
233
  }
234
- [data-component~="ui-button"][data-v-8513eb5d] {
234
+ [data-component~="ui-button"][data-v-c6ef4679] {
235
235
  appearance: none;
236
236
  min-block-size: var(--ui-button-min-block-size, var(--ui-control-size-md));
237
237
  border-style: solid;
@@ -255,44 +255,44 @@
255
255
  margin: 0;
256
256
  }
257
257
  @supports (text-box-trim: trim-both) {
258
- [data-component~="ui-button"][data-v-8513eb5d] {
258
+ [data-component~="ui-button"][data-v-c6ef4679] {
259
259
  text-box-trim: trim-both;
260
260
  text-box-edge: cap alphabetic;
261
261
  }
262
262
  }
263
- [data-component~="ui-button"][data-ui-button-state~="size=sm"][data-v-8513eb5d] {
263
+ [data-component~="ui-button"][data-ui-button-state~="size=sm"][data-v-c6ef4679] {
264
264
  min-block-size: var(--ui-control-size-sm);
265
265
  padding: var(--ui-space-1) var(--ui-space-2);
266
266
  font-size: 0.875rem;
267
267
  }
268
- [data-component~="ui-button"][data-ui-button-state~="size=lg"][data-v-8513eb5d] {
268
+ [data-component~="ui-button"][data-ui-button-state~="size=lg"][data-v-c6ef4679] {
269
269
  min-block-size: var(--ui-control-size-lg);
270
270
  padding: var(--ui-space-3) var(--ui-space-4);
271
271
  font-size: 1rem;
272
272
  }
273
- [data-component~="ui-button"][data-v-8513eb5d]:hover:enabled {
273
+ [data-component~="ui-button"][data-v-c6ef4679]:hover:enabled {
274
274
  border-color: var(--ui-button-hover-border, var(--_tone-hover));
275
275
  background-color: var(--ui-button-hover-surface, oklch(from var(--_tone) l c h / 12%));
276
276
  }
277
277
 
278
278
  /* Pressed reads as pressed in: the highlight goes, and the shadow moves inside. */
279
- [data-component~="ui-button"][data-v-8513eb5d]:active:enabled {
279
+ [data-component~="ui-button"][data-v-c6ef4679]:active:enabled {
280
280
  background-color: var(--ui-button-active-surface, oklch(from var(--_tone) l c h / 18%));
281
281
  background-image: none;
282
282
  box-shadow: var(--ui-pressed);
283
283
  }
284
- [data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-8513eb5d],
285
- [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-8513eb5d] {
284
+ [data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-c6ef4679],
285
+ [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-c6ef4679] {
286
286
  border-color: var(--_tone);
287
287
  background-color: var(--_tone);
288
288
  color: var(--_on-tone);
289
289
  box-shadow: var(--ui-button-solid-shadow, var(--ui-raised));
290
290
  }
291
- :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-8513eb5d], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-8513eb5d]):hover:enabled {
291
+ :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-c6ef4679], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-c6ef4679]):hover:enabled {
292
292
  background-color: var(--_tone-hover);
293
293
  border-color: var(--_tone-hover);
294
294
  }
295
- :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-8513eb5d], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-8513eb5d]):active:enabled {
295
+ :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-c6ef4679], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-c6ef4679]):active:enabled {
296
296
  background-color: var(--_tone-active);
297
297
  border-color: var(--_tone-active);
298
298
  background-image: none;
@@ -301,43 +301,43 @@
301
301
 
302
302
  /* Tone is the colour, variant is the volume. Every pairing works: a danger outline is a
303
303
  destructive secondary action, not a special case. */
304
- [data-component~="ui-button"][data-ui-button-state~="tone=neutral"][data-v-8513eb5d] {
304
+ [data-component~="ui-button"][data-ui-button-state~="tone=neutral"][data-v-c6ef4679] {
305
305
  --_hue: var(--ui-text);
306
306
  --_hue-hover: var(--ui-text);
307
307
  --_hue-active: var(--ui-text);
308
308
  --_hue-text: var(--ui-text);
309
309
  --_on-hue: var(--ui-surface);
310
310
  }
311
- [data-component~="ui-button"][data-ui-button-state~="tone=danger"][data-v-8513eb5d],
312
- [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-8513eb5d] {
311
+ [data-component~="ui-button"][data-ui-button-state~="tone=danger"][data-v-c6ef4679],
312
+ [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-c6ef4679] {
313
313
  --_hue: var(--ui-danger);
314
314
  --_hue-hover: var(--ui-danger-hover);
315
315
  --_hue-active: var(--ui-danger-hover);
316
316
  --_hue-text: var(--ui-danger-hover);
317
317
  --_on-hue: var(--ui-on-danger);
318
318
  }
319
- [data-component~="ui-button"][data-ui-button-state~="tone=success"][data-v-8513eb5d] {
319
+ [data-component~="ui-button"][data-ui-button-state~="tone=success"][data-v-c6ef4679] {
320
320
  --_hue: var(--ui-success);
321
321
  --_hue-hover: color-mix(in srgb, var(--ui-success) 85%, #000);
322
322
  --_hue-active: color-mix(in srgb, var(--ui-success) 75%, #000);
323
323
  --_hue-text: color-mix(in srgb, var(--ui-success) 85%, #000);
324
324
  --_on-hue: var(--ui-surface);
325
325
  }
326
- [data-component~="ui-button"][data-ui-button-state~="tone=warning"][data-v-8513eb5d] {
326
+ [data-component~="ui-button"][data-ui-button-state~="tone=warning"][data-v-c6ef4679] {
327
327
  --_hue: var(--ui-warning);
328
328
  --_hue-hover: color-mix(in srgb, var(--ui-warning) 85%, #000);
329
329
  --_hue-active: color-mix(in srgb, var(--ui-warning) 75%, #000);
330
330
  --_hue-text: color-mix(in srgb, var(--ui-warning) 85%, #000);
331
331
  --_on-hue: var(--ui-surface);
332
332
  }
333
- [data-component~="ui-button"][data-ui-button-state~="tone=info"][data-v-8513eb5d] {
333
+ [data-component~="ui-button"][data-ui-button-state~="tone=info"][data-v-c6ef4679] {
334
334
  --_hue: var(--ui-info);
335
335
  --_hue-hover: color-mix(in srgb, var(--ui-info) 85%, #000);
336
336
  --_hue-active: color-mix(in srgb, var(--ui-info) 75%, #000);
337
337
  --_hue-text: color-mix(in srgb, var(--ui-info) 85%, #000);
338
338
  --_on-hue: var(--ui-surface);
339
339
  }
340
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-8513eb5d] {
340
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-c6ef4679] {
341
341
  border-color: transparent;
342
342
  background: transparent;
343
343
  box-shadow: none;
@@ -345,34 +345,34 @@
345
345
  }
346
346
 
347
347
  /* Tints derive from the text colour, not a surface token, so hover and press show on any background. */
348
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-8513eb5d]:hover:enabled {
348
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-c6ef4679]:hover:enabled {
349
349
  background-color: var(--ui-button-ghost-hover-surface, oklch(from var(--_tone) l c h / 10%));
350
350
  border-color: transparent;
351
351
  }
352
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-8513eb5d]:active:enabled {
352
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-c6ef4679]:active:enabled {
353
353
  background-color: var(--ui-button-ghost-active-surface, oklch(from var(--_tone) l c h / 16%));
354
354
  border-color: transparent;
355
355
  box-shadow: var(--ui-pressed);
356
356
  }
357
357
 
358
358
  /* A soft halo that transitions in with the other box-shadow changes, layered on the raised shadow. */
359
- [data-component~="ui-button"][data-v-8513eb5d]:focus-visible {
359
+ [data-component~="ui-button"][data-v-c6ef4679]:focus-visible {
360
360
  outline: none;
361
361
  box-shadow:
362
362
  var(--ui-button-shadow, var(--ui-raised)),
363
363
  var(--ui-focus-halo);
364
364
  }
365
- [data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-8513eb5d]:focus-visible {
365
+ [data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-c6ef4679]:focus-visible {
366
366
  box-shadow:
367
367
  var(--ui-button-solid-shadow, var(--ui-raised)),
368
368
  var(--ui-focus-halo);
369
369
  }
370
370
 
371
371
  /* Disabled drops the tone rather than the shape: every variant keeps its own structure, so a
372
- disabled outline is still an outline and a disabled ghost is still text. Intent goes — an
372
+ disabled outline is still an outline and a disabled ghost still has no edge. Intent goes — an
373
373
  unavailable action says "unavailable", not "unavailable, and destructive" — and so do the
374
374
  highlight and shadow, because nothing that cannot be pressed should look raised. */
375
- [data-component~="ui-button"][data-v-8513eb5d]:disabled {
375
+ [data-component~="ui-button"][data-v-c6ef4679]:disabled {
376
376
  /* Derived from the tone itself, not from a separate disabled palette: faded toward the
377
377
  page, which desaturates it at the same time. Toward the page rather than toward white,
378
378
  so a dark theme dims where a light one lightens — the same rule, both schemes. */
@@ -389,25 +389,27 @@
389
389
  }
390
390
 
391
391
  /* A filled button keeps its fill, faded further so its text stays legible on it. */
392
- :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-8513eb5d], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-8513eb5d]):disabled {
392
+ :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-c6ef4679], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-c6ef4679]):disabled {
393
393
  --_tone: color-mix(in oklab, var(--_hue) 34%, var(--ui-surface));
394
394
  }
395
395
 
396
396
  /* Ghost has no hover to fall back on, so disabled is the one state it has to state outright:
397
- it takes a surface, light and tinted, rather than reading as plain text. */
398
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-8513eb5d]:disabled {
399
- background-color: color-mix(in oklab, var(--_hue) 28%, var(--ui-surface));
397
+ it takes a surface rather than reading as plain text. The surface is a wash of the faded tone,
398
+ the way hover washes the live one, so it stays light in every tone. An opaque mix toward the
399
+ hue came out a mid-grey slab for neutral, whose hue is the ink: louder than the enabled button. */
400
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-c6ef4679]:disabled {
401
+ background-color: oklch(from var(--_tone) l c h / 14%);
400
402
  border-color: transparent;
401
403
  }
402
404
  @media (pointer: coarse) {
403
- [data-component~="ui-button"][data-v-8513eb5d] {
405
+ [data-component~="ui-button"][data-v-c6ef4679] {
404
406
  min-block-size: var(--ui-control-min-block-size);
405
407
  }
406
408
  }
407
409
 
408
410
  /* Link: an inline text action. No box, padding, or minimum height (inline targets are exempt under
409
411
  WCAG 2.5.8); the inherited font; an underline that strengthens on hover; a visible focus ring. */
410
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-8513eb5d] {
412
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-c6ef4679] {
411
413
  display: inline;
412
414
  min-block-size: 0;
413
415
  padding: 0;
@@ -425,22 +427,22 @@
425
427
  color var(--ui-motion-fast) var(--ui-motion-ease),
426
428
  text-decoration-color var(--ui-motion-fast) var(--ui-motion-ease);
427
429
  }
428
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-8513eb5d]:hover:enabled {
430
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-c6ef4679]:hover:enabled {
429
431
  color: var(--ui-button-link-hover-text, var(--ui-text-primary));
430
432
  text-decoration-color: currentColor;
431
433
  background: none;
432
434
  transform: none;
433
435
  }
434
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-8513eb5d]:active:enabled {
436
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-c6ef4679]:active:enabled {
435
437
  background: none;
436
438
  transform: none;
437
439
  }
438
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-8513eb5d]:disabled {
440
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-c6ef4679]:disabled {
439
441
  background: none;
440
442
  border-color: transparent;
441
443
  color: var(--ui-button-disabled-text, var(--ui-disabled-text));
442
444
  }
443
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-8513eb5d]:focus-visible {
445
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-c6ef4679]:focus-visible {
444
446
  box-shadow: none;
445
447
  /* Fallback keeps the ring if the token is missing: an unresolved var() would void the outline. */
446
448
  outline: 2px solid var(--ui-focus-ring);
@@ -448,7 +450,7 @@
448
450
  }
449
451
 
450
452
  /* A component that sets its display still honours the hidden attribute on its root. */
451
- [data-component~="ui-button"][hidden][data-v-8513eb5d] {
453
+ [data-component~="ui-button"][hidden][data-v-c6ef4679] {
452
454
  display: none;
453
455
  }
454
456
 
@@ -4372,6 +4374,28 @@ html[data-ui-input-modality="touch"] [data-component~="ui-tree-item"] .drag-hand
4372
4374
  }
4373
4375
  }
4374
4376
 
4377
+ /*
4378
+ * Marker for the block being edited, drawn in the gutter beside the text rather
4379
+ * than around it: a border or fill on the reading column reads as a form field.
4380
+ * The bar sits outside the content box, so a consumer that zeroes
4381
+ * --ui-editor-content-padding-inline must leave room on its own wrapper.
4382
+ */
4383
+ .looma-editor .ProseMirror > .looma-active-block {
4384
+ position: relative;
4385
+ }
4386
+
4387
+ .looma-editor .ProseMirror > .looma-active-block::before {
4388
+ position: absolute;
4389
+ z-index: 1;
4390
+ inset-block: 0;
4391
+ inset-inline-start: calc(-1 * var(--ui-editor-active-block-offset, var(--ui-space-3)));
4392
+ inline-size: var(--ui-editor-active-block-width, 2px);
4393
+ border-radius: var(--ui-radius-round);
4394
+ background: var(--ui-editor-active-block-color, var(--ui-border-strong));
4395
+ content: "";
4396
+ pointer-events: none;
4397
+ }
4398
+
4375
4399
  .looma-editor .ProseMirror p {
4376
4400
  margin: 0 0 var(--ui-editor-paragraph-gap, var(--ui-space-3));
4377
4401
  }