diff --git a/apps/extension/package.json b/apps/extension/package.json
index a23dd32..86f3174 100644
--- a/apps/extension/package.json
+++ b/apps/extension/package.json
@@ -12,7 +12,7 @@
"typecheck": "tsc -p tsconfig.json"
},
"dependencies": {
- "@mdbase-dev/ui": "0.1.0-beta.123",
+ "@mdbase-dev/ui": "0.1.0-beta.124",
"@mdbase-reader/connect": "workspace:*",
"@mdbase-reader/core": "workspace:*",
"@mdbase-reader/platform": "workspace:*",
diff --git a/apps/reader/package.json b/apps/reader/package.json
index 2e257b8..5431452 100644
--- a/apps/reader/package.json
+++ b/apps/reader/package.json
@@ -21,7 +21,7 @@
},
"dependencies": {
"@citation-js/plugin-csl": "0.8.2",
- "@mdbase-dev/ui": "0.1.0-beta.123",
+ "@mdbase-dev/ui": "0.1.0-beta.124",
"@mdbase-reader/connect": "workspace:*",
"@mdbase-reader/core": "workspace:*",
"@mdbase-reader/markdown-editor": "workspace:*",
diff --git a/apps/reader/src/ReaderHeader.tsx b/apps/reader/src/ReaderHeader.tsx
index 351cbc8..d478602 100644
--- a/apps/reader/src/ReaderHeader.tsx
+++ b/apps/reader/src/ReaderHeader.tsx
@@ -1,5 +1,6 @@
import { AppSwitcher } from "@mdbase-dev/ui/app-switcher";
import { shortcutLabel } from "@mdbase-dev/ui/command-palette";
+import { useMdbaseMarkBusy } from "@mdbase-dev/ui/mark-activity";
import { CollectionPicker } from "./CollectionPicker.js";
import { DisplayMenu } from "./DisplayMenu.js";
@@ -204,6 +205,7 @@ function ConnectionState({
readonly directAccess: ReaderDirectAccessState;
}): JSX.Element {
const { snapshot, working, problem, request } = directAccess;
+ useMdbaseMarkBusy(state === "syncing" && "stream");
if (snapshot?.authority !== "connector" || state !== "connected") {
return ;
}
diff --git a/apps/reader/src/import-controller.ts b/apps/reader/src/import-controller.ts
index 3fd1259..e732ca4 100644
--- a/apps/reader/src/import-controller.ts
+++ b/apps/reader/src/import-controller.ts
@@ -10,6 +10,8 @@ import {
type MigrationResult,
} from "@mdbase-reader/migration";
+import { markFraction, withMarkProgress } from "./mark-activity.js";
+
import type { ReaderApplicationSession } from "@mdbase-reader/connect";
export interface ImportState {
plan: MigrationPlan | null;
@@ -150,10 +152,17 @@ export class ImportController {
this.update({ boundTarget: target.collectionId });
void this.task(async (signal) => {
this.update({ result: null });
- const result = await runMigration(plan, target, signal, (progress) => {
- this.update({ progress, message: `${progress.phase} — ${name}` });
- saveProgress(plan.service, progress);
- });
+ // The mark fills with the import and plays saved or error when it ends; a stop is quiet.
+ const result = await withMarkProgress(
+ (mark) =>
+ runMigration(plan, target, signal, (progress) => {
+ mark(markFraction(progress.completed, progress.total));
+ this.update({ progress, message: `${progress.phase} — ${name}` });
+ saveProgress(plan.service, progress);
+ }),
+ undefined,
+ signal,
+ );
this.client?.forget();
this.update({
result,
diff --git a/apps/reader/src/mark-activity.test.tsx b/apps/reader/src/mark-activity.test.tsx
new file mode 100644
index 0000000..f9da385
--- /dev/null
+++ b/apps/reader/src/mark-activity.test.tsx
@@ -0,0 +1,132 @@
+/* eslint-disable @typescript-eslint/require-await -- the work under test is async by contract */
+import { getMdbaseMarkActivity, resetMdbaseMarkActivity } from "@mdbase-dev/ui/mark-activity";
+import { sourceId, type SourceSummary } from "@mdbase-reader/core";
+import { afterEach, beforeEach, describe, expect, it } from "vitest";
+
+import { markFraction, withMarkProgress } from "./mark-activity.js";
+import { eachSource } from "./use-library-writes.js";
+
+import type { BulkStatusProgress } from "./LibraryBulkBar.js";
+
+const mark = (): ReturnType => getMdbaseMarkActivity();
+
+beforeEach(() => resetMdbaseMarkActivity());
+afterEach(() => resetMdbaseMarkActivity());
+
+describe("withMarkProgress", () => {
+ it("fills the mark while the work reports progress, then plays saved", async () => {
+ let seen: number | null = null;
+ const result = await withMarkProgress(async (update) => {
+ update(0.4);
+ seen = mark().progress;
+ return "done";
+ });
+ expect(result).toBe("done");
+ expect(seen).toBe(0.4);
+ expect(mark().progress).toBeNull();
+ expect(mark().signal?.kind).toBe("saved");
+ });
+
+ it("ends the way the result says", async () => {
+ await withMarkProgress(
+ async () => null,
+ () => "fail",
+ );
+ expect(mark()).toMatchObject({ progress: null, signal: { kind: "error" } });
+ resetMdbaseMarkActivity();
+ await withMarkProgress(
+ async () => null,
+ () => "cancel",
+ );
+ expect(mark()).toMatchObject({ progress: null, signal: null });
+ });
+
+ it("plays error when the work throws, and rethrows", async () => {
+ await expect(
+ withMarkProgress(async () => {
+ throw new Error("offline");
+ }),
+ ).rejects.toThrow("offline");
+ expect(mark()).toMatchObject({ progress: null, signal: { kind: "error" } });
+ });
+
+ it("stops quietly when the work was aborted", async () => {
+ const controller = new AbortController();
+ await expect(
+ withMarkProgress(
+ async () => {
+ controller.abort();
+ throw new DOMException("Stopped", "AbortError");
+ },
+ undefined,
+ controller.signal,
+ ),
+ ).rejects.toThrow("Stopped");
+ expect(mark()).toMatchObject({ progress: null, signal: null });
+ });
+
+ it("never leaves the mark filling when deciding the outcome throws", async () => {
+ await expect(
+ withMarkProgress(
+ async () => 1,
+ () => {
+ throw new Error("bad outcome");
+ },
+ ),
+ ).rejects.toThrow("bad outcome");
+ expect(mark().progress).toBeNull();
+ });
+});
+
+describe("markFraction", () => {
+ it("is the share done, and nothing of nothing", () => {
+ expect(markFraction(3, 4)).toBe(0.75);
+ expect(markFraction(0, 0)).toBe(0);
+ });
+});
+
+describe("a bulk library update on the mark", () => {
+ const sources = ["a", "b", "c", "d"].map(
+ (id) => ({ id: sourceId(id) }) as unknown as SourceSummary,
+ );
+
+ it("fills with each source written and plays saved when all succeed", async () => {
+ const fills: (number | null)[] = [];
+ const reports: BulkStatusProgress[] = [];
+ await eachSource(
+ sources,
+ (value) => {
+ reports.push(value);
+ fills.push(mark().progress);
+ },
+ async () => undefined,
+ );
+ expect(reports.at(-1)).toEqual({ done: 4, total: 4, failed: 0 });
+ expect(fills).toEqual([0.25, 0.5, 0.75, 1]);
+ expect(mark()).toMatchObject({ progress: null, signal: { kind: "saved" } });
+ });
+
+ it("plays error when any source could not be updated", async () => {
+ const reports: BulkStatusProgress[] = [];
+ await eachSource(
+ sources,
+ (value) => reports.push(value),
+ async (source) => {
+ if (source.id === sourceId("c")) {
+ throw new Error("conflict");
+ }
+ },
+ );
+ expect(reports.at(-1)).toEqual({ done: 4, total: 4, failed: 1 });
+ expect(mark()).toMatchObject({ progress: null, signal: { kind: "error" } });
+ });
+
+ it("leaves the mark alone for an empty selection", async () => {
+ await eachSource(
+ [],
+ () => undefined,
+ async () => undefined,
+ );
+ expect(mark()).toMatchObject({ progress: null, signal: null });
+ });
+});
diff --git a/apps/reader/src/mark-activity.ts b/apps/reader/src/mark-activity.ts
new file mode 100644
index 0000000..5777cff
--- /dev/null
+++ b/apps/reader/src/mark-activity.ts
@@ -0,0 +1,37 @@
+import { trackMdbaseMarkProgress } from "@mdbase-dev/ui/mark-activity";
+
+/** How a tracked operation ends on the mark: `finish` plays saved, `fail` plays error. */
+export type MarkProgressOutcome = "finish" | "fail" | "cancel";
+
+/**
+ * Shows `work`'s known progress on the app mark and always ends it. A result is ended by
+ * `outcome` (finished by default); a throw fails it, or cancels it quietly once `signal` is
+ * aborted, so a stopped operation never shakes the mark.
+ */
+export async function withMarkProgress(
+ work: (update: (fraction: number) => void) => Promise,
+ outcome: (result: T) => MarkProgressOutcome = () => "finish",
+ signal?: AbortSignal,
+): Promise {
+ const progress = trackMdbaseMarkProgress();
+ try {
+ const result = await work(progress.update);
+ progress[outcome(result)]();
+ return result;
+ } catch (reason) {
+ if (signal?.aborted) {
+ progress.cancel();
+ } else {
+ progress.fail();
+ }
+ throw reason;
+ } finally {
+ // Already ended above; this only guards against an `outcome` that throws.
+ progress.cancel();
+ }
+}
+
+/** `done` of `total` as a 0 to 1 fraction; nothing is done of nothing. */
+export function markFraction(done: number, total: number): number {
+ return total > 0 ? done / total : 0;
+}
diff --git a/apps/reader/src/use-annotation-edit.ts b/apps/reader/src/use-annotation-edit.ts
index 3682c25..1d4c3f5 100644
--- a/apps/reader/src/use-annotation-edit.ts
+++ b/apps/reader/src/use-annotation-edit.ts
@@ -1,3 +1,4 @@
+import { signalMdbaseMark } from "@mdbase-dev/ui/mark-activity";
import { useEffect, useMemo, useState, useSyncExternalStore } from "react";
import { confirmAnnotationDiscard } from "./annotation-draft-actions.js";
@@ -79,8 +80,16 @@ export function useAnnotationEdit(props: AnnotationEditProps): AnnotationEditCon
if (locked || !session.ownsEditor(editorOwner) || !ready) {
return;
}
+ // Saving an already-saved annotation writes nothing, so the mark stays still.
+ const wrote = session.getSnapshot().status !== "saved";
void session.save().then(() => {
- if (session.getSnapshot().status === "saved" && deletion.isMounted()) {
+ const after = session.getSnapshot();
+ if (wrote && after.status === "saved") {
+ signalMdbaseMark("saved");
+ } else if (wrote && (after.status === "error" || after.conflict)) {
+ signalMdbaseMark("error");
+ }
+ if (after.status === "saved" && deletion.isMounted()) {
onCancel();
}
});
diff --git a/apps/reader/src/use-bibliography-export.ts b/apps/reader/src/use-bibliography-export.ts
index 41426bb..0a3082d 100644
--- a/apps/reader/src/use-bibliography-export.ts
+++ b/apps/reader/src/use-bibliography-export.ts
@@ -1,3 +1,4 @@
+import { signalMdbaseMark } from "@mdbase-dev/ui/mark-activity";
import { buildCslBibliography, serializeCslBibliography } from "@mdbase-reader/core";
import { useCallback, useMemo, useState } from "react";
@@ -27,33 +28,35 @@ export function useBibliographyExport(
readonly status: BibliographyExportStatus;
readonly message: string | null;
}>({ status: "idle", message: null });
+ const fail = useCallback((message: string): void => {
+ signalMdbaseMark("error");
+ setState({ status: "error", message });
+ }, []);
const save = useCallback(
(items: ReturnType["items"]): void => {
if (!saveFile) {
- setState({ status: "error", message: "File export is unavailable in this build." });
+ fail("File export is unavailable in this build.");
return;
}
if (items.length === 0) {
- setState({ status: "error", message: "No valid citations are ready to export." });
+ fail("No valid citations are ready to export.");
return;
}
setState({ status: "exporting", message: null });
const contents = serializeCslBibliography(items);
void saveFile("references.json", new Blob([contents], { type: "application/json" }))
- .then(() =>
+ .then(() => {
+ signalMdbaseMark("saved");
setState({
status: "success",
message: `Exported ${countLabel(items.length, "citation")}.`,
- }),
- )
+ });
+ })
.catch((reason: unknown) =>
- setState({
- status: "error",
- message: readerErrorMessage(reason, "Reader could not export the bibliography."),
- }),
+ fail(readerErrorMessage(reason, "Reader could not export the bibliography.")),
);
},
- [saveFile],
+ [fail, saveFile],
);
const run = useCallback((): void => save(bibliography.items), [bibliography, save]);
const runFor = useCallback(
diff --git a/apps/reader/src/use-citation-editor.ts b/apps/reader/src/use-citation-editor.ts
index 85784cb..7b37cd1 100644
--- a/apps/reader/src/use-citation-editor.ts
+++ b/apps/reader/src/use-citation-editor.ts
@@ -1,3 +1,4 @@
+import { signalMdbaseMark } from "@mdbase-dev/ui/mark-activity";
import {
citationCompletenessWarnings,
citekeyForCitation,
@@ -97,8 +98,12 @@ export function useCitationEditor(input: {
.then((updated) => {
input.onSaved(updated);
setStatusState({ sourceId: updated.id, value: "saved" });
+ // The editor's SaveNotice usually plays this too; the mark plays it once.
+ signalMdbaseMark("saved");
})
.catch((reason: unknown) => {
+ // The editor's SaveNotice never shows attention; the error is reported below the form.
+ signalMdbaseMark("error");
setStatusState({ sourceId: source.id, value: "idle" });
setErrorState({
sourceId: source.id,
diff --git a/apps/reader/src/use-creation-saving.ts b/apps/reader/src/use-creation-saving.ts
index ccbe61d..43e633a 100644
--- a/apps/reader/src/use-creation-saving.ts
+++ b/apps/reader/src/use-creation-saving.ts
@@ -1,3 +1,4 @@
+import { signalMdbaseMark } from "@mdbase-dev/ui/mark-activity";
import { useRef, useState, type RefObject } from "react";
import { saveAnnotationDraftToCollection } from "./annotation-draft-actions.js";
@@ -46,6 +47,12 @@ export function useCreationSaving(input: {
busy.current = false;
setSaving(false);
};
+ // The app mark confirms each annotation the person saves and shakes when one could not be.
+ const saved = (): void => signalMdbaseMark("saved");
+ const failed = (value: NonNullable): void => {
+ signalMdbaseMark("error");
+ setProblem(value);
+ };
return {
busy,
saving,
@@ -72,8 +79,8 @@ export function useCreationSaving(input: {
},
value,
"",
- () => undefined,
- setProblem,
+ saved,
+ failed,
() => undefined,
);
},
@@ -83,12 +90,15 @@ export function useCreationSaving(input: {
return;
}
void saveAnnotationDraftToCollection(
- () => buffer.clearIf(value),
+ () => {
+ buffer.clearIf(value);
+ saved();
+ },
value,
source,
surface,
create,
- setProblem,
+ failed,
finish,
);
},
diff --git a/apps/reader/src/use-library-writes.ts b/apps/reader/src/use-library-writes.ts
index e42ce7b..0723820 100644
--- a/apps/reader/src/use-library-writes.ts
+++ b/apps/reader/src/use-library-writes.ts
@@ -1,7 +1,9 @@
+import { signalMdbaseMark } from "@mdbase-dev/ui/mark-activity";
import { useCallback, useMemo } from "react";
import { propertyValue } from "./library-columns.js";
import { parseFieldInput } from "./library-field-edit.js";
+import { markFraction, withMarkProgress } from "./mark-activity.js";
import type { FieldShape } from "./library-conditions.js";
import type { BulkStatusProgress } from "./LibraryBulkBar.js";
@@ -61,9 +63,16 @@ export function useLibraryWrites(
);
const saveField = useCallback(
async (source: SourceSummary, key: string, text: string): Promise => {
- if (saveFields) {
+ if (!saveFields) {
+ return;
+ }
+ try {
onSourceChanged?.(await saveFields(source.id, { [key]: fieldValue(source, key, text) }));
+ } catch (reason) {
+ signalMdbaseMark("error");
+ throw reason;
}
+ signalMdbaseMark("saved");
},
[fieldValue, onSourceChanged, saveFields],
);
@@ -76,24 +85,34 @@ export function useLibraryWrites(
);
}
-async function eachSource(
+/** Exported for tests. Shows the run on the app mark, which shakes if any source failed. */
+export async function eachSource(
sources: readonly SourceSummary[],
progress: Progress,
write: (source: SourceSummary) => Promise,
): Promise {
+ if (sources.length === 0) {
+ return;
+ }
let done = 0;
let failed = 0;
const queue = [...sources];
- const worker = async (): Promise => {
- for (let source = queue.shift(); source; source = queue.shift()) {
- try {
- await write(source);
- } catch {
- failed += 1;
- }
- done += 1;
- progress({ done, total: sources.length, failed });
- }
- };
- await Promise.all(Array.from({ length: Math.min(4, sources.length) }, worker));
+ await withMarkProgress(
+ async (mark) => {
+ const worker = async (): Promise => {
+ for (let source = queue.shift(); source; source = queue.shift()) {
+ try {
+ await write(source);
+ } catch {
+ failed += 1;
+ }
+ done += 1;
+ mark(markFraction(done, sources.length));
+ progress({ done, total: sources.length, failed });
+ }
+ };
+ await Promise.all(Array.from({ length: Math.min(4, sources.length) }, worker));
+ },
+ () => (failed > 0 ? "fail" : "finish"),
+ );
}
diff --git a/apps/reader/src/use-source-fields.ts b/apps/reader/src/use-source-fields.ts
index aeb6f5f..64bdfcc 100644
--- a/apps/reader/src/use-source-fields.ts
+++ b/apps/reader/src/use-source-fields.ts
@@ -1,3 +1,4 @@
+import { signalMdbaseMark } from "@mdbase-dev/ui/mark-activity";
import { useCallback, useState } from "react";
import { readerErrorMessage } from "./errors.js";
@@ -37,7 +38,9 @@ export function useSourceFields(input: {
setErrorState({ sourceId, value: null });
try {
onSaved(await saveFields(sourceId, fields));
+ signalMdbaseMark("saved");
} catch (reason) {
+ signalMdbaseMark("error");
setErrorState({
sourceId,
value: readerErrorMessage(reason, "Reader could not save these details."),
diff --git a/apps/reader/src/use-source-import.ts b/apps/reader/src/use-source-import.ts
index fb65e55..5a6c3e2 100644
--- a/apps/reader/src/use-source-import.ts
+++ b/apps/reader/src/use-source-import.ts
@@ -1,5 +1,7 @@
import { useCallback, useEffect, useRef, useState } from "react";
+import { markFraction, withMarkProgress } from "./mark-activity.js";
+
import type { FileImportSuggestion } from "./file-import-suggestion.js";
import type { ReaderWorkspaceController } from "./use-reader-workspace.js";
import type { Source, SourceId, SourceImportProgress } from "@mdbase-reader/core";
@@ -73,11 +75,20 @@ export function useSourceImport(
importController.current = controller;
setProgress(null);
try {
- const imported = await workspace.importSourceFile(importRequest(file, title, found), {
- signal: controller.signal,
- onProgress: setProgress,
- ...(recoveryFile.current === file ? { recoverExistingFiles: true } : {}),
- });
+ // The workspace reports failures and returns null; a stopped import ends quietly.
+ const imported = await withMarkProgress(
+ (mark) =>
+ workspace.importSourceFile(importRequest(file, title, found), {
+ signal: controller.signal,
+ onProgress: (value) => {
+ mark(markFraction(value.completedBytes, value.totalBytes));
+ setProgress(value);
+ },
+ ...(recoveryFile.current === file ? { recoverExistingFiles: true } : {}),
+ }),
+ (result) => (result ? "finish" : controller.signal.aborted ? "cancel" : "fail"),
+ controller.signal,
+ );
if (imported) {
recoveryFile.current = null;
if (useCitation) {
diff --git a/apps/reader/src/use-web-capture.ts b/apps/reader/src/use-web-capture.ts
index 2ef4596..affa665 100644
--- a/apps/reader/src/use-web-capture.ts
+++ b/apps/reader/src/use-web-capture.ts
@@ -1,3 +1,4 @@
+import { signalMdbaseMark, useMdbaseMarkBusy } from "@mdbase-dev/ui/mark-activity";
import { useCallback, useRef, useState } from "react";
import { readerErrorMessage } from "./errors.js";
@@ -37,6 +38,8 @@ export function useWebCapture(
} | null>(null);
const [notice, setNotice] = useState(null);
const recoveryInput = useRef(null);
+ // Fetching, reading and looking up a page has no measurable progress.
+ useMdbaseMarkBusy(status === "capturing" && "scan");
const run = useCallback(
async (
@@ -69,8 +72,11 @@ export function useWebCapture(
if (outcome.kind === "citation-only") {
setOffer({ candidate: outcome.candidate, url: input, reason: outcome.reason });
} else if (outcome.kind === "not-added") {
+ // The workspace reports why; the input is kept to recover the upload on retry.
+ signalMdbaseMark("error");
recoveryInput.current = input;
} else {
+ signalMdbaseMark("saved");
recoveryInput.current = null;
if (outcome.notices.length) {
setNotice({ message: outcome.notices.join(" "), sourceId: outcome.source.id });
@@ -79,6 +85,7 @@ export function useWebCapture(
}
}
} catch (reason) {
+ signalMdbaseMark("error");
setError(readerErrorMessage(reason, "Reader could not add that source."));
} finally {
setStatus("idle");
diff --git a/packages/markdown-editor/package.json b/packages/markdown-editor/package.json
index cf8aa1c..95a67df 100644
--- a/packages/markdown-editor/package.json
+++ b/packages/markdown-editor/package.json
@@ -21,7 +21,7 @@
"@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.43.8",
"@lezer/highlight": "^1.2.3",
- "@mdbase-dev/ui": "0.1.0-beta.123",
+ "@mdbase-dev/ui": "0.1.0-beta.124",
"@mdbase-reader/core": "workspace:*",
"react": "^19.2.8"
},
diff --git a/packages/ui/package.json b/packages/ui/package.json
index a486888..4e8a581 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -14,7 +14,7 @@
"dependencies": {
"@fontsource/atkinson-hyperlegible": "^5.3.0",
"@fontsource/azeret-mono": "^5.3.0",
- "@mdbase-dev/ui": "0.1.0-beta.123",
+ "@mdbase-dev/ui": "0.1.0-beta.124",
"@phosphor-icons/react": "^2.1.10",
"react": "^19.2.8"
},
diff --git a/packages/ui/src/styles.css b/packages/ui/src/styles.css
index 029ea37..093e273 100644
--- a/packages/ui/src/styles.css
+++ b/packages/ui/src/styles.css
@@ -1,6 +1,7 @@
@import "@mdbase-dev/ui/fonts.css";
@import "@mdbase-dev/ui/tokens.css";
@import "@mdbase-dev/ui/brand.css";
+@import "@mdbase-dev/ui/motion.css";
@import "@mdbase-dev/ui/controls.css";
@import "@mdbase-dev/ui/screens.css";
@import "@mdbase-dev/ui/palette.css";
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9ff42e9..64e7cee 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -93,8 +93,8 @@ importers:
apps/extension:
dependencies:
'@mdbase-dev/ui':
- specifier: 0.1.0-beta.123
- version: 0.1.0-beta.123(@codemirror/view@6.43.8)(react@19.2.8)
+ specifier: 0.1.0-beta.124
+ version: 0.1.0-beta.124(@codemirror/view@6.43.8)(react@19.2.8)
'@mdbase-reader/connect':
specifier: workspace:*
version: link:../../packages/connect
@@ -139,8 +139,8 @@ importers:
specifier: 0.8.2
version: 0.8.2(@citation-js/core@0.8.2)
'@mdbase-dev/ui':
- specifier: 0.1.0-beta.123
- version: 0.1.0-beta.123(@codemirror/view@6.43.8)(react@19.2.8)
+ specifier: 0.1.0-beta.124
+ version: 0.1.0-beta.124(@codemirror/view@6.43.8)(react@19.2.8)
'@mdbase-reader/connect':
specifier: workspace:*
version: link:../../packages/connect
@@ -276,8 +276,8 @@ importers:
specifier: ^1.2.3
version: 1.2.3
'@mdbase-dev/ui':
- specifier: 0.1.0-beta.123
- version: 0.1.0-beta.123(@codemirror/view@6.43.8)(react@19.2.8)
+ specifier: 0.1.0-beta.124
+ version: 0.1.0-beta.124(@codemirror/view@6.43.8)(react@19.2.8)
'@mdbase-reader/core':
specifier: workspace:*
version: link:../core
@@ -416,8 +416,8 @@ importers:
specifier: ^5.3.0
version: 5.3.0
'@mdbase-dev/ui':
- specifier: 0.1.0-beta.123
- version: 0.1.0-beta.123(@codemirror/view@6.43.8)(react@19.2.8)
+ specifier: 0.1.0-beta.124
+ version: 0.1.0-beta.124(@codemirror/view@6.43.8)(react@19.2.8)
'@phosphor-icons/react':
specifier: ^2.1.10
version: 2.1.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
@@ -1151,8 +1151,8 @@ packages:
'@mdbase-dev/connect@0.1.0-beta.123':
resolution: {integrity: sha512-LquiTAhumNaE2E96RimFPaxMcClH28cgYPtGPAAWD5L+PQjcv6aR8m/h7s0k933W0sJjY0BMkRjtX+aFu4fo5g==}
- '@mdbase-dev/ui@0.1.0-beta.123':
- resolution: {integrity: sha512-KaHQfUelNoXWtGgvde6li1IWtImckBML9FQ4cJ3DFXlHD70hedYF8ly7mz3U4TRu98RNCPCVWmugXaP3pE/LAw==}
+ '@mdbase-dev/ui@0.1.0-beta.124':
+ resolution: {integrity: sha512-75xiQ9PhKjVyk9R+xaE0APpNn5MprfrkXwXhVQM5eT3uTCCZ0YwFCTeIAFsjEB6MNItp4pujoyebSIPQKwXg7w==}
peerDependencies:
'@codemirror/view': ^6.0.0
react: ^19.2.0
@@ -4327,7 +4327,7 @@ snapshots:
dependencies:
'@mdbase-dev/connect-protocol': 0.1.0-beta.123
- '@mdbase-dev/ui@0.1.0-beta.123(@codemirror/view@6.43.8)(react@19.2.8)':
+ '@mdbase-dev/ui@0.1.0-beta.124(@codemirror/view@6.43.8)(react@19.2.8)':
dependencies:
'@fontsource/atkinson-hyperlegible': 5.3.0
'@fontsource/azeret-mono': 5.3.0