mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
- Box (ui.catalog 4, since 1.4.248): direction, gap/padding/paddingX/ paddingY/margin on the gap scale, align, justify, wrap, grow, shrink, width/height/min/max (px up to 4096 or whole percents), columns, scroll, border (true or a tone), radius, background (theme surfaces or tone tints). Strict props: hex colours, CSS lengths and unknown props are dropped with a warning like other components. - Renderer maps props to theme tokens through data attributes; sizes reach inline style only as validated px or percents. - Typings, docs, skill references, gallery Layout tab; orca/testing finds Boxes and summarizes danger or warning Boxes.
1579 lines
47 KiB
JavaScript
1579 lines
47 KiB
JavaScript
// Generated by config/scripts/build-plugin-declarative-runtime.mjs from
|
|
// src/shared/plugins/declarative/runtime/. Do not edit.
|
|
|
|
// src/shared/plugins/frames/plugin-frame-source-map-prelude.ts
|
|
var PLUGIN_SOURCE_MAP_HOOK = "orca.pluginSourceMap";
|
|
function pluginSourceMapPrelude(mappings) {
|
|
return `globalThis[Symbol.for(${JSON.stringify(PLUGIN_SOURCE_MAP_HOOK)})]?.(import.meta.url,${JSON.stringify(mappings)});`;
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-source-maps.ts
|
|
var BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
var MAX_MODULES = 256;
|
|
var maps = /* @__PURE__ */ new Map();
|
|
function decodeSourceMapMappings(mappings) {
|
|
const lines = [];
|
|
const state2 = [0, 0, 0, 0, 0];
|
|
for (const line of mappings.split(";")) {
|
|
const segments = [];
|
|
state2[0] = 0;
|
|
for (const text of line.split(",")) {
|
|
const fields = [];
|
|
let value = 0;
|
|
let shift = 0;
|
|
for (const char of text) {
|
|
const digit = BASE64.indexOf(char);
|
|
value += (digit & 31) << shift;
|
|
if (digit & 32) {
|
|
shift += 5;
|
|
continue;
|
|
}
|
|
fields.push(value & 1 ? -(value >>> 1) : value >>> 1);
|
|
value = 0;
|
|
shift = 0;
|
|
}
|
|
for (const [index, delta] of fields.entries()) {
|
|
state2[index] += delta;
|
|
}
|
|
if (fields.length >= 4) {
|
|
segments.push([state2[0], state2[2], state2[3]]);
|
|
}
|
|
}
|
|
lines.push(segments);
|
|
}
|
|
return lines;
|
|
}
|
|
function registerPluginSourceMap(url, mappings) {
|
|
maps.delete(url);
|
|
maps.set(url, { mappings, lineOneShift: pluginSourceMapPrelude(mappings).length });
|
|
for (const oldest of maps.keys()) {
|
|
if (maps.size <= MAX_MODULES) {
|
|
break;
|
|
}
|
|
maps.delete(oldest);
|
|
}
|
|
}
|
|
function installPluginSourceMapHook() {
|
|
Object.assign(globalThis, { [Symbol.for(PLUGIN_SOURCE_MAP_HOOK)]: registerPluginSourceMap });
|
|
}
|
|
function originalPluginPosition(url, line, column) {
|
|
const map = maps.get(url);
|
|
if (!map) {
|
|
return null;
|
|
}
|
|
map.lines ??= decodeSourceMapMappings(map.mappings);
|
|
const generated = column - 1 - (line === 1 ? map.lineOneShift : 0);
|
|
let found;
|
|
for (const segment of map.lines[line - 1] ?? []) {
|
|
if (segment[0] > generated) {
|
|
break;
|
|
}
|
|
found = segment;
|
|
}
|
|
return found ? { line: found[1] + 1, column: found[2] + 1 } : null;
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-atoms.ts
|
|
var ATOM = /* @__PURE__ */ Symbol.for("orca.declarative.atom");
|
|
var activeTracker = null;
|
|
var ATOM_KEY = /^[A-Za-z0-9_.:-]{1,128}$/;
|
|
var keyed = /* @__PURE__ */ new Map();
|
|
var snapshot = {
|
|
memory: /* @__PURE__ */ new Map(),
|
|
plugin: /* @__PURE__ */ new Map(),
|
|
workspace: /* @__PURE__ */ new Map()
|
|
};
|
|
var onPersistedChange = null;
|
|
var onMemoryChange = null;
|
|
var onStorageAtom = null;
|
|
var writableAtoms = /* @__PURE__ */ new Set();
|
|
function create(initial, fields = {}) {
|
|
const created = Object.freeze({
|
|
[ATOM]: {
|
|
value: initial,
|
|
initial,
|
|
listeners: /* @__PURE__ */ new Set(),
|
|
readonly: false,
|
|
persist: "memory",
|
|
version: 1,
|
|
...fields
|
|
}
|
|
});
|
|
if (!created[ATOM].readonly) {
|
|
writableAtoms.add(created[ATOM]);
|
|
}
|
|
return created;
|
|
}
|
|
function resetAtoms() {
|
|
snapshot.plugin = /* @__PURE__ */ new Map();
|
|
snapshot.workspace = /* @__PURE__ */ new Map();
|
|
for (const inner of writableAtoms) {
|
|
inner.value = inner.initial;
|
|
}
|
|
}
|
|
function stored(inner) {
|
|
if (!inner.key || inner.persist === "storage") {
|
|
return { found: false };
|
|
}
|
|
const entry = snapshot[inner.persist].get(inner.key);
|
|
if (!entry) {
|
|
return { found: false };
|
|
}
|
|
if (entry.version === inner.version) {
|
|
return { found: true, value: entry.value };
|
|
}
|
|
return inner.migrate && inner.persist !== "memory" ? { found: true, value: inner.migrate(entry.value, entry.version) } : { found: false };
|
|
}
|
|
function atom(...args) {
|
|
if (args.length === 1) {
|
|
return create(args[0]);
|
|
}
|
|
const [key, initial, options = {}] = args;
|
|
if (typeof key !== "string" || !ATOM_KEY.test(key)) {
|
|
throw new TypeError("an atom key is 1-128 letters, digits, or . _ : -");
|
|
}
|
|
if (keyed.has(key)) {
|
|
throw new Error(`atom key "${key}" is already used`);
|
|
}
|
|
const persist = options.persist ?? "memory";
|
|
if (!["memory", "plugin", "workspace", "storage"].includes(persist)) {
|
|
throw new TypeError('persist is "memory", "plugin", "workspace" or "storage"');
|
|
}
|
|
const result = create(initial, {
|
|
key,
|
|
persist,
|
|
version: options.version ?? 1,
|
|
migrate: options.migrate
|
|
});
|
|
const inner = result[ATOM];
|
|
const hydrated = stored(inner);
|
|
if (hydrated.found) {
|
|
inner.value = hydrated.value;
|
|
}
|
|
keyed.set(key, result);
|
|
if (persist === "storage") {
|
|
onStorageAtom?.(key);
|
|
}
|
|
return result;
|
|
}
|
|
function derived(compute) {
|
|
if (typeof compute !== "function") {
|
|
throw new TypeError("derived() takes a function");
|
|
}
|
|
return create(compute(), { readonly: true, compute });
|
|
}
|
|
function hostAtom(initial) {
|
|
const result = create(initial, { readonly: true });
|
|
return { atom: result, set: (value) => assign(result[ATOM], value, false) };
|
|
}
|
|
function state(target) {
|
|
const inner = typeof target === "object" && target !== null ? target[ATOM] : void 0;
|
|
if (!inner) {
|
|
throw new TypeError("expected an atom created by atom()");
|
|
}
|
|
return inner;
|
|
}
|
|
function read(target) {
|
|
const inner = state(target);
|
|
if (inner.compute) {
|
|
return inner.compute();
|
|
}
|
|
activeTracker?.(target);
|
|
return inner.value;
|
|
}
|
|
function assign(inner, value, persist) {
|
|
if (Object.is(value, inner.value)) {
|
|
return;
|
|
}
|
|
inner.value = value;
|
|
for (const listener of inner.listeners) {
|
|
listener();
|
|
}
|
|
const target = persist && inner.key ? keyed.get(inner.key) : void 0;
|
|
const notify = inner.persist === "memory" ? onMemoryChange : onPersistedChange;
|
|
if (target) {
|
|
notify?.(target);
|
|
}
|
|
}
|
|
function writable(target, verb) {
|
|
if (activeTracker) {
|
|
throw new Error(`${verb}() was called while rendering; call it from an event handler instead`);
|
|
}
|
|
const inner = state(target);
|
|
if (inner.readonly) {
|
|
throw new TypeError(`${verb}() cannot change a read-only atom`);
|
|
}
|
|
return inner;
|
|
}
|
|
function set(target, value) {
|
|
assign(writable(target, "set"), value, true);
|
|
}
|
|
function update(target, updater) {
|
|
const inner = writable(target, "update");
|
|
if (typeof updater !== "function") {
|
|
throw new TypeError("update() takes an updater function; use set() to store a value");
|
|
}
|
|
assign(inner, updater(inner.value), true);
|
|
}
|
|
function subscribeAtom(target, listener) {
|
|
const inner = state(target);
|
|
inner.listeners.add(listener);
|
|
return () => inner.listeners.delete(listener);
|
|
}
|
|
function trackAtomReads(reads, render) {
|
|
const previous = activeTracker;
|
|
activeTracker = (target) => reads.add(target);
|
|
try {
|
|
return render();
|
|
} finally {
|
|
activeTracker = previous;
|
|
}
|
|
}
|
|
function persistedEntry(target) {
|
|
const inner = state(target);
|
|
return inner.key && inner.persist !== "memory" ? { scope: inner.persist, key: inner.key, version: inner.version, value: inner.value } : null;
|
|
}
|
|
function watchPersistedAtoms(listener) {
|
|
onPersistedChange = listener;
|
|
}
|
|
function memoryEntry(target) {
|
|
const inner = state(target);
|
|
return inner.key && inner.persist === "memory" ? { key: inner.key, version: inner.version, value: inner.value } : null;
|
|
}
|
|
function watchMemoryAtoms(listener) {
|
|
onMemoryChange = listener;
|
|
}
|
|
function watchStorageAtoms(listener) {
|
|
onStorageAtom = listener;
|
|
for (const [key, target] of keyed) {
|
|
if (listener && target[ATOM].persist === "storage") {
|
|
listener(key);
|
|
}
|
|
}
|
|
}
|
|
function applyStorageValue(key, value) {
|
|
const inner = keyed.get(key)?.[ATOM];
|
|
if (inner?.persist === "storage") {
|
|
assign(inner, value === void 0 ? inner.initial : value, false);
|
|
}
|
|
}
|
|
function captureAtomValues() {
|
|
const values = /* @__PURE__ */ new Map();
|
|
for (const inner of writableAtoms) {
|
|
values.set(inner, inner.value);
|
|
}
|
|
return () => {
|
|
for (const [inner, value] of values) {
|
|
assign(inner, value, true);
|
|
}
|
|
};
|
|
}
|
|
function hydrateAtoms(scope, values) {
|
|
snapshot[scope] = new Map(values);
|
|
for (const target of keyed.values()) {
|
|
const inner = target[ATOM];
|
|
if (inner.persist !== scope) {
|
|
continue;
|
|
}
|
|
const hydrated = stored(inner);
|
|
if (hydrated.found || scope !== "memory") {
|
|
assign(inner, hydrated.found ? hydrated.value : inner.initial, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-error-location.ts
|
|
var ORCA_RESERVED_PATH_PREFIX = "/__orca/";
|
|
var STACK_FRAME_URL = /([a-z][a-z0-9+.-]*:\/\/[^\s()]+):(\d+):(\d+)\)?\s*$/i;
|
|
var LOCATED_MESSAGE = /^[^\n:]+\.[a-z]+:\d+:\d+: /i;
|
|
function pageBase() {
|
|
return typeof location === "undefined" ? null : new URL("/", location.href).href;
|
|
}
|
|
function pluginFrame(stackLine, base) {
|
|
const match = STACK_FRAME_URL.exec(stackLine);
|
|
const url = match?.[1];
|
|
if (!match || !url?.startsWith(base)) {
|
|
return null;
|
|
}
|
|
const path = `/${url.slice(base.length).split(/[?#]/)[0] ?? ""}`;
|
|
if (path.startsWith(ORCA_RESERVED_PATH_PREFIX)) {
|
|
return "orca";
|
|
}
|
|
const generated = { line: Number(match[2]), column: Number(match[3]) };
|
|
const at = originalPluginPosition(url, generated.line, generated.column) ?? generated;
|
|
try {
|
|
return { path: decodeURIComponent(path.slice(1)), ...at };
|
|
} catch {
|
|
return { path: path.slice(1), ...at };
|
|
}
|
|
}
|
|
var formatFrame = (frame) => `${frame.path}:${frame.line}:${frame.column}`;
|
|
function pluginErrorLocation(stack, base) {
|
|
for (const line of stack.split("\n")) {
|
|
const frame = pluginFrame(line, base);
|
|
if (frame && frame !== "orca") {
|
|
return formatFrame(frame);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function describePluginError(error, base = pageBase()) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
const stack = error instanceof Error && typeof error.stack === "string" ? error.stack : "";
|
|
const at = base && !LOCATED_MESSAGE.test(message) ? pluginErrorLocation(stack, base) : null;
|
|
if (!at) {
|
|
return message;
|
|
}
|
|
const file = at.replace(/:\d+:\d+$/, "");
|
|
return message.startsWith(`${file}:`) ? message : `${at}: ${message}`;
|
|
}
|
|
function describePluginErrorForLog(error, base = pageBase()) {
|
|
const headline = describePluginError(error, base);
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
const stack = error instanceof Error && typeof error.stack === "string" ? error.stack : "";
|
|
if (!base || LOCATED_MESSAGE.test(message)) {
|
|
return base ? headline : stack || headline;
|
|
}
|
|
const frames = stack.split("\n").flatMap((line) => {
|
|
if (!/^\s+at /.test(line)) {
|
|
return [];
|
|
}
|
|
const frame = pluginFrame(line, base);
|
|
if (frame === "orca") {
|
|
return [];
|
|
}
|
|
const closing = line.trimEnd().endsWith(")") ? ")" : "";
|
|
return [frame ? line.replace(STACK_FRAME_URL, () => `${formatFrame(frame)}${closing}`) : line];
|
|
});
|
|
return [headline, ...frames].join("\n");
|
|
}
|
|
|
|
// src/shared/plugins/declarative/plugin-declarative-message-names.ts
|
|
var PLUGIN_DECLARATIVE_MESSAGES = {
|
|
/** guest → host, `{ document, revision, tree }` */
|
|
render: "ui.render",
|
|
/** host → guest, `{ document, revision, callback, args, edit? }`: `revision` is the tree shown. */
|
|
event: "ui.event",
|
|
/** guest → host, `{ document, revision, message }`: the plugin threw; the last good tree stays. */
|
|
error: "ui.error",
|
|
/** host → guest, `{ document, revision, accepted, retryAfterMs? }`: whether a render is shown. */
|
|
ack: "ui.ack",
|
|
/** guest → host, `{ document }`: the runtime is up and waits for `ui.hydrate` to render. */
|
|
ready: "ui.ready",
|
|
/**
|
|
* host → guest, `{ panel?, visible?, plugin?, workspace?, memory?, retain? }`: persisted state and
|
|
* context; `memory` hands keyed memory atoms to a dev hot reload, `retain` asks for `ui.retain`.
|
|
*/
|
|
hydrate: "ui.hydrate",
|
|
/** host → guest, `{ visible }` */
|
|
visibility: "ui.visibility",
|
|
/** guest → host, `{ entries: [{ scope, key, version, value }] }`: persisted atoms changed. */
|
|
persist: "ui.persist",
|
|
/** guest → host, `{ document, entries: [{ key, version, value? }] }`: keyed memory atoms changed. */
|
|
retain: "ui.retain",
|
|
/** host → guest, `{ document }`: remember every atom's value before `orca plugin shot --act`. */
|
|
snapshot: "ui.snapshot",
|
|
/** host → guest, `{ document }`: put the atoms back to the last `ui.snapshot`. */
|
|
restore: "ui.restore"
|
|
};
|
|
var PLUGIN_DECLARATIVE_CATALOG_VERSION = 4;
|
|
var PLUGIN_DECLARATIVE_COMPONENT_PROPS = {
|
|
Stack: ["gap", "align"],
|
|
Row: ["gap", "align", "justify", "wrap"],
|
|
Text: ["tone", "muted", "size", "weight", "truncate"],
|
|
Heading: ["level"],
|
|
Button: ["variant", "tone", "size", "icon", "tooltip", "disabled", "onPress"],
|
|
IconButton: ["icon", "label", "tooltip", "variant", "tone", "size", "disabled", "onPress"],
|
|
Badge: ["tone"],
|
|
Card: ["title", "description"],
|
|
List: ["label"],
|
|
ListItem: ["title", "description", "icon", "current", "disabled", "onPress"],
|
|
Input: [
|
|
"label",
|
|
"labelHidden",
|
|
"value",
|
|
"defaultValue",
|
|
"placeholder",
|
|
"type",
|
|
"maxLength",
|
|
"invalid",
|
|
"disabled",
|
|
"onChange",
|
|
"onSubmit"
|
|
],
|
|
Switch: ["label", "checked", "defaultChecked", "disabled", "onChange"],
|
|
Select: [
|
|
"label",
|
|
"labelHidden",
|
|
"value",
|
|
"defaultValue",
|
|
"placeholder",
|
|
"options",
|
|
"disabled",
|
|
"onChange"
|
|
],
|
|
Separator: ["orientation"],
|
|
Progress: ["label", "labelHidden", "value"],
|
|
Spinner: ["label", "labelHidden"],
|
|
EmptyState: ["title", "description", "icon"],
|
|
Link: ["href", "onPress"],
|
|
Code: ["block"],
|
|
// Catalog 2 (Orca 1.4.247).
|
|
Textarea: [
|
|
"label",
|
|
"labelHidden",
|
|
"value",
|
|
"defaultValue",
|
|
"placeholder",
|
|
"rows",
|
|
"maxLength",
|
|
"invalid",
|
|
"disabled",
|
|
"onChange",
|
|
"onSubmit"
|
|
],
|
|
Checkbox: ["label", "description", "checked", "defaultChecked", "disabled", "onChange"],
|
|
RadioGroup: [
|
|
"label",
|
|
"labelHidden",
|
|
"value",
|
|
"defaultValue",
|
|
"options",
|
|
"orientation",
|
|
"disabled",
|
|
"onChange"
|
|
],
|
|
Slider: [
|
|
"label",
|
|
"labelHidden",
|
|
"value",
|
|
"defaultValue",
|
|
"min",
|
|
"max",
|
|
"step",
|
|
"showValue",
|
|
"disabled",
|
|
"onChange"
|
|
],
|
|
Field: ["label", "description", "error", "required"],
|
|
Tabs: ["label", "value", "defaultValue", "onChange"],
|
|
Tab: ["value", "label", "icon", "disabled"],
|
|
Collapsible: ["title", "description", "open", "defaultOpen", "disabled", "onChange"],
|
|
Toolbar: ["label", "gap"],
|
|
Grid: ["columns", "minItemWidth", "gap"],
|
|
Table: [
|
|
"label",
|
|
"columns",
|
|
"rows",
|
|
"sort",
|
|
"selectionMode",
|
|
"selected",
|
|
"defaultSelected",
|
|
"hasMore",
|
|
"loading",
|
|
"emptyText",
|
|
"maxHeight",
|
|
"onSortChange",
|
|
"onSelectionChange",
|
|
"onRowAction",
|
|
"onLoadMore"
|
|
],
|
|
Tree: [
|
|
"label",
|
|
"items",
|
|
"selected",
|
|
"defaultSelected",
|
|
"expanded",
|
|
"defaultExpanded",
|
|
"maxHeight",
|
|
"onSelect",
|
|
"onToggle",
|
|
"onAction"
|
|
],
|
|
Markdown: ["content"],
|
|
CodeView: ["code", "language", "label", "lineNumbers", "wrap", "maxHeight"],
|
|
Image: ["src", "alt", "size", "fit", "rounded"],
|
|
KeyValue: ["label", "items"],
|
|
Callout: ["tone", "title", "icon"],
|
|
Skeleton: ["shape", "lines", "label"],
|
|
// Catalog 3 (also Orca 1.4.247).
|
|
RelativeTime: ["at", "format", "tone", "muted"],
|
|
// Catalog 4 (Orca 1.4.248).
|
|
Box: [
|
|
"direction",
|
|
"gap",
|
|
"padding",
|
|
"paddingX",
|
|
"paddingY",
|
|
"margin",
|
|
"align",
|
|
"justify",
|
|
"wrap",
|
|
"grow",
|
|
"shrink",
|
|
"width",
|
|
"height",
|
|
"minWidth",
|
|
"minHeight",
|
|
"maxWidth",
|
|
"maxHeight",
|
|
"columns",
|
|
"scroll",
|
|
"border",
|
|
"radius",
|
|
"background"
|
|
]
|
|
};
|
|
var PLUGIN_DECLARATIVE_VALUE_CONTROLS = {
|
|
Input: { value: "value", change: "onChange" },
|
|
Textarea: { value: "value", change: "onChange" },
|
|
Switch: { value: "checked", change: "onChange" },
|
|
Checkbox: { value: "checked", change: "onChange" },
|
|
Select: { value: "value", change: "onChange" },
|
|
RadioGroup: { value: "value", change: "onChange" },
|
|
Slider: { value: "value", change: "onChange" },
|
|
Tabs: { value: "value", change: "onChange" },
|
|
Collapsible: { value: "open", change: "onChange" },
|
|
Table: { value: "selected", change: "onSelectionChange" },
|
|
Tree: { value: "selected", change: "onSelect" }
|
|
};
|
|
var VALUE_CONTROL_BY_TYPE = new Map(
|
|
Object.entries(PLUGIN_DECLARATIVE_VALUE_CONTROLS)
|
|
);
|
|
function pluginDeclarativeValueControl(type) {
|
|
return VALUE_CONTROL_BY_TYPE.get(type) ?? null;
|
|
}
|
|
var PLUGIN_DECLARATIVE_EDIT_PROP = "$edit";
|
|
function isReservedPluginDeclarativeEvent(name) {
|
|
return name.startsWith("ui.") || name.startsWith("editor.") || name === "ready" || name === "hello" || name === "ping" || name === "pong";
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-elements.ts
|
|
var ORCA_ELEMENT = /* @__PURE__ */ Symbol.for("orca.declarative.element");
|
|
var Fragment = /* @__PURE__ */ Symbol.for("orca.declarative.fragment");
|
|
function isOrcaElement(value) {
|
|
return typeof value === "object" && value !== null && "$$typeof" in value && value.$$typeof === ORCA_ELEMENT;
|
|
}
|
|
function createElement(type, props, key) {
|
|
return Object.freeze({
|
|
$$typeof: ORCA_ELEMENT,
|
|
type,
|
|
props,
|
|
key: key === void 0 || key === null ? void 0 : String(key)
|
|
});
|
|
}
|
|
function jsx(type, props, key) {
|
|
const { key: propKey, ...rest } = props ?? {};
|
|
return createElement(type, rest, key ?? propKey);
|
|
}
|
|
function h(type, props, ...children) {
|
|
const { key, ...rest } = props ?? {};
|
|
if (children.length > 0) {
|
|
rest.children = children.length === 1 ? children[0] : children;
|
|
}
|
|
return createElement(type, rest, key);
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-evaluate.ts
|
|
var MAX_EVALUATION_DEPTH = 512;
|
|
var MAX_PROP_DEPTH = 40;
|
|
var IGNORED_PROPS = /* @__PURE__ */ new Set([
|
|
"children",
|
|
"ref",
|
|
"key",
|
|
PLUGIN_DECLARATIVE_EDIT_PROP
|
|
]);
|
|
function isCallback(value) {
|
|
return typeof value === "function";
|
|
}
|
|
function describe(value) {
|
|
if (value === null || typeof value !== "object") {
|
|
return typeof value === "number" ? String(value) : typeof value;
|
|
}
|
|
return Object.getPrototypeOf(value)?.constructor?.name ?? "object";
|
|
}
|
|
function assertPlainJson(value, where, depth = 0) {
|
|
if (depth > MAX_PROP_DEPTH) {
|
|
throw new TypeError(`${where} is nested too deeply`);
|
|
}
|
|
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
return;
|
|
}
|
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
return;
|
|
}
|
|
if (Array.isArray(value)) {
|
|
value.forEach((item, index) => assertPlainJson(item, `${where}[${index}]`, depth + 1));
|
|
return;
|
|
}
|
|
if (typeof value === "object") {
|
|
const prototype = Object.getPrototypeOf(value);
|
|
if (prototype === Object.prototype || prototype === null) {
|
|
for (const [name, item] of Object.entries(value)) {
|
|
if (item !== void 0) {
|
|
assertPlainJson(item, `${where}.${name}`, depth + 1);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
throw new TypeError(`${where} must be plain JSON data, not ${describe(value)}`);
|
|
}
|
|
function plainJsonCopy(value, where) {
|
|
assertPlainJson(value, where);
|
|
return withoutUndefined(value);
|
|
}
|
|
function withoutUndefined(value) {
|
|
if (Array.isArray(value)) {
|
|
return value.map(withoutUndefined);
|
|
}
|
|
if (value === null || typeof value !== "object") {
|
|
return value;
|
|
}
|
|
const copy = {};
|
|
for (const [name, item] of Object.entries(value)) {
|
|
if (item !== void 0) {
|
|
copy[name] = withoutUndefined(item);
|
|
}
|
|
}
|
|
return copy;
|
|
}
|
|
function assertUniqueKeys(children, parent) {
|
|
const seen = /* @__PURE__ */ new Set();
|
|
for (const child of children) {
|
|
if (typeof child !== "string") {
|
|
if (seen.has(child.key)) {
|
|
throw new Error(`duplicate key "${child.key.slice(0, 64)}" among the children of ${parent}`);
|
|
}
|
|
seen.add(child.key);
|
|
}
|
|
}
|
|
}
|
|
function hashId(text) {
|
|
let a = 2166136261;
|
|
let b = 16777619;
|
|
for (let index = 0; index < text.length; index++) {
|
|
const code = text.charCodeAt(index);
|
|
a = Math.imul(a ^ code, 16777619);
|
|
b = Math.imul(b ^ code, 1540483477);
|
|
}
|
|
return `c${(a >>> 0).toString(36)}${(b >>> 0).toString(36)}`;
|
|
}
|
|
function evaluateOrcaTree(component, editsSeen = /* @__PURE__ */ new Map()) {
|
|
const callbacks = /* @__PURE__ */ new Map();
|
|
function callbackId(path, prop) {
|
|
const base = hashId(`${path}|${prop}`);
|
|
let id = base;
|
|
for (let attempt = 1; callbacks.has(id); attempt++) {
|
|
id = `${base}.${attempt}`;
|
|
}
|
|
return id;
|
|
}
|
|
function hostNode(type, props, key, parentPath, depth) {
|
|
if (!/^[A-Z]/.test(type)) {
|
|
throw new TypeError(
|
|
`<${type.slice(0, 64)}> is not an Orca component; panels never render DOM`
|
|
);
|
|
}
|
|
const path = `${parentPath}/${key}`;
|
|
const wireProps = {};
|
|
for (const [name, value] of Object.entries(props)) {
|
|
if (IGNORED_PROPS.has(name) || value === void 0) {
|
|
continue;
|
|
}
|
|
if (isCallback(value)) {
|
|
const id = callbackId(path, name);
|
|
callbacks.set(id, value);
|
|
wireProps[name] = { $callback: id };
|
|
} else {
|
|
wireProps[name] = plainJsonCopy(value, `<${type}> prop "${name}"`);
|
|
}
|
|
}
|
|
const control = pluginDeclarativeValueControl(type);
|
|
const onChange = control ? wireProps[control.change] : void 0;
|
|
if (typeof onChange === "object" && onChange !== null) {
|
|
const id = "$callback" in onChange ? onChange.$callback : void 0;
|
|
wireProps[PLUGIN_DECLARATIVE_EDIT_PROP] = typeof id === "string" ? editsSeen.get(id) ?? 0 : 0;
|
|
}
|
|
const children = [];
|
|
const list = Array.isArray(props.children) ? props.children : [props.children];
|
|
list.forEach((child, index) => emit(child, `#${index}`, "", children, path, depth + 1));
|
|
assertUniqueKeys(children, `<${type}>`);
|
|
return { type, key, props: wireProps, children };
|
|
}
|
|
function emit(value, slot, scope, out, parentPath, depth) {
|
|
if (depth > MAX_EVALUATION_DEPTH) {
|
|
throw new Error(`component tree is deeper than ${MAX_EVALUATION_DEPTH}`);
|
|
}
|
|
if (value === null || value === void 0 || typeof value === "boolean") {
|
|
return;
|
|
}
|
|
if (typeof value === "string" || typeof value === "number") {
|
|
out.push(String(value));
|
|
return;
|
|
}
|
|
if (Array.isArray(value)) {
|
|
value.forEach(
|
|
(item, index) => emit(item, `${slot}.${index}`, `${slot}.`, out, parentPath, depth + 1)
|
|
);
|
|
return;
|
|
}
|
|
if (!isOrcaElement(value)) {
|
|
throw new TypeError(`a ${describe(value)} is not a valid child; render a component or text`);
|
|
}
|
|
const { type, props } = value;
|
|
const key = value.key === void 0 ? slot : `${scope}${value.key}`;
|
|
if (type === Fragment) {
|
|
emit(props.children, key, `${key}.`, out, parentPath, depth + 1);
|
|
} else if (typeof type === "function") {
|
|
emit(type(props), key, `${key}:`, out, parentPath, depth + 1);
|
|
} else if (typeof type === "string") {
|
|
out.push(hostNode(type, props, key, parentPath, depth));
|
|
} else {
|
|
throw new TypeError('invalid element type; import components from "orca"');
|
|
}
|
|
}
|
|
const root = [];
|
|
emit(component({}), "#0", "", root, "", 0);
|
|
const nodes = root.map(
|
|
(child, index) => typeof child === "string" ? { type: "Text", key: `#text${index}`, props: {}, children: [child] } : child
|
|
);
|
|
assertUniqueKeys(nodes, "the panel");
|
|
const tree = nodes.length <= 1 ? nodes[0] ?? null : { type: "Stack", key: "#root", props: {}, children: nodes };
|
|
return { tree, callbacks };
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-memory-retention.ts
|
|
var RETAIN_DEBOUNCE_MS = 50;
|
|
var RETAIN_BATCH = 64;
|
|
function createMemoryRetention(post) {
|
|
const pending = /* @__PURE__ */ new Set();
|
|
let timer = null;
|
|
let started = false;
|
|
function flush() {
|
|
if (timer !== null) {
|
|
clearTimeout(timer);
|
|
timer = null;
|
|
}
|
|
const entries = [];
|
|
for (const target of pending) {
|
|
const entry = memoryEntry(target);
|
|
if (!entry) {
|
|
continue;
|
|
}
|
|
try {
|
|
assertPlainJson(entry.value, `atom "${entry.key}"`);
|
|
entries.push(entry);
|
|
} catch {
|
|
entries.push({ key: entry.key, version: entry.version });
|
|
}
|
|
}
|
|
pending.clear();
|
|
for (let start = 0; start < entries.length; start += RETAIN_BATCH) {
|
|
post(entries.slice(start, start + RETAIN_BATCH));
|
|
}
|
|
}
|
|
function add(target) {
|
|
pending.add(target);
|
|
timer ??= setTimeout(flush, RETAIN_DEBOUNCE_MS);
|
|
}
|
|
return {
|
|
start() {
|
|
if (!started) {
|
|
started = true;
|
|
watchMemoryAtoms(add);
|
|
}
|
|
},
|
|
flush,
|
|
stop() {
|
|
if (started) {
|
|
flush();
|
|
watchMemoryAtoms(null);
|
|
started = false;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-host-messages.ts
|
|
function readHostMessage(payload) {
|
|
if (typeof payload !== "object" || payload === null) {
|
|
return {};
|
|
}
|
|
const panel = "panel" in payload ? payload.panel : void 0;
|
|
return {
|
|
document: "document" in payload ? payload.document : void 0,
|
|
revision: "revision" in payload ? payload.revision : void 0,
|
|
callback: "callback" in payload ? payload.callback : void 0,
|
|
args: "args" in payload ? payload.args : void 0,
|
|
edit: "edit" in payload ? payload.edit : void 0,
|
|
accepted: "accepted" in payload ? payload.accepted : void 0,
|
|
retryAfterMs: "retryAfterMs" in payload ? payload.retryAfterMs : void 0,
|
|
colorScheme: "colorScheme" in payload ? payload.colorScheme : void 0,
|
|
visible: "visible" in payload ? payload.visible : void 0,
|
|
panelId: typeof panel === "object" && panel !== null && "id" in panel ? panel.id : void 0,
|
|
plugin: "plugin" in payload ? payload.plugin : void 0,
|
|
workspace: "workspace" in payload ? payload.workspace : void 0,
|
|
memory: "memory" in payload ? payload.memory : void 0,
|
|
retain: "retain" in payload ? payload.retain : void 0
|
|
};
|
|
}
|
|
function readStoredValues(raw) {
|
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
return null;
|
|
}
|
|
const values = /* @__PURE__ */ new Map();
|
|
for (const [key, entry] of Object.entries(raw)) {
|
|
if (typeof entry !== "object" || entry === null) {
|
|
continue;
|
|
}
|
|
const version = "version" in entry ? entry.version : void 0;
|
|
if (typeof version === "number" && "value" in entry) {
|
|
values.set(key, { version, value: entry.value });
|
|
}
|
|
}
|
|
return values;
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-act-snapshot.ts
|
|
function actSnapshotSubscriptions(on2, document2) {
|
|
let restore = null;
|
|
const ours = (payload) => readHostMessage(payload).document === document2;
|
|
return [
|
|
on2(PLUGIN_DECLARATIVE_MESSAGES.snapshot, (payload) => {
|
|
if (ours(payload)) {
|
|
restore = captureAtomValues();
|
|
}
|
|
}),
|
|
on2(PLUGIN_DECLARATIVE_MESSAGES.restore, (payload) => {
|
|
if (ours(payload) && restore) {
|
|
const pending = restore;
|
|
restore = null;
|
|
pending();
|
|
}
|
|
})
|
|
];
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-renderer-support.ts
|
|
var MIN_RENDER_INTERVAL_MS = 16;
|
|
var MAX_RETAINED_RENDERS = 32;
|
|
var HYDRATION_TIMEOUT_MS = 3e3;
|
|
function errorMessage(error) {
|
|
return describePluginError(error).slice(0, 4096);
|
|
}
|
|
function defaultSchedule(flush, delayMs) {
|
|
if (delayMs > 0) {
|
|
setTimeout(flush, delayMs);
|
|
} else {
|
|
queueMicrotask(flush);
|
|
}
|
|
}
|
|
function newDocumentId(now) {
|
|
return `${now.toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
}
|
|
function isCallable(value) {
|
|
return typeof value === "function";
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-panel-context.ts
|
|
function createPanelContext(onRetain) {
|
|
const visibility = hostAtom(true);
|
|
const lifetime = new AbortController();
|
|
const context = {
|
|
subscriptions: [],
|
|
signal: lifetime.signal,
|
|
visible: visibility.atom,
|
|
panel: { id: "" }
|
|
};
|
|
let hydratedNow = false;
|
|
let resolve = () => void 0;
|
|
const hydrated = new Promise((done) => {
|
|
resolve = () => {
|
|
hydratedNow = true;
|
|
done();
|
|
};
|
|
});
|
|
function setVisibility(payload) {
|
|
const { visible } = readHostMessage(payload);
|
|
if (typeof visible === "boolean") {
|
|
visibility.set(visible);
|
|
}
|
|
}
|
|
return {
|
|
context,
|
|
hydrated,
|
|
isHydrated: () => hydratedNow,
|
|
markHydrated: () => resolve(),
|
|
setVisibility,
|
|
/** `ui.hydrate`: on boot, and again with the new workspace's values after a switch. */
|
|
hydrate(payload) {
|
|
const message = readHostMessage(payload);
|
|
if (typeof message.panelId === "string") {
|
|
context.panel = { id: message.panelId };
|
|
}
|
|
setVisibility(payload);
|
|
for (const scope of ["plugin", "workspace", "memory"]) {
|
|
const values = readStoredValues(message[scope]);
|
|
if (values) {
|
|
hydrateAtoms(scope, values);
|
|
}
|
|
}
|
|
if (message.retain === true) {
|
|
onRetain?.();
|
|
}
|
|
resolve();
|
|
},
|
|
abort: () => lifetime.abort()
|
|
};
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-persist-queue.ts
|
|
var PERSIST_DEBOUNCE_MS = 400;
|
|
function createPersistQueue(options) {
|
|
const pending = /* @__PURE__ */ new Set();
|
|
let timer = null;
|
|
function flush() {
|
|
if (timer !== null) {
|
|
clearTimeout(timer);
|
|
timer = null;
|
|
}
|
|
const entries = [];
|
|
for (const target of pending) {
|
|
const entry = persistedEntry(target);
|
|
if (!entry) {
|
|
continue;
|
|
}
|
|
try {
|
|
assertPlainJson(entry.value, `persisted atom "${entry.key}"`);
|
|
entries.push(entry);
|
|
} catch (error) {
|
|
options.report(error);
|
|
}
|
|
}
|
|
pending.clear();
|
|
if (entries.length > 0) {
|
|
options.post(entries);
|
|
}
|
|
}
|
|
return {
|
|
add(target) {
|
|
pending.add(target);
|
|
timer ??= setTimeout(flush, PERSIST_DEBOUNCE_MS);
|
|
},
|
|
flush
|
|
};
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-storage-sync.ts
|
|
function createDeclarativeStorageSync(options) {
|
|
const storage2 = options.bridge?.storage;
|
|
const loads = /* @__PURE__ */ new Set();
|
|
const writtenSince = /* @__PURE__ */ new Map();
|
|
let generation = 0;
|
|
function load(key) {
|
|
if (!storage2) {
|
|
return;
|
|
}
|
|
const startedAt = generation;
|
|
const task = storage2.get(key).then((value) => {
|
|
if ((writtenSince.get(key) ?? -1) < startedAt) {
|
|
applyStorageValue(key, value ?? void 0);
|
|
}
|
|
}).catch(options.report).finally(() => loads.delete(task));
|
|
loads.add(task);
|
|
}
|
|
watchStorageAtoms((key) => {
|
|
generation += 1;
|
|
load(key);
|
|
});
|
|
const subscription = storage2?.onDidChange(({ key }) => {
|
|
generation += 1;
|
|
load(key);
|
|
});
|
|
return {
|
|
write(entries) {
|
|
const rest = [];
|
|
for (const entry of entries) {
|
|
if (entry.scope !== "storage") {
|
|
rest.push(entry);
|
|
continue;
|
|
}
|
|
generation += 1;
|
|
writtenSince.set(entry.key, generation);
|
|
void storage2?.set(entry.key, entry.value).catch(options.report);
|
|
}
|
|
return rest;
|
|
},
|
|
loaded: async () => {
|
|
await Promise.all(loads);
|
|
},
|
|
dispose() {
|
|
watchStorageAtoms(null);
|
|
subscription?.dispose();
|
|
}
|
|
};
|
|
}
|
|
function createDeclarativePersistence(options) {
|
|
const storageSync = createDeclarativeStorageSync(options);
|
|
const queue = createPersistQueue({
|
|
report: options.report,
|
|
post: (all) => {
|
|
const entries = storageSync.write(all);
|
|
if (entries.length > 0) {
|
|
options.post(entries);
|
|
}
|
|
}
|
|
});
|
|
watchPersistedAtoms(queue.add);
|
|
return {
|
|
flush: queue.flush,
|
|
loaded: storageSync.loaded,
|
|
dispose() {
|
|
watchPersistedAtoms(null);
|
|
storageSync.dispose();
|
|
}
|
|
};
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-renderer.ts
|
|
function createOrcaDeclarativeRuntime(options) {
|
|
const { post } = options;
|
|
const bridge = options.bridge ?? null;
|
|
const schedule = options.schedule ?? defaultSchedule;
|
|
const now = options.now ?? (() => Date.now());
|
|
const document2 = newDocumentId(now());
|
|
const theme = hostAtom({ colorScheme: options.colorScheme ?? "dark" });
|
|
const retention = createMemoryRetention(
|
|
(entries) => post(PLUGIN_DECLARATIVE_MESSAGES.retain, { document: document2, entries })
|
|
);
|
|
const panel = createPanelContext(retention.start);
|
|
const { context } = panel;
|
|
let component = null;
|
|
let revision = 0;
|
|
let lastRenderAt = Number.NEGATIVE_INFINITY;
|
|
let renders = /* @__PURE__ */ new Map();
|
|
let accepted = null;
|
|
const editsSeen = /* @__PURE__ */ new Map();
|
|
let subscriptions = /* @__PURE__ */ new Map();
|
|
let scheduled = false;
|
|
let setupCleanup = null;
|
|
let disposed = false;
|
|
function invalidate(delayMs = 0) {
|
|
if (scheduled || !component) {
|
|
return;
|
|
}
|
|
scheduled = true;
|
|
const wait = Math.max(delayMs, lastRenderAt + MIN_RENDER_INTERVAL_MS - now());
|
|
schedule(() => {
|
|
if (scheduled) {
|
|
render();
|
|
}
|
|
}, wait);
|
|
}
|
|
function resubscribe(reads) {
|
|
const next = /* @__PURE__ */ new Map();
|
|
for (const target of reads) {
|
|
next.set(target, subscriptions.get(target) ?? subscribeAtom(target, () => invalidate()));
|
|
}
|
|
for (const [target, unsubscribe] of subscriptions) {
|
|
if (!next.has(target)) {
|
|
unsubscribe();
|
|
}
|
|
}
|
|
subscriptions = next;
|
|
}
|
|
function report(error) {
|
|
console.error(describePluginErrorForLog(error));
|
|
const stack = error instanceof Error && error.stack ? error.stack.slice(0, 8192) : void 0;
|
|
post(PLUGIN_DECLARATIVE_MESSAGES.error, {
|
|
document: document2,
|
|
revision,
|
|
message: errorMessage(error),
|
|
...stack ? { stack } : {}
|
|
});
|
|
}
|
|
function retain(at, callbacks) {
|
|
renders.set(at, callbacks);
|
|
for (const kept of renders.keys()) {
|
|
if (renders.size <= MAX_RETAINED_RENDERS) {
|
|
break;
|
|
}
|
|
if (kept !== accepted) {
|
|
renders.delete(kept);
|
|
}
|
|
}
|
|
}
|
|
function render() {
|
|
scheduled = false;
|
|
if (!component || disposed) {
|
|
return;
|
|
}
|
|
lastRenderAt = now();
|
|
const reads = /* @__PURE__ */ new Set();
|
|
const mounted = component;
|
|
let evaluation;
|
|
try {
|
|
evaluation = trackAtomReads(reads, () => evaluateOrcaTree(mounted, editsSeen));
|
|
} catch (error) {
|
|
resubscribe(/* @__PURE__ */ new Set([...subscriptions.keys(), ...reads]));
|
|
report(error);
|
|
return;
|
|
}
|
|
revision += 1;
|
|
retain(revision, evaluation.callbacks);
|
|
resubscribe(reads);
|
|
post(PLUGIN_DECLARATIVE_MESSAGES.render, { document: document2, revision, tree: evaluation.tree });
|
|
}
|
|
function dispatchEvent(payload) {
|
|
const message = readHostMessage(payload);
|
|
const { callback, args, edit } = message;
|
|
if (message.document !== document2 || typeof callback !== "string") {
|
|
return false;
|
|
}
|
|
const handler = typeof message.revision === "number" ? renders.get(message.revision)?.get(callback) : void 0;
|
|
if (!handler) {
|
|
return false;
|
|
}
|
|
if (typeof edit === "number" && Number.isSafeInteger(edit)) {
|
|
editsSeen.set(callback, Math.max(edit, editsSeen.get(callback) ?? 0));
|
|
invalidate();
|
|
}
|
|
try {
|
|
const result = handler(...Array.isArray(args) ? args : []);
|
|
if (result instanceof Promise) {
|
|
result.catch(report);
|
|
}
|
|
} catch (error) {
|
|
report(error);
|
|
}
|
|
return true;
|
|
}
|
|
function acknowledge(payload) {
|
|
const message = readHostMessage(payload);
|
|
const acked = message.revision;
|
|
if (message.document !== document2 || typeof acked !== "number" || !renders.has(acked)) {
|
|
return;
|
|
}
|
|
if (message.accepted === true) {
|
|
accepted = acked;
|
|
for (const kept of renders.keys()) {
|
|
if (kept < acked) {
|
|
renders.delete(kept);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
renders.delete(acked);
|
|
const retryAfterMs = message.retryAfterMs;
|
|
if (typeof retryAfterMs === "number" && acked === revision) {
|
|
invalidate(retryAfterMs);
|
|
}
|
|
}
|
|
const persistence = createDeclarativePersistence({
|
|
bridge,
|
|
report,
|
|
post: (entries) => post(PLUGIN_DECLARATIVE_MESSAGES.persist, { document: document2, entries })
|
|
});
|
|
const flushPersist = persistence.flush;
|
|
const bridgeSubscriptions = bridge?.on ? [
|
|
bridge.on(PLUGIN_DECLARATIVE_MESSAGES.event, dispatchEvent),
|
|
bridge.on(PLUGIN_DECLARATIVE_MESSAGES.ack, acknowledge),
|
|
bridge.on(PLUGIN_DECLARATIVE_MESSAGES.hydrate, panel.hydrate),
|
|
bridge.on(PLUGIN_DECLARATIVE_MESSAGES.visibility, panel.setVisibility),
|
|
...actSnapshotSubscriptions(bridge.on.bind(bridge), document2),
|
|
bridge.on("theme", (payload) => {
|
|
const { colorScheme } = readHostMessage(payload);
|
|
if ((colorScheme === "light" || colorScheme === "dark") && read(theme.atom).colorScheme !== colorScheme) {
|
|
theme.set({ colorScheme });
|
|
}
|
|
})
|
|
] : [];
|
|
if (options.awaitHydration) {
|
|
post(PLUGIN_DECLARATIVE_MESSAGES.ready, { document: document2 });
|
|
const fallback = setTimeout(panel.markHydrated, HYDRATION_TIMEOUT_MS);
|
|
void panel.hydrated.then(() => clearTimeout(fallback));
|
|
} else {
|
|
panel.markHydrated();
|
|
}
|
|
function unsubscribeAll() {
|
|
for (const unsubscribe of subscriptions.values()) {
|
|
unsubscribe();
|
|
}
|
|
subscriptions = /* @__PURE__ */ new Map();
|
|
}
|
|
const runtime2 = {
|
|
document: document2,
|
|
async boot(module) {
|
|
await panel.hydrated;
|
|
await persistence.loaded();
|
|
if (isCallable(module.setup)) {
|
|
const cleanup = module.setup(context);
|
|
setupCleanup = isCallable(cleanup) ? () => void cleanup() : null;
|
|
}
|
|
const panelComponent = module.default;
|
|
if (isCallable(panelComponent)) {
|
|
runtime2.mount((props) => panelComponent(props));
|
|
} else if (!component) {
|
|
const kind = module.default === null ? "null" : typeof module.default;
|
|
throw new Error(
|
|
`The panel module must export a component function as its default export (got ${kind})`
|
|
);
|
|
}
|
|
},
|
|
mount(next) {
|
|
component = next;
|
|
if (panel.isHydrated()) {
|
|
render();
|
|
return;
|
|
}
|
|
void panel.hydrated.then(() => {
|
|
if (component === next) {
|
|
render();
|
|
}
|
|
});
|
|
},
|
|
unmount() {
|
|
component = null;
|
|
scheduled = false;
|
|
unsubscribeAll();
|
|
renders = /* @__PURE__ */ new Map();
|
|
accepted = null;
|
|
revision += 1;
|
|
post(PLUGIN_DECLARATIVE_MESSAGES.render, { document: document2, revision, tree: null });
|
|
},
|
|
mounted: () => component !== null,
|
|
dispatchEvent,
|
|
acknowledge,
|
|
hydrate: panel.hydrate,
|
|
rerender: () => render(),
|
|
flush() {
|
|
if (scheduled) {
|
|
render();
|
|
}
|
|
flushPersist();
|
|
retention.flush();
|
|
},
|
|
report,
|
|
themeAtom: theme.atom,
|
|
bridge,
|
|
dispose() {
|
|
if (disposed) {
|
|
return;
|
|
}
|
|
flushPersist();
|
|
retention.stop();
|
|
disposed = true;
|
|
component = null;
|
|
scheduled = false;
|
|
unsubscribeAll();
|
|
persistence.dispose();
|
|
for (const subscription of [...bridgeSubscriptions, ...context.subscriptions]) {
|
|
try {
|
|
subscription.dispose();
|
|
} catch (error) {
|
|
report(error);
|
|
}
|
|
}
|
|
try {
|
|
setupCleanup?.();
|
|
} catch (error) {
|
|
report(error);
|
|
}
|
|
panel.abort();
|
|
}
|
|
};
|
|
return runtime2;
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-sdk.ts
|
|
function hostError(code, message) {
|
|
return Object.assign(new Error(message), { code, retryable: code === "timeout" });
|
|
}
|
|
function unavailable(what) {
|
|
return Promise.reject(hostError("unavailable", `${what} is not available in this Orca`));
|
|
}
|
|
function isComponentTag(name) {
|
|
return Object.hasOwn(PLUGIN_DECLARATIVE_COMPONENT_PROPS, name);
|
|
}
|
|
function createOrcaSdk(source) {
|
|
const host2 = Object.freeze({
|
|
call(method, params, options = {}) {
|
|
const bridgeHost = source.bridge()?.host;
|
|
if (!bridgeHost) {
|
|
return unavailable("host.call");
|
|
}
|
|
const { signal, timeoutMs } = options;
|
|
if (timeoutMs === void 0) {
|
|
return bridgeHost.call(method, params, signal ? { signal } : void 0);
|
|
}
|
|
const controller = new AbortController();
|
|
const forward = () => controller.abort();
|
|
if (signal?.aborted) {
|
|
controller.abort();
|
|
}
|
|
signal?.addEventListener("abort", forward, { once: true });
|
|
let timedOut = false;
|
|
const timer = setTimeout(() => {
|
|
timedOut = true;
|
|
controller.abort();
|
|
}, timeoutMs);
|
|
return bridgeHost.call(method, params, { signal: controller.signal }).catch((error) => {
|
|
throw timedOut ? hostError("timeout", `${method} took longer than ${timeoutMs} ms`) : error;
|
|
}).finally(() => {
|
|
clearTimeout(timer);
|
|
signal?.removeEventListener("abort", forward);
|
|
});
|
|
},
|
|
has(method) {
|
|
return source.bridge()?.host?.has?.(method) === true;
|
|
}
|
|
});
|
|
const api2 = Object.freeze({
|
|
get major() {
|
|
return source.bridge()?.api?.major ?? 1;
|
|
},
|
|
get minor() {
|
|
return source.bridge()?.api?.minor ?? 0;
|
|
}
|
|
});
|
|
const env2 = Object.freeze({
|
|
/** Orca's UI language (BCP-47); the guest's `navigator.language` follows the OS instead. */
|
|
get locale() {
|
|
return source.bridge()?.locale ?? "en";
|
|
},
|
|
/** Reading it while rendering re-renders on light/dark switches. */
|
|
get theme() {
|
|
return source.theme();
|
|
},
|
|
client: "desktop"
|
|
});
|
|
const shell2 = Object.freeze({
|
|
openExternal(url) {
|
|
return host2.call("shell.openExternal", { url: String(url) });
|
|
}
|
|
});
|
|
const files2 = Object.freeze({
|
|
saveAs(options) {
|
|
const { data } = options;
|
|
const bytes = ArrayBuffer.isView(data) ? data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) : data;
|
|
return host2.call("files.saveAs", { ...options, data: bytes });
|
|
}
|
|
});
|
|
function on2(event, handler) {
|
|
if (typeof event !== "string" || isReservedPluginDeclarativeEvent(event)) {
|
|
throw new TypeError(`"${String(event)}" is not an event plugins can subscribe to`);
|
|
}
|
|
return source.bridge()?.on?.(event, handler) ?? { dispose: () => void 0 };
|
|
}
|
|
const ui2 = Object.freeze({
|
|
/** Grows with the component catalog. */
|
|
catalog: PLUGIN_DECLARATIVE_CATALOG_VERSION,
|
|
/** Whether this Orca draws `component`, and accepts `prop` on it when given. */
|
|
has(component, prop) {
|
|
if (!isComponentTag(component)) {
|
|
return false;
|
|
}
|
|
const props = PLUGIN_DECLARATIVE_COMPONENT_PROPS[component];
|
|
return prop === void 0 || props.includes(prop);
|
|
}
|
|
});
|
|
return { host: host2, api: api2, env: env2, shell: shell2, files: files2, on: on2, ui: ui2 };
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/declarative-data-sdk.ts
|
|
function unavailable2(what) {
|
|
return Promise.reject(
|
|
Object.assign(new Error(`${what} is not available in this Orca`), {
|
|
code: "unavailable",
|
|
retryable: false
|
|
})
|
|
);
|
|
}
|
|
var NOTHING = { dispose: () => void 0 };
|
|
function createOrcaDataSdk(bridge) {
|
|
const plugin2 = Object.freeze({
|
|
request(name, input, options) {
|
|
return bridge()?.plugin?.request(name, input, options) ?? unavailable2("plugin.request");
|
|
},
|
|
on(event, handler) {
|
|
return bridge()?.plugin?.on(event, handler) ?? NOTHING;
|
|
}
|
|
});
|
|
const storage2 = Object.freeze({
|
|
get: (key) => bridge()?.storage?.get(key) ?? unavailable2("storage"),
|
|
set: (key, value) => bridge()?.storage?.set(key, value) ?? unavailable2("storage"),
|
|
delete: (key) => bridge()?.storage?.delete(key) ?? unavailable2("storage"),
|
|
keys: () => bridge()?.storage?.keys() ?? unavailable2("storage"),
|
|
onDidChange: (handler) => bridge()?.storage?.onDidChange(handler) ?? NOTHING
|
|
});
|
|
const settings2 = Object.freeze({
|
|
get: (key) => bridge()?.settings?.get(key) ?? unavailable2("settings"),
|
|
set: (key, value) => bridge()?.settings?.set(key, value) ?? unavailable2("settings"),
|
|
onDidChange: (handler) => bridge()?.settings?.onDidChange(handler) ?? NOTHING
|
|
});
|
|
const secrets2 = Object.freeze({
|
|
get: (key) => bridge()?.secrets?.get(key) ?? unavailable2("secrets"),
|
|
set: (key, value) => bridge()?.secrets?.set(key, value) ?? unavailable2("secrets"),
|
|
delete: (key) => bridge()?.secrets?.delete(key) ?? unavailable2("secrets")
|
|
});
|
|
return { plugin: plugin2, storage: storage2, settings: settings2, secrets: secrets2 };
|
|
}
|
|
|
|
// src/shared/plugins/declarative/runtime/orca-module.ts
|
|
var runtime = null;
|
|
var pendingMount = null;
|
|
function isOrcaBridge(value) {
|
|
return typeof value === "object" && value !== null && "on" in value && typeof value.on === "function";
|
|
}
|
|
function globalBridge() {
|
|
const scope = globalThis;
|
|
const candidate = "orca" in scope ? scope.orca : void 0;
|
|
return isOrcaBridge(candidate) ? candidate : null;
|
|
}
|
|
function documentColorScheme() {
|
|
const root = typeof document === "undefined" ? null : document.documentElement;
|
|
if (!root) {
|
|
return void 0;
|
|
}
|
|
return root.classList.contains("light") ? "light" : "dark";
|
|
}
|
|
function installOrcaDeclarativeRuntime(options) {
|
|
runtime?.dispose();
|
|
resetAtoms();
|
|
installPluginSourceMapHook();
|
|
const installed = createOrcaDeclarativeRuntime({
|
|
bridge: globalBridge(),
|
|
colorScheme: documentColorScheme(),
|
|
...options
|
|
});
|
|
runtime = installed;
|
|
if (typeof addEventListener === "function") {
|
|
addEventListener("pagehide", () => installed.dispose(), { once: true });
|
|
}
|
|
const queued = pendingMount;
|
|
pendingMount = null;
|
|
if (queued) {
|
|
installed.mount(queued);
|
|
}
|
|
return installed;
|
|
}
|
|
function mount(component) {
|
|
if (runtime) {
|
|
runtime.mount(component);
|
|
} else {
|
|
pendingMount = component;
|
|
}
|
|
}
|
|
var sdk = createOrcaSdk({
|
|
bridge: () => runtime?.bridge ?? globalBridge(),
|
|
theme: () => runtime ? read(runtime.themeAtom) : { colorScheme: documentColorScheme() ?? "dark" }
|
|
});
|
|
var { host, api, env, shell, files, on, ui } = sdk;
|
|
var { plugin, storage, settings, secrets } = createOrcaDataSdk(
|
|
() => runtime?.bridge ?? globalBridge()
|
|
);
|
|
var Stack = "Stack";
|
|
var Row = "Row";
|
|
var Text = "Text";
|
|
var Heading = "Heading";
|
|
var Button = "Button";
|
|
var IconButton = "IconButton";
|
|
var Badge = "Badge";
|
|
var Card = "Card";
|
|
var List = "List";
|
|
var ListItem = "ListItem";
|
|
var Input = "Input";
|
|
var Switch = "Switch";
|
|
var Select = "Select";
|
|
var Separator = "Separator";
|
|
var Progress = "Progress";
|
|
var Spinner = "Spinner";
|
|
var EmptyState = "EmptyState";
|
|
var Link = "Link";
|
|
var Code = "Code";
|
|
var Textarea = "Textarea";
|
|
var Checkbox = "Checkbox";
|
|
var RadioGroup = "RadioGroup";
|
|
var Slider = "Slider";
|
|
var Field = "Field";
|
|
var Tabs = "Tabs";
|
|
var Tab = "Tab";
|
|
var Collapsible = "Collapsible";
|
|
var Toolbar = "Toolbar";
|
|
var Grid = "Grid";
|
|
var Table = "Table";
|
|
var Tree = "Tree";
|
|
var Markdown = "Markdown";
|
|
var CodeView = "CodeView";
|
|
var Image = "Image";
|
|
var KeyValue = "KeyValue";
|
|
var Callout = "Callout";
|
|
var Skeleton = "Skeleton";
|
|
var RelativeTime = "RelativeTime";
|
|
var Box = "Box";
|
|
export {
|
|
Badge,
|
|
Box,
|
|
Button,
|
|
Callout,
|
|
Card,
|
|
Checkbox,
|
|
Code,
|
|
CodeView,
|
|
Collapsible,
|
|
EmptyState,
|
|
Field,
|
|
Fragment,
|
|
Grid,
|
|
Heading,
|
|
IconButton,
|
|
Image,
|
|
Input,
|
|
KeyValue,
|
|
Link,
|
|
List,
|
|
ListItem,
|
|
Markdown,
|
|
Progress,
|
|
RadioGroup,
|
|
RelativeTime,
|
|
Row,
|
|
Select,
|
|
Separator,
|
|
Skeleton,
|
|
Slider,
|
|
Spinner,
|
|
Stack,
|
|
Switch,
|
|
Tab,
|
|
Table,
|
|
Tabs,
|
|
Text,
|
|
Textarea,
|
|
Toolbar,
|
|
Tree,
|
|
api,
|
|
atom,
|
|
derived,
|
|
env,
|
|
files,
|
|
h,
|
|
host,
|
|
installOrcaDeclarativeRuntime,
|
|
jsx,
|
|
jsx as jsxDEV,
|
|
jsx as jsxs,
|
|
mount,
|
|
on,
|
|
plugin,
|
|
read,
|
|
secrets,
|
|
set,
|
|
settings,
|
|
shell,
|
|
storage,
|
|
ui,
|
|
update
|
|
};
|