@lapismd/mira 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @lapismd/mira
2
2
 
3
+ ## 0.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [`dd83f89`](https://github.com/lapismd/mira-mde/commit/dd83f89e474e9ae0e4fdd8c7cdfc9825e62b4a0d) Thanks [@stevejuma](https://github.com/stevejuma)! - Refresh unfocused frontmatter text controls after an external controller update, including after prior user editing, and preserve Live Preview disclosure across CodeMirror widget remounts.
8
+
9
+ - [`9e5d84f`](https://github.com/lapismd/mira-mde/commit/9e5d84f8148cd2d0a6008b31f04d943e1c0d9477) Thanks [@stevejuma](https://github.com/stevejuma)! - Keep ordinary parentheses visible in live preview, wrap editable table cells without overflow, and let source table rows scroll without scrollbar chrome.
10
+
11
+ - [`99fdddc`](https://github.com/lapismd/mira-mde/commit/99fdddcbe134eec42abfdfe4de951cc4a48f3aab) Thanks [@stevejuma](https://github.com/stevejuma)! - Keep Find and Replace input and placeholder typography compact across consumer base styles with a public size token.
12
+
13
+ - [`a9ea809`](https://github.com/lapismd/mira-mde/commit/a9ea8098a843452d7c4e122ab7ed190c48c84dcd) Thanks [@stevejuma](https://github.com/stevejuma)! - Present Find and Replace with pill-shaped fields, a search glyph, inline options, borderless actions, and no panel divider.
14
+
15
+ - [`990662d`](https://github.com/lapismd/mira-mde/commit/990662d2f7fc9fbdfa1cff6ee9c643aa2f9478b0) Thanks [@stevejuma](https://github.com/stevejuma)! - Preserve an internal-link trigger's inherited Mira theme and color mode when its hover preview portals to the owner document.
16
+
17
+ - [`bfc57f4`](https://github.com/lapismd/mira-mde/commit/bfc57f4fe8084e20644a34a7bb6c5b96719fb442) Thanks [@stevejuma](https://github.com/stevejuma)! - Avoid repeating an image embed's visible figure caption through its nested image alternative text.
18
+
19
+ - [`937ecd0`](https://github.com/lapismd/mira-mde/commit/937ecd04fe49b5c83b64df7d3dcd31257e2e57ff) Thanks [@stevejuma](https://github.com/stevejuma)! - Keep live-preview continuation indentation stable while CodeMirror rebuilds
20
+ line decorations after focus and selection changes.
21
+
22
+ - [`5165834`](https://github.com/lapismd/mira-mde/commit/5165834d3dd32a21f9d9703cd282511341c1ecf0) Thanks [@stevejuma](https://github.com/stevejuma)! - Keep contextual block-toolbar portals out of document layout and remove them
23
+ with their owning CodeMirror view.
24
+
25
+ - [`f83553a`](https://github.com/lapismd/mira-mde/commit/f83553a5fbf6f3fc9dfb6b1057ddac82b6a1b2c8) Thanks [@stevejuma](https://github.com/stevejuma)! - Align editable Markdown hover cards around a minimal padded presentation and persistence-safe outside-click dismissal.
26
+
27
+ - [`74963d2`](https://github.com/lapismd/mira-mde/commit/74963d237be51561a6d5282ec6f48e90736a1dd1) Thanks [@stevejuma](https://github.com/stevejuma)! - Preserve the active pipe-table column when Enter moves to the next row.
28
+
29
+ - [`7d2dd4b`](https://github.com/lapismd/mira-mde/commit/7d2dd4bdb2f9d78a760589f1e11450e4de5eb501) Thanks [@stevejuma](https://github.com/stevejuma)! - Keep the Find and Replace panel wrapper transparent so it does not paint a separate surface behind the editor controls.
30
+
31
+ - [`f573127`](https://github.com/lapismd/mira-mde/commit/f573127bb1d89c7da313eb897ba23ceef3b2236a) Thanks [@stevejuma](https://github.com/stevejuma)! - Allow consumers to compose Mira's complete portable authoring stack without its duplicate base CodeMirror setup.
32
+
3
33
  ## 0.0.1
4
34
 
5
35
  ### Patch Changes
@@ -11,6 +11,69 @@ describe("createRichEditorExtensions", () => {
11
11
  it("can be disabled", () => {
12
12
  expect(createRichEditorExtensions({ enabled: false })).toEqual([]);
13
13
  });
14
+ it("preserves a local frontmatter disclosure across preview widget remounts", async () => {
15
+ const parent = document.createElement("div");
16
+ document.body.append(parent);
17
+ const source = "---\ntitle: Portable\n---\n\n# Note";
18
+ const view = new EditorView({
19
+ doc: source,
20
+ selection: { anchor: source.length },
21
+ extensions: [
22
+ createMarkdownCodeMirrorExtensions(),
23
+ createRichEditorExtensions({
24
+ livePreview: true,
25
+ frontmatterOpen: false,
26
+ }),
27
+ ],
28
+ parent,
29
+ });
30
+ await nextFrame();
31
+ await nextFrame();
32
+ const expand = parent.querySelector('.mira-rich-widget--frontmatter button[aria-label="Expand properties"]');
33
+ expect(expand).not.toBeNull();
34
+ expand?.click();
35
+ await nextFrame();
36
+ expect(parent.querySelector(".md-frontmatter__content")).not.toBeNull();
37
+ view.dispatch({ selection: { anchor: 2 } });
38
+ await nextFrame();
39
+ expect(parent.querySelector(".mira-rich-widget--frontmatter")).toBeNull();
40
+ view.dispatch({ selection: { anchor: source.length } });
41
+ await nextFrame();
42
+ await nextFrame();
43
+ expect(parent.querySelector('.mira-rich-widget--frontmatter button[aria-label="Collapse properties"]')).not.toBeNull();
44
+ expect(parent.querySelector(".md-frontmatter__content")).not.toBeNull();
45
+ view.destroy();
46
+ parent.remove();
47
+ });
48
+ it("keeps an interactive frontmatter menu mounted while its trigger receives focus", async () => {
49
+ const parent = document.createElement("div");
50
+ document.body.append(parent);
51
+ const source = "---\ntitle: Portable\n---\n\n# Note";
52
+ const view = new EditorView({
53
+ doc: source,
54
+ selection: { anchor: source.length },
55
+ extensions: [
56
+ createMarkdownCodeMirrorExtensions(),
57
+ createRichEditorExtensions({
58
+ livePreview: true,
59
+ frontmatterOpen: true,
60
+ }),
61
+ ],
62
+ parent,
63
+ });
64
+ await nextFrame();
65
+ await nextFrame();
66
+ const trigger = parent.querySelector('button[aria-label="Property options for title"]');
67
+ expect(trigger).not.toBeNull();
68
+ trigger?.focus();
69
+ trigger?.click();
70
+ await nextFrame();
71
+ expect(trigger?.isConnected).toBe(true);
72
+ expect(trigger?.getAttribute("aria-expanded")).toBe("true");
73
+ expect(document.body.querySelector('[role="menu"][aria-label="Property options for title"]')).not.toBeNull();
74
+ view.destroy();
75
+ parent.remove();
76
+ });
14
77
  it("renders and reveals a seeded divider comment and rule as one widget", async () => {
15
78
  const parent = document.createElement("div");
16
79
  document.body.append(parent);
@@ -1 +1 @@
1
- {"version":3,"file":"preview-widgets.d.ts","sourceRoot":"","sources":["../../../../../src/internal/codemirror/rich/widgets/preview-widgets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAc1D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAUtD,qBAAa,kBAAmB,SAAQ,UAAU;IAE9C,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;QACtC,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,qBAAqB,CAAC;KAChC;IAKM,EAAE,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;IAgB/C,IAAa,eAAe,IAAI,MAAM,CAErC;IAEQ,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW;IA6H7C,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,2BAA2B;IAiBnC,OAAO,CAAC,0BAA0B;IAwDzB,OAAO,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI;IAQ/B,WAAW,IAAI,OAAO;CAGhC;AAED,qBAAa,oBAAqB,SAAQ,UAAU;IAEhD,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,qBAAqB,CAAC;KAChC;IAKM,EAAE,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO;IAQxC,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW;IA+CpC,OAAO,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI;IAQ/B,WAAW,IAAI,OAAO;CAGhC;AAED,qBAAa,gBAAiB,SAAQ,UAAU;IAE5C,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,qBAAqB,CAAC;KAChC;IAKM,EAAE,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO;IAQpC,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW;IAwCpC,OAAO,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI;IAQ/B,WAAW,IAAI,OAAO;CAGhC"}
1
+ {"version":3,"file":"preview-widgets.d.ts","sourceRoot":"","sources":["../../../../../src/internal/codemirror/rich/widgets/preview-widgets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAc1D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAetD,qBAAa,kBAAmB,SAAQ,UAAU;IAE9C,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;QACtC,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,qBAAqB,CAAC;KAChC;IAKM,EAAE,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;IAgB/C,IAAa,eAAe,IAAI,MAAM,CAErC;IAEQ,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW;IAoK7C,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,2BAA2B;IAiBnC,OAAO,CAAC,0BAA0B;IAwDzB,OAAO,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI;IAU/B,WAAW,IAAI,OAAO;CAGhC;AAED,qBAAa,oBAAqB,SAAQ,UAAU;IAEhD,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,qBAAqB,CAAC;KAChC;IAKM,EAAE,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO;IAQxC,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW;IA+CpC,OAAO,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI;IAQ/B,WAAW,IAAI,OAAO;CAGhC;AAED,qBAAa,gBAAiB,SAAQ,UAAU;IAE5C,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,qBAAqB,CAAC;KAChC;IAKM,EAAE,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO;IAQpC,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW;IAwCpC,OAAO,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI;IAQ/B,WAAW,IAAI,OAAO;CAGhC"}
@@ -8,6 +8,8 @@ import { shouldActivateEditablePreview, PREVIEW_INTERACTIVE_SELECTOR, } from "..
8
8
  import { estimateMarkdownBlockHeight } from "../utils/height-estimates";
9
9
  import { createSourceToggleButton, selectWidgetSource } from "./source-toggle";
10
10
  const previewWidgetMounts = new WeakMap();
11
+ const previewWidgetObservers = new WeakMap();
12
+ const frontmatterDisclosureByView = new WeakMap();
11
13
  export class BlockPreviewWidget extends WidgetType {
12
14
  config;
13
15
  constructor(config) {
@@ -65,6 +67,20 @@ export class BlockPreviewWidget extends WidgetType {
65
67
  selectWidgetSource(view, this.config.from, this.config.to);
66
68
  }));
67
69
  let pendingFrontmatterValue = "";
70
+ const configuredFrontmatterOpen = this.config.options.frontmatterOpen ?? true;
71
+ const retainedFrontmatterDisclosure = this.config.nodeName === "Frontmatter"
72
+ ? frontmatterDisclosureByView.get(view)
73
+ : undefined;
74
+ const frontmatterOpen = retainedFrontmatterDisclosure?.configuredOpen ===
75
+ configuredFrontmatterOpen
76
+ ? retainedFrontmatterDisclosure.open
77
+ : configuredFrontmatterOpen;
78
+ if (this.config.nodeName === "Frontmatter") {
79
+ frontmatterDisclosureByView.set(view, {
80
+ configuredOpen: configuredFrontmatterOpen,
81
+ open: frontmatterOpen,
82
+ });
83
+ }
68
84
  const component = mount(MarkdownPreview, {
69
85
  target: container,
70
86
  props: {
@@ -75,7 +91,7 @@ export class BlockPreviewWidget extends WidgetType {
75
91
  linkResolver: this.config.options.linkResolver,
76
92
  assetResolver: this.config.options.assetResolver,
77
93
  fileAdapter: this.config.options.fileAdapter,
78
- frontmatterOpen: this.config.options.frontmatterOpen ?? true,
94
+ frontmatterOpen,
79
95
  frontmatterConfig: this.config.options.frontmatterConfig,
80
96
  onChange: (replacement, from, to) => {
81
97
  const absoluteFrom = this.absoluteMarkdownOffset(from);
@@ -94,6 +110,28 @@ export class BlockPreviewWidget extends WidgetType {
94
110
  },
95
111
  });
96
112
  previewWidgetMounts.set(container, component);
113
+ if (this.config.nodeName === "Frontmatter") {
114
+ const retainDisclosure = () => {
115
+ const expanded = container
116
+ .querySelector(".md-frontmatter__trigger")
117
+ ?.getAttribute("aria-expanded");
118
+ if (expanded !== "true" && expanded !== "false")
119
+ return;
120
+ frontmatterDisclosureByView.set(view, {
121
+ configuredOpen: configuredFrontmatterOpen,
122
+ open: expanded === "true",
123
+ });
124
+ };
125
+ retainDisclosure();
126
+ const observer = new MutationObserver(retainDisclosure);
127
+ observer.observe(container, {
128
+ attributes: true,
129
+ attributeFilter: ["aria-expanded"],
130
+ childList: true,
131
+ subtree: true,
132
+ });
133
+ previewWidgetObservers.set(container, observer);
134
+ }
97
135
  if (this.config.nodeName === "HorizontalRule") {
98
136
  container.addEventListener(MIRA_DOODLE_DIVIDER_CONTROL_EVENT, (event) => {
99
137
  this.handleDoodleDividerControl(view, event);
@@ -182,6 +220,8 @@ export class BlockPreviewWidget extends WidgetType {
182
220
  view.scrollDOM.scrollLeft = scrollLeft;
183
221
  }
184
222
  destroy(dom) {
223
+ previewWidgetObservers.get(dom)?.disconnect();
224
+ previewWidgetObservers.delete(dom);
185
225
  const component = previewWidgetMounts.get(dom);
186
226
  if (component) {
187
227
  void unmount(component);
@@ -46,8 +46,12 @@
46
46
  });
47
47
 
48
48
  $effect(() => {
49
+ const nextValue = value;
49
50
  if (editorEl?.ownerDocument.activeElement !== editorEl) {
50
- query = value;
51
+ query = nextValue;
52
+ if (editorEl && editorEl.textContent !== nextValue) {
53
+ editorEl.textContent = nextValue;
54
+ }
51
55
  }
52
56
  });
53
57
 
@@ -1 +1 @@
1
- {"version":3,"file":"frontmatter-property-text-input.svelte.d.ts","sourceRoot":"","sources":["../../../src/preview/components/frontmatter-property-text-input.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAGH,KAAK,iBAAiB,EACvB,MAAM,qBAAqB,CAAC;AAI7B,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAoLJ,QAAA,MAAM,4BAA4B,2CAAwC,CAAC;AAC3E,KAAK,4BAA4B,GAAG,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC;AACpF,eAAe,4BAA4B,CAAC"}
1
+ {"version":3,"file":"frontmatter-property-text-input.svelte.d.ts","sourceRoot":"","sources":["../../../src/preview/components/frontmatter-property-text-input.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAGH,KAAK,iBAAiB,EACvB,MAAM,qBAAqB,CAAC;AAI7B,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAwLJ,QAAA,MAAM,4BAA4B,2CAAwC,CAAC;AAC3E,KAAK,4BAA4B,GAAG,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC;AACpF,eAAe,4BAA4B,CAAC"}
@@ -451,6 +451,37 @@ describe("portable preview surfaces", () => {
451
451
  expect(controller.propertyManager.properties(controller.getRecord())[0]?.kind).toBe("number");
452
452
  await unmount(component);
453
453
  });
454
+ it("refreshes an edited text property after focus leaves the control", async () => {
455
+ const target = document.createElement("div");
456
+ document.body.append(target);
457
+ const controller = new FrontmatterController({
458
+ record: { status: "ready" },
459
+ });
460
+ const component = mount(FrontmatterEditor, {
461
+ target,
462
+ props: {
463
+ controller,
464
+ },
465
+ });
466
+ await settle();
467
+ const status = target.querySelector('[role="combobox"][aria-label="status value"]');
468
+ expect(status?.textContent).toBe("ready");
469
+ status?.focus();
470
+ expect(document.activeElement).toBe(status);
471
+ if (status)
472
+ status.textContent = "local";
473
+ status?.dispatchEvent(new InputEvent("input", { bubbles: true }));
474
+ controller.syncRecord({ status: "planned" }, { commit: false });
475
+ await settle();
476
+ expect(status?.textContent).toBe("local");
477
+ status?.blur();
478
+ await settle();
479
+ controller.syncRecord({ status: "review" }, { commit: false });
480
+ await settle();
481
+ expect(status?.textContent).toBe("review");
482
+ await unmount(component);
483
+ target.remove();
484
+ });
454
485
  it("keeps read-only list callouts passive and makes editable markers selectable", async () => {
455
486
  const readOnlyTarget = document.createElement("div");
456
487
  const editableTarget = document.createElement("div");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lapismd/mira",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Composable Markdown editor, extension SDK, preview renderer, and UI for Mira.",
5
5
  "author": "Steve Juma <help@lapis.md>",
6
6
  "license": "AGPL-3.0-or-later",