@abraca/nuxt 2.0.11 → 2.4.0
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/module.d.mts +68 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +99 -4
- package/dist/runtime/components/ACodeEditor.d.vue.ts +26 -0
- package/dist/runtime/components/ACodeEditor.vue +268 -0
- package/dist/runtime/components/ACodeEditor.vue.d.ts +26 -0
- package/dist/runtime/components/ADocumentTree.vue +52 -20
- package/dist/runtime/components/AEditor.d.vue.ts +20 -13
- package/dist/runtime/components/AEditor.vue +55 -2
- package/dist/runtime/components/AEditor.vue.d.ts +20 -13
- package/dist/runtime/components/ANodePanel.vue +64 -60
- package/dist/runtime/components/ANotificationBell.d.vue.ts +1 -1
- package/dist/runtime/components/ANotificationBell.vue.d.ts +1 -1
- package/dist/runtime/components/ASpaceFormModal.d.vue.ts +2 -2
- package/dist/runtime/components/ASpaceFormModal.vue.d.ts +2 -2
- package/dist/runtime/components/aware/AMedia.d.vue.ts +1 -1
- package/dist/runtime/components/aware/AMedia.vue.d.ts +1 -1
- package/dist/runtime/components/aware/APresenceBlobs.d.vue.ts +29 -1
- package/dist/runtime/components/aware/APresenceBlobs.vue +54 -8
- package/dist/runtime/components/aware/APresenceBlobs.vue.d.ts +29 -1
- package/dist/runtime/components/aware/APresenceCursors.d.vue.ts +11 -0
- package/dist/runtime/components/aware/APresenceCursors.vue +74 -9
- package/dist/runtime/components/aware/APresenceCursors.vue.d.ts +11 -0
- package/dist/runtime/components/aware/AToggleGroup.d.vue.ts +28 -13
- package/dist/runtime/components/aware/AToggleGroup.vue +56 -20
- package/dist/runtime/components/aware/AToggleGroup.vue.d.ts +28 -13
- package/dist/runtime/components/docs/ADocsNavigation.d.vue.ts +1 -1
- package/dist/runtime/components/docs/ADocsNavigation.vue.d.ts +1 -1
- package/dist/runtime/components/docs/ADocsSearch.d.vue.ts +1 -1
- package/dist/runtime/components/docs/ADocsSearch.vue.d.ts +1 -1
- package/dist/runtime/components/docs/ADocsSearchButton.d.vue.ts +1 -1
- package/dist/runtime/components/docs/ADocsSearchButton.vue.d.ts +1 -1
- package/dist/runtime/components/docs/ADocsToc.d.vue.ts +2 -2
- package/dist/runtime/components/docs/ADocsToc.vue.d.ts +2 -2
- package/dist/runtime/components/editor/AEditorRedoButton.d.vue.ts +1 -1
- package/dist/runtime/components/editor/AEditorRedoButton.vue.d.ts +1 -1
- package/dist/runtime/components/editor/AEditorUndoButton.d.vue.ts +1 -1
- package/dist/runtime/components/editor/AEditorUndoButton.vue.d.ts +1 -1
- package/dist/runtime/components/editor/AFileGlbViewer.vue +27 -10
- package/dist/runtime/components/editor/ANodeInlineLabel.d.vue.ts +1 -1
- package/dist/runtime/components/editor/ANodeInlineLabel.vue.d.ts +1 -1
- package/dist/runtime/components/registry/APluginBrowser.d.vue.ts +23 -0
- package/dist/runtime/components/registry/APluginBrowser.vue +155 -0
- package/dist/runtime/components/registry/APluginBrowser.vue.d.ts +23 -0
- package/dist/runtime/components/registry/APluginCapabilityDialog.d.vue.ts +17 -0
- package/dist/runtime/components/registry/APluginCapabilityDialog.vue +159 -0
- package/dist/runtime/components/registry/APluginCapabilityDialog.vue.d.ts +17 -0
- package/dist/runtime/components/registry/APluginCard.d.vue.ts +20 -0
- package/dist/runtime/components/registry/APluginCard.vue +91 -0
- package/dist/runtime/components/registry/APluginCard.vue.d.ts +20 -0
- package/dist/runtime/components/registry/APluginDetail.d.vue.ts +18 -0
- package/dist/runtime/components/registry/APluginDetail.vue +252 -0
- package/dist/runtime/components/registry/APluginDetail.vue.d.ts +18 -0
- package/dist/runtime/components/renderers/ACodeRenderer.d.vue.ts +15 -0
- package/dist/runtime/components/renderers/ACodeRenderer.vue +68 -0
- package/dist/runtime/components/renderers/ACodeRenderer.vue.d.ts +15 -0
- package/dist/runtime/components/renderers/AGraphRenderer.vue +416 -120
- package/dist/runtime/components/renderers/AProseRenderer.d.vue.ts +2 -2
- package/dist/runtime/components/renderers/AProseRenderer.vue.d.ts +2 -2
- package/dist/runtime/components/renderers/sheets/ASheetsToolbar.d.vue.ts +4 -4
- package/dist/runtime/components/renderers/sheets/ASheetsToolbar.vue.d.ts +4 -4
- package/dist/runtime/components/shell/ABreadcrumbForDoc.d.vue.ts +11 -0
- package/dist/runtime/components/shell/ABreadcrumbForDoc.vue +16 -0
- package/dist/runtime/components/shell/ABreadcrumbForDoc.vue.d.ts +11 -0
- package/dist/runtime/components/shell/ASettingsSection.d.vue.ts +35 -0
- package/dist/runtime/components/shell/ASettingsSection.vue +26 -0
- package/dist/runtime/components/shell/ASettingsSection.vue.d.ts +35 -0
- package/dist/runtime/components/shell/ASidebar.d.vue.ts +1 -1
- package/dist/runtime/components/shell/ASidebar.vue.d.ts +1 -1
- package/dist/runtime/components/shell/AUserMenu.d.vue.ts +3 -0
- package/dist/runtime/components/shell/AUserMenu.vue +4 -0
- package/dist/runtime/components/shell/AUserMenu.vue.d.ts +3 -0
- package/dist/runtime/composables/useAbracadabraSchema.d.ts +83 -0
- package/dist/runtime/composables/useAbracadabraSchema.js +52 -0
- package/dist/runtime/composables/useAggregatedPresence.d.ts +1 -6
- package/dist/runtime/composables/useCalendarView.d.ts +1 -1
- package/dist/runtime/composables/useChat.js +1 -0
- package/dist/runtime/composables/useDocBreadcrumb.d.ts +21 -0
- package/dist/runtime/composables/useDocBreadcrumb.js +33 -0
- package/dist/runtime/composables/useDocEntryTyped.d.ts +60 -0
- package/dist/runtime/composables/useDocEntryTyped.js +70 -0
- package/dist/runtime/composables/useEditorDragHandle.js +18 -0
- package/dist/runtime/composables/useEditorSuggestions.js +2 -1
- package/dist/runtime/composables/useInstalledPlugins.d.ts +3 -21
- package/dist/runtime/composables/useInstalledPlugins.js +2 -12
- package/dist/runtime/composables/useMetaMenuItems.d.ts +21 -0
- package/dist/runtime/composables/useMetaMenuItems.js +115 -0
- package/dist/runtime/composables/useMetaValidator.d.ts +27 -0
- package/dist/runtime/composables/useMetaValidator.js +10 -0
- package/dist/runtime/composables/usePluginCatalog.d.ts +161 -0
- package/dist/runtime/composables/usePluginCatalog.js +234 -0
- package/dist/runtime/composables/useQuery.d.ts +79 -0
- package/dist/runtime/composables/useQuery.js +97 -0
- package/dist/runtime/composables/useSpaces.js +4 -5
- package/dist/runtime/composables/useTableView.d.ts +3 -3
- package/dist/runtime/composables/useTypedDoc.d.ts +97 -0
- package/dist/runtime/composables/useTypedDoc.js +114 -0
- package/dist/runtime/composables/useWebRTC.js +44 -5
- package/dist/runtime/extensions/document-meta.js +5 -0
- package/dist/runtime/extensions/timeline.d.ts +11 -0
- package/dist/runtime/extensions/timeline.js +52 -0
- package/dist/runtime/extensions/views/DocumentMetaView.d.vue.ts +4 -0
- package/dist/runtime/extensions/views/DocumentMetaView.vue +63 -0
- package/dist/runtime/extensions/views/DocumentMetaView.vue.d.ts +4 -0
- package/dist/runtime/extensions/views/TimelineItemView.d.vue.ts +4 -0
- package/dist/runtime/extensions/views/TimelineItemView.vue +131 -0
- package/dist/runtime/extensions/views/TimelineItemView.vue.d.ts +4 -0
- package/dist/runtime/extensions/views/TimelineView.d.vue.ts +9 -0
- package/dist/runtime/extensions/views/TimelineView.vue +29 -0
- package/dist/runtime/extensions/views/TimelineView.vue.d.ts +9 -0
- package/dist/runtime/locale.d.ts +2 -0
- package/dist/runtime/locale.js +2 -0
- package/dist/runtime/plugin-abracadabra.client.js +107 -6
- package/dist/runtime/plugin-registry.d.ts +11 -30
- package/dist/runtime/plugin-registry.js +2 -82
- package/dist/runtime/plugins/core.plugin.js +10 -4
- package/dist/runtime/server/api/_abracadabra/spaces.get.d.ts +1 -1
- package/dist/runtime/server/plugins/abracadabra-service.js +28 -0
- package/dist/runtime/server/utils/docCache.js +24 -3
- package/dist/runtime/server/utils/schemaServerSupport.d.ts +52 -0
- package/dist/runtime/server/utils/schemaServerSupport.js +51 -0
- package/dist/runtime/types.d.ts +63 -46
- package/dist/runtime/utils/docTypes.d.ts +15 -0
- package/dist/runtime/utils/docTypes.js +20 -0
- package/dist/runtime/utils/loadCodeMirror.d.ts +32 -0
- package/dist/runtime/utils/loadCodeMirror.js +65 -0
- package/dist/runtime/utils/loadThree.d.ts +18 -0
- package/dist/runtime/utils/loadThree.js +46 -0
- package/dist/runtime/utils/markdownToYjs.d.ts +1 -23
- package/dist/runtime/utils/markdownToYjs.js +5 -440
- package/dist/runtime/utils/schemaSupport.d.ts +60 -0
- package/dist/runtime/utils/schemaSupport.js +40 -0
- package/dist/runtime/utils/yjsConvert.d.ts +1 -14
- package/dist/runtime/utils/yjsConvert.js +5 -331
- package/package.json +86 -21
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { computed, shallowRef, watch, isRef } from "vue";
|
|
2
|
+
import { useDocTree } from "./useDocTree.js";
|
|
3
|
+
import { useAbracadabraSchema } from "./useAbracadabraSchema.js";
|
|
4
|
+
import { tryLoadSchemaModule } from "../utils/schemaSupport.js";
|
|
5
|
+
import { useRuntimeConfig } from "#imports";
|
|
6
|
+
const _moduleRef = shallowRef(null);
|
|
7
|
+
let _moduleRequested = false;
|
|
8
|
+
function _ensureSchemaModule() {
|
|
9
|
+
if (_moduleRequested) return;
|
|
10
|
+
_moduleRequested = true;
|
|
11
|
+
void tryLoadSchemaModule().then((m) => {
|
|
12
|
+
_moduleRef.value = m;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export function __resetUseDocEntryTypedCacheForTests() {
|
|
16
|
+
_moduleRef.value = null;
|
|
17
|
+
_moduleRequested = false;
|
|
18
|
+
}
|
|
19
|
+
export function __setSchemaModuleForTests(mod) {
|
|
20
|
+
_moduleRef.value = mod;
|
|
21
|
+
_moduleRequested = true;
|
|
22
|
+
}
|
|
23
|
+
function projectEntry(entry, expectedType) {
|
|
24
|
+
if (!entry) return null;
|
|
25
|
+
if (entry.type !== expectedType) return null;
|
|
26
|
+
return {
|
|
27
|
+
id: entry.id,
|
|
28
|
+
type: expectedType,
|
|
29
|
+
label: entry.label,
|
|
30
|
+
parentId: entry.parentId,
|
|
31
|
+
order: entry.order,
|
|
32
|
+
meta: entry.meta,
|
|
33
|
+
createdAt: entry.createdAt,
|
|
34
|
+
updatedAt: entry.updatedAt
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function useDocEntryTyped(docId, schema, expectedType, options = {}) {
|
|
38
|
+
const tree = useDocTree();
|
|
39
|
+
const { runMigrations: _ } = useAbracadabraSchema();
|
|
40
|
+
void _;
|
|
41
|
+
const cfg = useRuntimeConfig().public.abracadabra?.schema;
|
|
42
|
+
const migrateOnRead = options.migrate ?? cfg?.migrateOnRead ?? false;
|
|
43
|
+
if (migrateOnRead) _ensureSchemaModule();
|
|
44
|
+
return computed(() => {
|
|
45
|
+
const id = isRef(docId) ? docId.value : docId;
|
|
46
|
+
if (!id) return null;
|
|
47
|
+
const entry = tree.getEntry(id);
|
|
48
|
+
const projected = projectEntry(entry, expectedType);
|
|
49
|
+
if (!projected) return null;
|
|
50
|
+
if (!migrateOnRead) {
|
|
51
|
+
return { ...projected, migrated: false };
|
|
52
|
+
}
|
|
53
|
+
const mod = _moduleRef.value;
|
|
54
|
+
if (!mod) {
|
|
55
|
+
return { ...projected, migrated: false };
|
|
56
|
+
}
|
|
57
|
+
const before = projected.meta;
|
|
58
|
+
if (before === void 0 || before === null) {
|
|
59
|
+
return { ...projected, migrated: false };
|
|
60
|
+
}
|
|
61
|
+
const after = mod.runMigrations(schema, expectedType, before);
|
|
62
|
+
const migrated = after !== before;
|
|
63
|
+
return {
|
|
64
|
+
...projected,
|
|
65
|
+
meta: migrated ? after : before,
|
|
66
|
+
migrated
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
watch(_moduleRef, () => void 0, { flush: "sync" });
|
|
@@ -73,6 +73,24 @@ export function useEditorDragHandle(pageConfig) {
|
|
|
73
73
|
{ label: "Download image", icon: "i-lucide-download", to: node?.attrs?.src, download: true }
|
|
74
74
|
];
|
|
75
75
|
}
|
|
76
|
+
if (nodeType === "timeline") {
|
|
77
|
+
return [
|
|
78
|
+
{
|
|
79
|
+
label: "Add Event",
|
|
80
|
+
icon: "i-lucide-plus",
|
|
81
|
+
onSelect: () => {
|
|
82
|
+
if (pos === void 0) return;
|
|
83
|
+
const node = editor.state.doc.nodeAt(pos);
|
|
84
|
+
if (!node) return;
|
|
85
|
+
editor.chain().focus().insertContentAt(pos + node.nodeSize - 1, {
|
|
86
|
+
type: "timelineItem",
|
|
87
|
+
attrs: { date: "", label: `Event ${node.childCount + 1}`, icon: "" },
|
|
88
|
+
content: [{ type: "paragraph" }]
|
|
89
|
+
}).run();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
];
|
|
93
|
+
}
|
|
76
94
|
if (nodeType === "table") {
|
|
77
95
|
return [
|
|
78
96
|
{
|
|
@@ -73,7 +73,8 @@ export function useEditorSuggestions(options = {}) {
|
|
|
73
73
|
...extEnabled("mathBlock") ? [{ kind: "mathBlock", label: "Math (block)", icon: "i-lucide-square-sigma", description: "Display-mode LaTeX equation", keywords: ["katex", "latex", "equation", "formula", "math"] }] : [],
|
|
74
74
|
...extEnabled("mathInline") ? [{ kind: "mathInline", label: "Math (inline)", icon: "i-lucide-sigma", description: "Inline LaTeX expression", keywords: ["katex", "latex", "inline math"] }] : [],
|
|
75
75
|
...extEnabled("diff") ? [{ kind: "diff", label: "Diff", icon: "i-lucide-git-compare", description: "Side-by-side text diff", keywords: ["compare", "changes", "git"] }] : [],
|
|
76
|
-
...extEnabled("svgEmbed") ? [{ kind: "svgEmbed", label: "SVG embed", icon: "i-lucide-image", description: "Inline SVG (sanitized)", keywords: ["svg", "vector", "icon", "diagram"] }] : []
|
|
76
|
+
...extEnabled("svgEmbed") ? [{ kind: "svgEmbed", label: "SVG embed", icon: "i-lucide-image", description: "Inline SVG (sanitized)", keywords: ["svg", "vector", "icon", "diagram"] }] : [],
|
|
77
|
+
...extEnabled("timeline") ? [{ kind: "timeline", label: "Timeline", icon: "i-lucide-git-commit-vertical", description: "Vertical event timeline", keywords: ["events", "history", "milestones"] }] : []
|
|
77
78
|
]
|
|
78
79
|
];
|
|
79
80
|
try {
|
|
@@ -1,24 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
name: string;
|
|
5
|
-
label?: string;
|
|
6
|
-
version?: string;
|
|
7
|
-
description?: string;
|
|
8
|
-
enabled: boolean;
|
|
9
|
-
/** Error message from the last load attempt, if any */
|
|
10
|
-
error?: string;
|
|
11
|
-
installedAt: number;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Normalizes user-entered shorthand to a full CDN URL.
|
|
15
|
-
*
|
|
16
|
-
* Supported formats:
|
|
17
|
-
* - `npm:package-name[@version]` -> jsDelivr npm CDN
|
|
18
|
-
* - `github:user/repo[@branch]` -> jsDelivr GitHub CDN
|
|
19
|
-
* - Any other string is returned as-is (assumed to be a full URL)
|
|
20
|
-
*/
|
|
21
|
-
export declare function normalizePluginUrl(input: string): string;
|
|
1
|
+
import { normalizePluginUrl, type ExternalPluginEntry } from '@abraca/plugin';
|
|
2
|
+
export { normalizePluginUrl };
|
|
3
|
+
export type { ExternalPluginEntry };
|
|
22
4
|
export declare function useInstalledPlugins(): {
|
|
23
5
|
entries: import("@vueuse/shared").RemovableRef<ExternalPluginEntry[]>;
|
|
24
6
|
install: (rawUrl: string) => void;
|
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
import { useLocalStorage } from "@vueuse/core";
|
|
2
|
+
import { normalizePluginUrl } from "@abraca/plugin";
|
|
3
|
+
export { normalizePluginUrl };
|
|
2
4
|
const STORAGE_KEY = "abracadabra_external_plugins";
|
|
3
5
|
const DISABLED_BUILTINS_KEY = "abracadabra_disabled_builtins";
|
|
4
|
-
export function normalizePluginUrl(input) {
|
|
5
|
-
const trimmed = input.trim();
|
|
6
|
-
if (trimmed.startsWith("npm:")) {
|
|
7
|
-
const pkg = trimmed.slice(4);
|
|
8
|
-
return `https://cdn.jsdelivr.net/npm/${pkg}/dist/plugin.js`;
|
|
9
|
-
}
|
|
10
|
-
if (trimmed.startsWith("github:")) {
|
|
11
|
-
const repo = trimmed.slice(7);
|
|
12
|
-
return `https://cdn.jsdelivr.net/gh/${repo}/dist/plugin.js`;
|
|
13
|
-
}
|
|
14
|
-
return trimmed;
|
|
15
|
-
}
|
|
16
6
|
export function useInstalledPlugins() {
|
|
17
7
|
const entries = useLocalStorage(STORAGE_KEY, []);
|
|
18
8
|
const disabledBuiltins = useLocalStorage(DISABLED_BUILTINS_KEY, []);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useMetaMenuItems — unified "Add Property" menu items for documentMeta.
|
|
3
|
+
*
|
|
4
|
+
* Used by:
|
|
5
|
+
* - The (+) button inside DocumentMetaView
|
|
6
|
+
* - The drag-handle menu on the documentMeta row
|
|
7
|
+
*
|
|
8
|
+
* Returns two groups:
|
|
9
|
+
* [0] Schema properties (from `editor.storage.metaField.configFields`)
|
|
10
|
+
* [1] Standard properties (from `META_FIELD_DEFINITIONS`)
|
|
11
|
+
*
|
|
12
|
+
* Items already present are marked with a check trailing icon. Schema fields
|
|
13
|
+
* focus the existing chip instead of duplicating; standard fields insert a
|
|
14
|
+
* new chip (their `metaKey` is timestamped, so duplicates are intentional)
|
|
15
|
+
* unless they're singleton field types (rating, icon, color, members).
|
|
16
|
+
*
|
|
17
|
+
* Ported 1:1 from cou-sh/app/composables/useMetaMenuItems.ts.
|
|
18
|
+
*/
|
|
19
|
+
import type { Editor } from '@tiptap/vue-3';
|
|
20
|
+
import type { DropdownMenuItem } from '@nuxt/ui';
|
|
21
|
+
export declare function buildMetaMenuItems(editor: Editor): DropdownMenuItem[][];
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { META_FIELD_DEFINITIONS } from "../utils/metaFieldDefinitions.js";
|
|
2
|
+
import { schemaFieldToAttrs } from "../extensions/meta-field.js";
|
|
3
|
+
function collectPresentFields(editor) {
|
|
4
|
+
const present = [];
|
|
5
|
+
const doc = editor.state.doc;
|
|
6
|
+
doc.forEach((node, offset) => {
|
|
7
|
+
if (node.type.name !== "documentMeta") return;
|
|
8
|
+
node.forEach((child, childOffset) => {
|
|
9
|
+
if (child.type.name !== "metaField") return;
|
|
10
|
+
present.push({
|
|
11
|
+
fieldType: child.attrs.fieldType ?? "",
|
|
12
|
+
metaKey: child.attrs.metaKey ?? "",
|
|
13
|
+
startKey: child.attrs.startKey ?? "",
|
|
14
|
+
endKey: child.attrs.endKey ?? "",
|
|
15
|
+
latKey: child.attrs.latKey ?? "",
|
|
16
|
+
lngKey: child.attrs.lngKey ?? "",
|
|
17
|
+
userDefined: !!child.attrs.userDefined,
|
|
18
|
+
pos: offset + 1 + childOffset
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
return present;
|
|
23
|
+
}
|
|
24
|
+
function schemaFieldKey(field) {
|
|
25
|
+
if (field.type === "daterange" || field.type === "timerange")
|
|
26
|
+
return `${field.type}:${String(field.startKey)}|${String(field.endKey)}`;
|
|
27
|
+
if (field.type === "datetimerange")
|
|
28
|
+
return `${field.type}:${String(field.startKey)}|${String(field.endKey)}`;
|
|
29
|
+
if (field.type === "location")
|
|
30
|
+
return `${field.type}:${String(field.latKey)}|${String(field.lngKey)}`;
|
|
31
|
+
if ("key" in field) return `${field.type}:${String(field.key)}`;
|
|
32
|
+
return field.type;
|
|
33
|
+
}
|
|
34
|
+
function presentFieldKey(p) {
|
|
35
|
+
if (p.fieldType === "daterange" || p.fieldType === "timerange" || p.fieldType === "datetimerange")
|
|
36
|
+
return `${p.fieldType}:${p.startKey}|${p.endKey}`;
|
|
37
|
+
if (p.fieldType === "location")
|
|
38
|
+
return `${p.fieldType}:${p.latKey}|${p.lngKey}`;
|
|
39
|
+
return `${p.fieldType}:${p.metaKey}`;
|
|
40
|
+
}
|
|
41
|
+
function focusExistingChip(editor, pos) {
|
|
42
|
+
try {
|
|
43
|
+
const { node } = editor.view.domAtPos(pos);
|
|
44
|
+
const el = node instanceof Element ? node : node.parentElement;
|
|
45
|
+
if (!el) return;
|
|
46
|
+
el.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "nearest" });
|
|
47
|
+
el.classList.add("meta-chip-flash");
|
|
48
|
+
setTimeout(() => el.classList.remove("meta-chip-flash"), 900);
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function buildMetaMenuItems(editor) {
|
|
53
|
+
const storage = editor.storage?.metaField;
|
|
54
|
+
const configFields = storage?.configFields ?? [];
|
|
55
|
+
const present = collectPresentFields(editor);
|
|
56
|
+
const presentKeys = new Set(present.map(presentFieldKey));
|
|
57
|
+
const presentFieldTypes = new Set(present.map((p) => p.fieldType));
|
|
58
|
+
const groups = [];
|
|
59
|
+
if (configFields.length) {
|
|
60
|
+
groups.push(configFields.map((f) => {
|
|
61
|
+
const key = schemaFieldKey(f);
|
|
62
|
+
const already = presentKeys.has(key);
|
|
63
|
+
const existing = already ? present.find((p) => presentFieldKey(p) === key) : void 0;
|
|
64
|
+
return {
|
|
65
|
+
label: f.label || ("key" in f ? String(f.key) : f.type),
|
|
66
|
+
icon: "i-lucide-settings-2",
|
|
67
|
+
trailingIcon: already ? "i-lucide-check" : void 0,
|
|
68
|
+
onSelect: () => {
|
|
69
|
+
if (already && existing) {
|
|
70
|
+
focusExistingChip(editor, existing.pos);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const attrs = schemaFieldToAttrs(f);
|
|
74
|
+
signalAutoOpen(editor, attrs);
|
|
75
|
+
editor.commands.insertMetaField(attrs);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
groups.push(META_FIELD_DEFINITIONS.map((def) => {
|
|
81
|
+
const sampleAttrs = def.buildAttrs();
|
|
82
|
+
const already = presentFieldTypes.has(String(sampleAttrs.fieldType));
|
|
83
|
+
const existing = already ? present.find((p) => p.fieldType === sampleAttrs.fieldType) : void 0;
|
|
84
|
+
return {
|
|
85
|
+
label: def.label,
|
|
86
|
+
icon: def.icon,
|
|
87
|
+
trailingIcon: already ? "i-lucide-check" : void 0,
|
|
88
|
+
onSelect: () => {
|
|
89
|
+
if (already && existing && isSingletonFieldType(String(sampleAttrs.fieldType))) {
|
|
90
|
+
focusExistingChip(editor, existing.pos);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const attrs = def.buildAttrs();
|
|
94
|
+
signalAutoOpen(editor, attrs);
|
|
95
|
+
editor.commands.insertMetaField(attrs);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}));
|
|
99
|
+
return groups;
|
|
100
|
+
}
|
|
101
|
+
function isSingletonFieldType(fieldType) {
|
|
102
|
+
return fieldType === "rating" || fieldType === "icon" || fieldType === "colorPreset" || fieldType === "colorPicker" || fieldType === "members";
|
|
103
|
+
}
|
|
104
|
+
function isPopoverFieldType(fieldType) {
|
|
105
|
+
return fieldType === "colorPreset" || fieldType === "colorPicker" || fieldType === "icon" || fieldType === "location" || fieldType === "select" || fieldType === "multiselect" || fieldType === "members" || fieldType === "textarea" || fieldType === "date" || fieldType === "datetime" || fieldType === "time" || fieldType === "daterange" || fieldType === "datetimerange";
|
|
106
|
+
}
|
|
107
|
+
function signalAutoOpen(editor, attrs) {
|
|
108
|
+
const storage = editor.storage?.metaField;
|
|
109
|
+
if (!storage) return;
|
|
110
|
+
const fieldType = String(attrs.fieldType ?? "");
|
|
111
|
+
if (!isPopoverFieldType(fieldType)) return;
|
|
112
|
+
const marker = String(attrs.metaKey ?? attrs.startKey ?? attrs.latKey ?? "");
|
|
113
|
+
if (!marker) return;
|
|
114
|
+
storage.pendingOpen = `${fieldType}:${marker}`;
|
|
115
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useMetaValidator — ad-hoc meta validation for UI forms.
|
|
3
|
+
*
|
|
4
|
+
* Returns a single `validate(typeName, meta)` function that delegates to
|
|
5
|
+
* whichever registry was attached via the `abracadabra:before-boot` hook.
|
|
6
|
+
* Returns `{ ok: true, value }` for unknown doc-types (Rule 4).
|
|
7
|
+
*
|
|
8
|
+
* Intended for properties-panel inputs and similar UI surfaces that
|
|
9
|
+
* want a non-throwing pre-write check. For the throwing path, use
|
|
10
|
+
* `useTypedDoc(...)` (which validates inside `update`/`set`).
|
|
11
|
+
*/
|
|
12
|
+
import { type SchemaRegistryLike } from './useAbracadabraSchema.js';
|
|
13
|
+
export interface MetaValidationFailure {
|
|
14
|
+
ok: false;
|
|
15
|
+
errors: ReadonlyArray<{
|
|
16
|
+
path: ReadonlyArray<PropertyKey>;
|
|
17
|
+
message: string;
|
|
18
|
+
code?: string;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
export interface MetaValidationOk {
|
|
22
|
+
ok: true;
|
|
23
|
+
value: unknown;
|
|
24
|
+
}
|
|
25
|
+
export declare function useMetaValidator(_explicitSchema?: SchemaRegistryLike): {
|
|
26
|
+
validate: (typeName: string, meta: unknown) => MetaValidationOk | MetaValidationFailure;
|
|
27
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useAbracadabraSchema } from "./useAbracadabraSchema.js";
|
|
2
|
+
export function useMetaValidator(_explicitSchema) {
|
|
3
|
+
const { validateMeta } = useAbracadabraSchema();
|
|
4
|
+
return {
|
|
5
|
+
validate(typeName, meta) {
|
|
6
|
+
const result = validateMeta(typeName, meta);
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { PluginManifest, PluginCapability } from '@abraca/plugin';
|
|
2
|
+
export interface CatalogPlugin {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string | null;
|
|
5
|
+
description: string;
|
|
6
|
+
repository: string | null;
|
|
7
|
+
homepage: string | null;
|
|
8
|
+
categories: string[];
|
|
9
|
+
pricing: string;
|
|
10
|
+
status: string;
|
|
11
|
+
latest_version: string | null;
|
|
12
|
+
owner_login: string | null;
|
|
13
|
+
created_at: string;
|
|
14
|
+
updated_at: string;
|
|
15
|
+
}
|
|
16
|
+
export interface CatalogVersionSummary {
|
|
17
|
+
version: string;
|
|
18
|
+
integrity: string;
|
|
19
|
+
status: string;
|
|
20
|
+
github_tag: string | null;
|
|
21
|
+
submitted_at: string;
|
|
22
|
+
scanned_at: string | null;
|
|
23
|
+
}
|
|
24
|
+
export interface CatalogPluginDetail {
|
|
25
|
+
plugin: CatalogPlugin;
|
|
26
|
+
versions: CatalogVersionSummary[];
|
|
27
|
+
}
|
|
28
|
+
export interface CatalogVersionDetail {
|
|
29
|
+
plugin_id: string;
|
|
30
|
+
version: string;
|
|
31
|
+
integrity: string;
|
|
32
|
+
status: string;
|
|
33
|
+
artifact_url: string | null;
|
|
34
|
+
github_tag: string | null;
|
|
35
|
+
submitted_at: string;
|
|
36
|
+
scanned_at: string | null;
|
|
37
|
+
manifest: PluginManifest;
|
|
38
|
+
}
|
|
39
|
+
export interface CatalogListResult {
|
|
40
|
+
plugins: CatalogPlugin[];
|
|
41
|
+
pagination: {
|
|
42
|
+
limit: number;
|
|
43
|
+
offset: number;
|
|
44
|
+
has_more: boolean;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export interface CatalogListParams {
|
|
48
|
+
category?: string;
|
|
49
|
+
search?: string;
|
|
50
|
+
limit?: number;
|
|
51
|
+
offset?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Mirrors `GET /plugins/policy` on the abracadabra server. Fields are
|
|
55
|
+
* stable across server versions; new fields are additive.
|
|
56
|
+
*/
|
|
57
|
+
export interface ServerPluginPolicy {
|
|
58
|
+
registry_url: string;
|
|
59
|
+
allowlist: string[];
|
|
60
|
+
allow_user_install: boolean;
|
|
61
|
+
allow_unsafe_install: boolean;
|
|
62
|
+
auto_update: 'off' | 'patch' | 'minor' | 'major';
|
|
63
|
+
require_review_on_cap_growth: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** One row in `checkUpdates()`'s report. */
|
|
66
|
+
export interface AutoUpdateOutcome {
|
|
67
|
+
pluginId: string;
|
|
68
|
+
fromVersion: string;
|
|
69
|
+
toVersion: string;
|
|
70
|
+
/**
|
|
71
|
+
* `applied` — version was bumped, artifact URL re-installed.
|
|
72
|
+
* `pending-review` — version is newer but introduces capabilities not
|
|
73
|
+
* present in the installed version; `require_review_on_cap_growth`
|
|
74
|
+
* is on, so the user must explicitly accept the new manifest.
|
|
75
|
+
* `skipped-policy` — bump fell outside `auto_update` (e.g. a minor
|
|
76
|
+
* bump while policy is `patch`).
|
|
77
|
+
* `skipped-blocked` — server policy blocks this plugin entirely now.
|
|
78
|
+
*/
|
|
79
|
+
state: 'applied' | 'pending-review' | 'skipped-policy' | 'skipped-blocked';
|
|
80
|
+
/** Capabilities new in the upstream version vs the installed one. */
|
|
81
|
+
newCapabilities: PluginCapability[];
|
|
82
|
+
/** Set when state === 'pending-review' or skipped — actionable reason. */
|
|
83
|
+
reason?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `policyDecisionFor` outcome. Drives every Install button across the UI —
|
|
87
|
+
* one helper, three rendered states, no scattered "if" chains.
|
|
88
|
+
*/
|
|
89
|
+
export type PolicyDecision =
|
|
90
|
+
/** Plugin is in the server's `allowlist`. Install + auto-update silently. */
|
|
91
|
+
{
|
|
92
|
+
state: 'allowed';
|
|
93
|
+
reason?: undefined;
|
|
94
|
+
}
|
|
95
|
+
/** Not allowlisted but `allow_user_install` is true. Show capability disclosure. */
|
|
96
|
+
| {
|
|
97
|
+
state: 'gated';
|
|
98
|
+
reason?: undefined;
|
|
99
|
+
}
|
|
100
|
+
/** Not allowlisted and `allow_user_install` is false. Install button hidden. */
|
|
101
|
+
| {
|
|
102
|
+
state: 'blocked';
|
|
103
|
+
reason: string;
|
|
104
|
+
}
|
|
105
|
+
/** Policy hasn't been fetched yet — fall back to permissive (allowed). */
|
|
106
|
+
| {
|
|
107
|
+
state: 'unknown';
|
|
108
|
+
reason?: undefined;
|
|
109
|
+
};
|
|
110
|
+
export declare function usePluginCatalog(): {
|
|
111
|
+
/** Public catalog (latest page fetched). Refresh via `list()`. */
|
|
112
|
+
plugins: import("vue").ShallowRef<CatalogPlugin[], CatalogPlugin[]>;
|
|
113
|
+
/** Distinct category tags across `plugins`, alphabetical. */
|
|
114
|
+
categories: import("vue").ComputedRef<string[]>;
|
|
115
|
+
pagination: import("vue").Ref<{
|
|
116
|
+
limit: number;
|
|
117
|
+
offset: number;
|
|
118
|
+
has_more: boolean;
|
|
119
|
+
} | null, {
|
|
120
|
+
limit: number;
|
|
121
|
+
offset: number;
|
|
122
|
+
has_more: boolean;
|
|
123
|
+
} | {
|
|
124
|
+
limit: number;
|
|
125
|
+
offset: number;
|
|
126
|
+
has_more: boolean;
|
|
127
|
+
} | null>;
|
|
128
|
+
isLoading: import("vue").Ref<boolean, boolean>;
|
|
129
|
+
error: import("vue").Ref<Error | null, Error | null>;
|
|
130
|
+
list: (params?: CatalogListParams) => Promise<CatalogListResult>;
|
|
131
|
+
get: (id: string) => Promise<CatalogPluginDetail>;
|
|
132
|
+
getLatest: (id: string) => Promise<CatalogVersionDetail>;
|
|
133
|
+
getVersion: (id: string, version: string) => Promise<CatalogVersionDetail>;
|
|
134
|
+
install: (id: string) => Promise<CatalogVersionDetail>;
|
|
135
|
+
installFromUrl: (url: string) => Promise<void>;
|
|
136
|
+
isInstalled: (id: string) => boolean;
|
|
137
|
+
capabilitiesFor: (detail: CatalogVersionDetail | PluginManifest) => {
|
|
138
|
+
required: PluginCapability[];
|
|
139
|
+
optional: PluginCapability[];
|
|
140
|
+
};
|
|
141
|
+
/** Current server policy. `null` until `loadPolicy()` resolves. */
|
|
142
|
+
policy: import("vue").Ref<{
|
|
143
|
+
registry_url: string;
|
|
144
|
+
allowlist: string[];
|
|
145
|
+
allow_user_install: boolean;
|
|
146
|
+
allow_unsafe_install: boolean;
|
|
147
|
+
auto_update: "off" | "patch" | "minor" | "major";
|
|
148
|
+
require_review_on_cap_growth: boolean;
|
|
149
|
+
} | null, ServerPluginPolicy | {
|
|
150
|
+
registry_url: string;
|
|
151
|
+
allowlist: string[];
|
|
152
|
+
allow_user_install: boolean;
|
|
153
|
+
allow_unsafe_install: boolean;
|
|
154
|
+
auto_update: "off" | "patch" | "minor" | "major";
|
|
155
|
+
require_review_on_cap_growth: boolean;
|
|
156
|
+
} | null>;
|
|
157
|
+
loadPolicy: (serverUrl: string) => Promise<ServerPluginPolicy>;
|
|
158
|
+
/** Per-plugin install gate. Drives every Install button across the UI. */
|
|
159
|
+
policyDecisionFor: (pluginId: string) => PolicyDecision;
|
|
160
|
+
checkUpdates: () => Promise<AutoUpdateOutcome[]>;
|
|
161
|
+
};
|