@ai-matrx/canvas 0.8.5 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +5 -4
- package/dist/index.cjs +63 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +63 -5
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +197 -65
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +23 -7
- package/dist/react.d.ts +23 -7
- package/dist/react.js +197 -65
- package/dist/react.js.map +1 -1
- package/dist/styles.css +15 -10
- package/dist/{toggleKind-BoFnqhzq.d.cts → toggleKind-CI2nBjum.d.cts} +47 -2
- package/dist/{toggleKind-BoFnqhzq.d.ts → toggleKind-CI2nBjum.d.ts} +47 -2
- package/package.json +1 -1
package/dist/react.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ReactNode, CSSProperties, ButtonHTMLAttributes, HTMLAttributes, ReactElement } from 'react';
|
|
3
|
-
import { b as CanvasJson, i as CanvasItem, d as CanvasPaneId, h as CanvasController, a as CanvasWindowId,
|
|
3
|
+
import { b as CanvasJson, i as CanvasItem, d as CanvasPaneId, h as CanvasController, a as CanvasWindowId, Q as CanvasPresentation, V as CanvasSessionEvent, T as CanvasSessionAttachReason, U as CanvasSessionDetachReason, P as CanvasPreferredTarget, C as CanvasItemId, S as CanvasRect, e as CanvasStoreBinding, O as CanvasPersistencePort, F as CanvasErrorSink, I as CanvasLayoutRules, a0 as ToggleKindInput, H as CanvasKindVisibility, f as CanvasState } from './toggleKind-CI2nBjum.cjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* THE canvas kind registry. A kind is one sort of thing the canvas can show —
|
|
@@ -392,13 +392,29 @@ declare function CanvasPaneView({ paneId }: {
|
|
|
392
392
|
paneId: CanvasPaneId;
|
|
393
393
|
}): react.JSX.Element | null;
|
|
394
394
|
/**
|
|
395
|
-
* Every tab's floor (px): icon + ~10 letters + "…" — still readable. Tabs
|
|
396
|
-
* evenly from 12rem down to this;
|
|
397
|
-
* --mxc-tab-min.
|
|
395
|
+
* Every tab's floor (px): icon + ~10 letters + "…" — still readable. Tabs share
|
|
396
|
+
* the strip evenly from 12rem down to this; a tab that would go under it is not
|
|
397
|
+
* drawn (it waits in "All tabs"). Mirrored to CSS as --mxc-tab-min.
|
|
398
398
|
*/
|
|
399
399
|
declare const CANVAS_TAB_MIN_WIDTH = 120;
|
|
400
|
-
/**
|
|
401
|
-
declare
|
|
400
|
+
/** Room kept for "All tabs" (its chevron and the count of tabs out of the strip) while it shows. */
|
|
401
|
+
declare const CANVAS_TAB_LIST_ROOM = 44;
|
|
402
|
+
/** The run of strip entries drawn whole: `start` (inclusive) to `end` (exclusive). */
|
|
403
|
+
interface CanvasTabWindow {
|
|
404
|
+
readonly start: number;
|
|
405
|
+
readonly end: number;
|
|
406
|
+
/** True when even the current entry alone is wider than the room: it is drawn under its floor (ellipsis). */
|
|
407
|
+
readonly squeezed: boolean;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Which strip entries are drawn: every one when they fit at their floor; else
|
|
411
|
+
* the run that fits WHOLE beside "All tabs" and holds the current entry, moved
|
|
412
|
+
* as little as possible from `previousStart` (so switching tabs does not
|
|
413
|
+
* reshuffle the strip). A tab is never drawn sliced at an edge — the rest wait
|
|
414
|
+
* in "All tabs". `units` is each entry's width in tabs (a split's joined tab
|
|
415
|
+
* counts its halves). An unmeasured strip (width 0) draws everything.
|
|
416
|
+
*/
|
|
417
|
+
declare function fitCanvasTabWindow(units: readonly number[], current: number, width: number, previousStart?: number): CanvasTabWindow;
|
|
402
418
|
/** One entry of a strip: a tab, or a split's visible tabs drawn as ONE joined tab (Chrome split view). */
|
|
403
419
|
type CanvasStripEntry = {
|
|
404
420
|
readonly type: "tab";
|
|
@@ -531,4 +547,4 @@ interface BrowserBarProps {
|
|
|
531
547
|
/** A browser's back · forward · reload · address bar, for a kind's leading slot or its body. */
|
|
532
548
|
declare function BrowserBar({ url, canGoBack, canGoForward, onBack, onForward, onReload, onNavigate, trailing, className }: BrowserBarProps): react.JSX.Element;
|
|
533
549
|
|
|
534
|
-
export { type AnyCanvasKind, BrowserBar, type BrowserBarProps, type BrowserTab, BrowserTabs, type BrowserTabsProps, CANVAS_TAB_MIN_WIDTH, CanvasColumn, type CanvasColumnProps, type CanvasFloating, CanvasFrame, CanvasHeaderButton, type CanvasHeaderButtonProps, type CanvasHostPorts, type CanvasKind, type CanvasKindIcon, CanvasKindIcons, type CanvasKindProps, type CanvasLauncherEntry, type CanvasMenuEntry, type CanvasMenuItem, CanvasMenuTrigger, type CanvasNativeBodyFrame, type CanvasNativeBodyInfo, type CanvasNativeRect, type CanvasOverlayPort, CanvasPaneView, CanvasPanel, CanvasPanelEmpty, type CanvasPanelProps, CanvasProvider, type CanvasProviderProps, type CanvasShowMenu, type CanvasStripEntry, type CanvasStripTab, CanvasTab, type CanvasTabProps, type CanvasTabStripMode, CanvasToggle, CanvasWindowScope, CanvasWindowTabs, beginCanvasResize, canvasSessionKey,
|
|
550
|
+
export { type AnyCanvasKind, BrowserBar, type BrowserBarProps, type BrowserTab, BrowserTabs, type BrowserTabsProps, CANVAS_TAB_LIST_ROOM, CANVAS_TAB_MIN_WIDTH, CanvasColumn, type CanvasColumnProps, type CanvasFloating, CanvasFrame, CanvasHeaderButton, type CanvasHeaderButtonProps, type CanvasHostPorts, type CanvasKind, type CanvasKindIcon, CanvasKindIcons, type CanvasKindProps, type CanvasLauncherEntry, type CanvasMenuEntry, type CanvasMenuItem, CanvasMenuTrigger, type CanvasNativeBodyFrame, type CanvasNativeBodyInfo, type CanvasNativeRect, type CanvasOverlayPort, CanvasPaneView, CanvasPanel, CanvasPanelEmpty, type CanvasPanelProps, CanvasProvider, type CanvasProviderProps, type CanvasShowMenu, type CanvasStripEntry, type CanvasStripTab, CanvasTab, type CanvasTabProps, type CanvasTabStripMode, type CanvasTabWindow, CanvasToggle, CanvasWindowScope, CanvasWindowTabs, beginCanvasResize, canvasSessionKey, defineCanvasKind, fitCanvasTabWindow, getCanvasKind, itemTitle, listCanvasKinds, planCanvasTabStrip, registerCanvasKind, registerCanvasKinds, useCanvas, useCanvasColumnWidth, useCanvasHostPorts, useCanvasIsPresented, useCanvasKind, useCanvasKindToggle, useCanvasKinds, useCanvasLauncherEntries, useCanvasLauncherEntry, useCanvasMenuItems, useCanvasNativeBodies, useCanvasPresentation, useCanvasState, useCanvasWindowId, useOptionalCanvas, useOptionalCanvasState };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ReactNode, CSSProperties, ButtonHTMLAttributes, HTMLAttributes, ReactElement } from 'react';
|
|
3
|
-
import { b as CanvasJson, i as CanvasItem, d as CanvasPaneId, h as CanvasController, a as CanvasWindowId,
|
|
3
|
+
import { b as CanvasJson, i as CanvasItem, d as CanvasPaneId, h as CanvasController, a as CanvasWindowId, Q as CanvasPresentation, V as CanvasSessionEvent, T as CanvasSessionAttachReason, U as CanvasSessionDetachReason, P as CanvasPreferredTarget, C as CanvasItemId, S as CanvasRect, e as CanvasStoreBinding, O as CanvasPersistencePort, F as CanvasErrorSink, I as CanvasLayoutRules, a0 as ToggleKindInput, H as CanvasKindVisibility, f as CanvasState } from './toggleKind-CI2nBjum.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* THE canvas kind registry. A kind is one sort of thing the canvas can show —
|
|
@@ -392,13 +392,29 @@ declare function CanvasPaneView({ paneId }: {
|
|
|
392
392
|
paneId: CanvasPaneId;
|
|
393
393
|
}): react.JSX.Element | null;
|
|
394
394
|
/**
|
|
395
|
-
* Every tab's floor (px): icon + ~10 letters + "…" — still readable. Tabs
|
|
396
|
-
* evenly from 12rem down to this;
|
|
397
|
-
* --mxc-tab-min.
|
|
395
|
+
* Every tab's floor (px): icon + ~10 letters + "…" — still readable. Tabs share
|
|
396
|
+
* the strip evenly from 12rem down to this; a tab that would go under it is not
|
|
397
|
+
* drawn (it waits in "All tabs"). Mirrored to CSS as --mxc-tab-min.
|
|
398
398
|
*/
|
|
399
399
|
declare const CANVAS_TAB_MIN_WIDTH = 120;
|
|
400
|
-
/**
|
|
401
|
-
declare
|
|
400
|
+
/** Room kept for "All tabs" (its chevron and the count of tabs out of the strip) while it shows. */
|
|
401
|
+
declare const CANVAS_TAB_LIST_ROOM = 44;
|
|
402
|
+
/** The run of strip entries drawn whole: `start` (inclusive) to `end` (exclusive). */
|
|
403
|
+
interface CanvasTabWindow {
|
|
404
|
+
readonly start: number;
|
|
405
|
+
readonly end: number;
|
|
406
|
+
/** True when even the current entry alone is wider than the room: it is drawn under its floor (ellipsis). */
|
|
407
|
+
readonly squeezed: boolean;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Which strip entries are drawn: every one when they fit at their floor; else
|
|
411
|
+
* the run that fits WHOLE beside "All tabs" and holds the current entry, moved
|
|
412
|
+
* as little as possible from `previousStart` (so switching tabs does not
|
|
413
|
+
* reshuffle the strip). A tab is never drawn sliced at an edge — the rest wait
|
|
414
|
+
* in "All tabs". `units` is each entry's width in tabs (a split's joined tab
|
|
415
|
+
* counts its halves). An unmeasured strip (width 0) draws everything.
|
|
416
|
+
*/
|
|
417
|
+
declare function fitCanvasTabWindow(units: readonly number[], current: number, width: number, previousStart?: number): CanvasTabWindow;
|
|
402
418
|
/** One entry of a strip: a tab, or a split's visible tabs drawn as ONE joined tab (Chrome split view). */
|
|
403
419
|
type CanvasStripEntry = {
|
|
404
420
|
readonly type: "tab";
|
|
@@ -531,4 +547,4 @@ interface BrowserBarProps {
|
|
|
531
547
|
/** A browser's back · forward · reload · address bar, for a kind's leading slot or its body. */
|
|
532
548
|
declare function BrowserBar({ url, canGoBack, canGoForward, onBack, onForward, onReload, onNavigate, trailing, className }: BrowserBarProps): react.JSX.Element;
|
|
533
549
|
|
|
534
|
-
export { type AnyCanvasKind, BrowserBar, type BrowserBarProps, type BrowserTab, BrowserTabs, type BrowserTabsProps, CANVAS_TAB_MIN_WIDTH, CanvasColumn, type CanvasColumnProps, type CanvasFloating, CanvasFrame, CanvasHeaderButton, type CanvasHeaderButtonProps, type CanvasHostPorts, type CanvasKind, type CanvasKindIcon, CanvasKindIcons, type CanvasKindProps, type CanvasLauncherEntry, type CanvasMenuEntry, type CanvasMenuItem, CanvasMenuTrigger, type CanvasNativeBodyFrame, type CanvasNativeBodyInfo, type CanvasNativeRect, type CanvasOverlayPort, CanvasPaneView, CanvasPanel, CanvasPanelEmpty, type CanvasPanelProps, CanvasProvider, type CanvasProviderProps, type CanvasShowMenu, type CanvasStripEntry, type CanvasStripTab, CanvasTab, type CanvasTabProps, type CanvasTabStripMode, CanvasToggle, CanvasWindowScope, CanvasWindowTabs, beginCanvasResize, canvasSessionKey,
|
|
550
|
+
export { type AnyCanvasKind, BrowserBar, type BrowserBarProps, type BrowserTab, BrowserTabs, type BrowserTabsProps, CANVAS_TAB_LIST_ROOM, CANVAS_TAB_MIN_WIDTH, CanvasColumn, type CanvasColumnProps, type CanvasFloating, CanvasFrame, CanvasHeaderButton, type CanvasHeaderButtonProps, type CanvasHostPorts, type CanvasKind, type CanvasKindIcon, CanvasKindIcons, type CanvasKindProps, type CanvasLauncherEntry, type CanvasMenuEntry, type CanvasMenuItem, CanvasMenuTrigger, type CanvasNativeBodyFrame, type CanvasNativeBodyInfo, type CanvasNativeRect, type CanvasOverlayPort, CanvasPaneView, CanvasPanel, CanvasPanelEmpty, type CanvasPanelProps, CanvasProvider, type CanvasProviderProps, type CanvasShowMenu, type CanvasStripEntry, type CanvasStripTab, CanvasTab, type CanvasTabProps, type CanvasTabStripMode, type CanvasTabWindow, CanvasToggle, CanvasWindowScope, CanvasWindowTabs, beginCanvasResize, canvasSessionKey, defineCanvasKind, fitCanvasTabWindow, getCanvasKind, itemTitle, listCanvasKinds, planCanvasTabStrip, registerCanvasKind, registerCanvasKinds, useCanvas, useCanvasColumnWidth, useCanvasHostPorts, useCanvasIsPresented, useCanvasKind, useCanvasKindToggle, useCanvasKinds, useCanvasLauncherEntries, useCanvasLauncherEntry, useCanvasMenuItems, useCanvasNativeBodies, useCanvasPresentation, useCanvasState, useCanvasWindowId, useOptionalCanvas, useOptionalCanvasState };
|
package/dist/react.js
CHANGED
|
@@ -987,6 +987,7 @@ function toPersistableSnapshot(state, isRestorable) {
|
|
|
987
987
|
|
|
988
988
|
// src/core/rules.ts
|
|
989
989
|
var CANVAS_PANE_MIN_WIDTH = 280;
|
|
990
|
+
var CANVAS_PANE_MIN_HEIGHT = 200;
|
|
990
991
|
var CANVAS_WEB_LAYOUT_RULES = {
|
|
991
992
|
defaultWidth: CANVAS_DEFAULT_WIDTH,
|
|
992
993
|
minWidth: CANVAS_MIN_WIDTH,
|
|
@@ -1000,7 +1001,10 @@ var CANVAS_WEB_LAYOUT_RULES = {
|
|
|
1000
1001
|
sideSplit: 0.5,
|
|
1001
1002
|
// A pane narrower than this cannot show a header (Back, a readable tab, "+", the menu, close) and a
|
|
1002
1003
|
// body side by side with another: a split right that would go under it stacks instead.
|
|
1003
|
-
paneMinWidth: CANVAS_PANE_MIN_WIDTH
|
|
1004
|
+
paneMinWidth: CANVAS_PANE_MIN_WIDTH,
|
|
1005
|
+
// 2026-10-07 live: Quick Notes / Notifications / Quick Chat stacked in an 800px window left
|
|
1006
|
+
// Notifications its header and one row.
|
|
1007
|
+
paneMinHeight: CANVAS_PANE_MIN_HEIGHT
|
|
1004
1008
|
};
|
|
1005
1009
|
var clampFraction = (value, fallback) => Number.isFinite(value) ? Math.min(0.88, Math.max(0.12, value)) : fallback;
|
|
1006
1010
|
function resolveCanvasLayoutRules(rules) {
|
|
@@ -1012,7 +1016,8 @@ function resolveCanvasLayoutRules(rules) {
|
|
|
1012
1016
|
centreMinWidth: positive(merged.centreMinWidth, CANVAS_WEB_LAYOUT_RULES.centreMinWidth),
|
|
1013
1017
|
stackSplit: clampFraction(merged.stackSplit, 0.5),
|
|
1014
1018
|
sideSplit: clampFraction(merged.sideSplit, 0.5),
|
|
1015
|
-
paneMinWidth: positive(merged.paneMinWidth, 0)
|
|
1019
|
+
paneMinWidth: positive(merged.paneMinWidth, 0),
|
|
1020
|
+
paneMinHeight: positive(merged.paneMinHeight, 0)
|
|
1016
1021
|
};
|
|
1017
1022
|
}
|
|
1018
1023
|
function defaultCanvasWidth(rules, region) {
|
|
@@ -1031,6 +1036,30 @@ function clampDraggedCanvasWidth(next, region, rules) {
|
|
|
1031
1036
|
function shouldStackSplit(width, children, rules) {
|
|
1032
1037
|
return rules.paneMinWidth > 0 && width > 0 && width < rules.paneMinWidth * children;
|
|
1033
1038
|
}
|
|
1039
|
+
function fitCanvasSplit(size, orientation, rules) {
|
|
1040
|
+
if (!size || size.width <= 0 || size.height <= 0) return orientation;
|
|
1041
|
+
const sideFits = rules.paneMinWidth <= 0 || size.width >= 2 * rules.paneMinWidth;
|
|
1042
|
+
const stackFits = rules.paneMinHeight <= 0 || size.height >= 2 * rules.paneMinHeight;
|
|
1043
|
+
if (orientation === "horizontal") return sideFits || stackFits ? "horizontal" : null;
|
|
1044
|
+
return stackFits ? "vertical" : sideFits ? "horizontal" : null;
|
|
1045
|
+
}
|
|
1046
|
+
function fitCanvasSplitSizes(sizes, total, mins) {
|
|
1047
|
+
if (!(total > 0) || sizes.length === 0) return [...sizes];
|
|
1048
|
+
const sum = sizes.reduce((a, b) => a + b, 0) || 1;
|
|
1049
|
+
const px = sizes.map((s) => s / sum * total);
|
|
1050
|
+
const floor = mins.map((m) => Number.isFinite(m) && m > 0 ? m : 0);
|
|
1051
|
+
const needed = floor.reduce((a, b) => a + b, 0);
|
|
1052
|
+
if (needed >= total) return needed > 0 ? floor.map((m) => m / needed) : px.map((p) => p / total);
|
|
1053
|
+
const short = px.reduce((acc, p, i) => acc + Math.max(0, (floor[i] ?? 0) - p), 0);
|
|
1054
|
+
if (short <= 0) return px.map((p) => p / total);
|
|
1055
|
+
const spare = px.map((p, i) => Math.max(0, p - (floor[i] ?? 0)));
|
|
1056
|
+
const spareSum = spare.reduce((a, b) => a + b, 0);
|
|
1057
|
+
return px.map((p, i) => {
|
|
1058
|
+
const min = floor[i] ?? 0;
|
|
1059
|
+
if (p <= min) return min / total;
|
|
1060
|
+
return (p - short * (spare[i] ?? 0) / spareSum) / total;
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1034
1063
|
|
|
1035
1064
|
// src/core/commands.ts
|
|
1036
1065
|
var CANVAS_SHORTCUTS = [
|
|
@@ -1128,6 +1157,7 @@ function createCanvasController(options) {
|
|
|
1128
1157
|
options,
|
|
1129
1158
|
onError: options.onError ?? consoleCanvasErrorSink,
|
|
1130
1159
|
rules: resolveCanvasLayoutRules(options.layoutRules),
|
|
1160
|
+
measures: /* @__PURE__ */ new Set(),
|
|
1131
1161
|
presentations: /* @__PURE__ */ new Map(),
|
|
1132
1162
|
presentationListeners: /* @__PURE__ */ new Set(),
|
|
1133
1163
|
views: /* @__PURE__ */ new Map(),
|
|
@@ -1148,6 +1178,14 @@ function view(shared, windowId) {
|
|
|
1148
1178
|
for (const listener of [...shared.presentationListeners]) listener();
|
|
1149
1179
|
};
|
|
1150
1180
|
const shareFor = (orientation) => orientation === "vertical" ? rules.stackSplit : rules.sideSplit;
|
|
1181
|
+
const measurePane = (paneId) => {
|
|
1182
|
+
for (const measure of shared.measures) {
|
|
1183
|
+
const size = measure(paneId);
|
|
1184
|
+
if (size) return size;
|
|
1185
|
+
}
|
|
1186
|
+
return null;
|
|
1187
|
+
};
|
|
1188
|
+
const fitSplit = (paneId, orientation) => fitCanvasSplit(measurePane(paneId), orientation, rules);
|
|
1151
1189
|
const acceptable = (input) => {
|
|
1152
1190
|
if (input.kind !== CANVAS_LAUNCHER_KIND && options.isKnownKind && !options.isKnownKind(input.kind)) {
|
|
1153
1191
|
onError({ code: "unknown-kind", message: `No canvas kind "${input.kind}" is registered.`, detail: input });
|
|
@@ -1178,6 +1216,13 @@ function view(shared, windowId) {
|
|
|
1178
1216
|
if (holding) return { paneId: holding };
|
|
1179
1217
|
return state.panes[frame2.focusedPaneId]?.activeItemId ? preferred : "focused";
|
|
1180
1218
|
};
|
|
1219
|
+
const splitTargetFor = (asked, forWindow) => {
|
|
1220
|
+
const state = store.getState();
|
|
1221
|
+
const frame2 = canvasWindowFrame(state, forWindow ? canvasWindowId(forWindow) : windowId);
|
|
1222
|
+
if (!frame2) return asked;
|
|
1223
|
+
const fitted = fitSplit(frame2.focusedPaneId, asked === "split-down" ? "vertical" : "horizontal");
|
|
1224
|
+
return fitted === null ? "focused" : fitted === "vertical" ? "split-down" : "split-right";
|
|
1225
|
+
};
|
|
1181
1226
|
const controller = {
|
|
1182
1227
|
store,
|
|
1183
1228
|
windowId,
|
|
@@ -1186,7 +1231,8 @@ function view(shared, windowId) {
|
|
|
1186
1231
|
getState: () => store.getState(),
|
|
1187
1232
|
open(input) {
|
|
1188
1233
|
if (!acceptable(input)) return null;
|
|
1189
|
-
const
|
|
1234
|
+
const asked = input.target ?? preferredTargetFor(input);
|
|
1235
|
+
const target = asked === "split-down" || asked === "split-right" ? splitTargetFor(asked, input.windowId) : asked;
|
|
1190
1236
|
const splitShare = target === "split-down" ? rules.stackSplit : target === "split-right" ? rules.sideSplit : void 0;
|
|
1191
1237
|
const withTarget = input.target === target ? input : { ...input, target };
|
|
1192
1238
|
const resolved = withTarget.windowId || windowId === CANVAS_MAIN_WINDOW ? withTarget : { ...withTarget, windowId };
|
|
@@ -1251,7 +1297,15 @@ function view(shared, windowId) {
|
|
|
1251
1297
|
activate: (itemId) => dispatch(canvasActions.activate(itemId)),
|
|
1252
1298
|
focusPane: (paneId) => dispatch(canvasActions.focusPane(paneId)),
|
|
1253
1299
|
moveItem: (itemId, toPaneId, index) => dispatch(canvasActions.moveItem(itemId, toPaneId, index)),
|
|
1254
|
-
splitPane
|
|
1300
|
+
splitPane(paneId, orientation, moveItemId) {
|
|
1301
|
+
const fitted = fitSplit(paneId, orientation);
|
|
1302
|
+
if (fitted) dispatch(canvasActions.splitPane(paneId, fitted, moveItemId, shareFor(fitted)));
|
|
1303
|
+
},
|
|
1304
|
+
fitSplit,
|
|
1305
|
+
registerPaneMeasure(measure) {
|
|
1306
|
+
shared.measures.add(measure);
|
|
1307
|
+
return () => void shared.measures.delete(measure);
|
|
1308
|
+
},
|
|
1255
1309
|
closePane: (paneId) => dispatch(canvasActions.closePane(paneId)),
|
|
1256
1310
|
resizeSplit: (splitId, sizes) => dispatch(canvasActions.resizeSplit(splitId, sizes)),
|
|
1257
1311
|
show: (w = windowId) => dispatch(canvasActions.setOpen(true, w)),
|
|
@@ -2889,8 +2943,23 @@ var Rendered = memo(function Rendered2({
|
|
|
2889
2943
|
presentation
|
|
2890
2944
|
}) {
|
|
2891
2945
|
const props = { item, data: item.data, paneId, isFocused, isVisible, canvas, windowId, presentation };
|
|
2892
|
-
|
|
2946
|
+
const [attempt, setAttempt] = useState4(0);
|
|
2947
|
+
const retry = () => {
|
|
2948
|
+
resetKindComponent(kind);
|
|
2949
|
+
setAttempt((n) => n + 1);
|
|
2950
|
+
};
|
|
2951
|
+
return /* @__PURE__ */ jsx6(ItemBoundary, { item, onClose: () => canvas.close(item.id), onRetry: retry, children: /* @__PURE__ */ jsx6(Suspense, { fallback: /* @__PURE__ */ jsx6(LoadingBody, { onRetry: retry, onClose: () => canvas.close(item.id) }), children: /* @__PURE__ */ jsx6(KindRender, { kind, props }) }, attempt) });
|
|
2893
2952
|
});
|
|
2953
|
+
var CANVAS_BODY_STALL_MS = 2e4;
|
|
2954
|
+
function LoadingBody({ onRetry, onClose }) {
|
|
2955
|
+
const [stalled, setStalled] = useState4(false);
|
|
2956
|
+
useEffect6(() => {
|
|
2957
|
+
const timer = setTimeout(() => setStalled(true), CANVAS_BODY_STALL_MS);
|
|
2958
|
+
return () => clearTimeout(timer);
|
|
2959
|
+
}, []);
|
|
2960
|
+
if (!stalled) return /* @__PURE__ */ jsx6("div", { className: "mxc-loading", "aria-busy": "true" });
|
|
2961
|
+
return /* @__PURE__ */ jsx6(ItemProblem, { message: "This item is taking a long time to load.", onRetry, onClose });
|
|
2962
|
+
}
|
|
2894
2963
|
function KindRender({ kind, props }) {
|
|
2895
2964
|
const Render = kindComponent(kind);
|
|
2896
2965
|
if (!Render) throw new Error(`[@ai-matrx/canvas] kind "${kind.id}" has nothing to render.`);
|
|
@@ -3169,9 +3238,36 @@ function PaneTitle({ item, kind }) {
|
|
|
3169
3238
|
}
|
|
3170
3239
|
var CANVAS_TAB_MIN_WIDTH = 120;
|
|
3171
3240
|
var TAB_GAP = 2;
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3241
|
+
var CANVAS_TAB_LIST_ROOM = 44;
|
|
3242
|
+
function fitCanvasTabWindow(units, current, width, previousStart = 0) {
|
|
3243
|
+
const n = units.length;
|
|
3244
|
+
const need = (i) => Math.max(1, units[i] ?? 1) * CANVAS_TAB_MIN_WIDTH;
|
|
3245
|
+
const all = units.reduce((sum, _, i) => sum + need(i), 0) + Math.max(0, n - 1) * TAB_GAP;
|
|
3246
|
+
if (n <= 1 || width <= 0 || all <= width) return { start: 0, end: n, squeezed: n === 1 && width > 0 && all > width };
|
|
3247
|
+
const room = width - CANVAS_TAB_LIST_ROOM;
|
|
3248
|
+
const at = Math.min(n - 1, Math.max(0, current));
|
|
3249
|
+
let start = Math.min(Math.max(0, previousStart), at);
|
|
3250
|
+
let used = 0;
|
|
3251
|
+
let end = start;
|
|
3252
|
+
while (end < n && used + need(end) + (end > start ? TAB_GAP : 0) <= room) {
|
|
3253
|
+
used += need(end) + (end > start ? TAB_GAP : 0);
|
|
3254
|
+
end += 1;
|
|
3255
|
+
}
|
|
3256
|
+
if (end <= at) {
|
|
3257
|
+
start = at;
|
|
3258
|
+
end = at + 1;
|
|
3259
|
+
used = need(at);
|
|
3260
|
+
while (start > 0 && used + TAB_GAP + need(start - 1) <= room) {
|
|
3261
|
+
start -= 1;
|
|
3262
|
+
used += TAB_GAP + need(start);
|
|
3263
|
+
}
|
|
3264
|
+
} else {
|
|
3265
|
+
while (start > 0 && used + TAB_GAP + need(start - 1) <= room) {
|
|
3266
|
+
start -= 1;
|
|
3267
|
+
used += TAB_GAP + need(start);
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
return { start, end, squeezed: need(at) > room && end - start === 1 };
|
|
3175
3271
|
}
|
|
3176
3272
|
function planCanvasTabStrip(tabs, actives) {
|
|
3177
3273
|
const joined = actives ? tabs.filter((tab) => actives.get(tab.paneId) === tab.itemId) : [];
|
|
@@ -3206,9 +3302,12 @@ function TabStrip({
|
|
|
3206
3302
|
activeItemId,
|
|
3207
3303
|
actives
|
|
3208
3304
|
}) {
|
|
3305
|
+
const canvas = useCanvas();
|
|
3209
3306
|
const strip = useRef7(null);
|
|
3210
3307
|
const list = useRef7(null);
|
|
3211
3308
|
const [width, setWidth] = useState5(0);
|
|
3309
|
+
const windowStart = useRef7(0);
|
|
3310
|
+
const focusAfterMove = useRef7(false);
|
|
3212
3311
|
const read = useRef7(() => {
|
|
3213
3312
|
});
|
|
3214
3313
|
useIsoLayoutEffect4(() => {
|
|
@@ -3233,31 +3332,31 @@ function TabStrip({
|
|
|
3233
3332
|
useIsoLayoutEffect4(() => {
|
|
3234
3333
|
read.current();
|
|
3235
3334
|
}, [tabs.length]);
|
|
3236
|
-
useEffect7(() => {
|
|
3237
|
-
const el = list.current;
|
|
3238
|
-
if (!el) return void 0;
|
|
3239
|
-
const onWheel = (event) => {
|
|
3240
|
-
if (Math.abs(event.deltaY) <= Math.abs(event.deltaX) || el.scrollWidth <= el.clientWidth) return;
|
|
3241
|
-
event.preventDefault();
|
|
3242
|
-
el.scrollLeft += event.deltaY;
|
|
3243
|
-
};
|
|
3244
|
-
el.addEventListener("wheel", onWheel, { passive: false });
|
|
3245
|
-
return () => el.removeEventListener("wheel", onWheel);
|
|
3246
|
-
}, []);
|
|
3247
3335
|
useIsoLayoutEffect4(() => {
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
const target = tab.closest(".mxc-tab-group") ?? tab;
|
|
3253
|
-
const box = el.getBoundingClientRect();
|
|
3254
|
-
const rect = target.getBoundingClientRect();
|
|
3255
|
-
if (rect.left < box.left) el.scrollLeft -= box.left - rect.left;
|
|
3256
|
-
else if (rect.right > box.right) el.scrollLeft += rect.right - box.right;
|
|
3257
|
-
}, [activeItemId, width, tabs.length]);
|
|
3336
|
+
if (!focusAfterMove.current || !activeItemId) return;
|
|
3337
|
+
focusAfterMove.current = false;
|
|
3338
|
+
tabElement(list.current, activeItemId)?.focus();
|
|
3339
|
+
}, [activeItemId]);
|
|
3258
3340
|
const entries = planCanvasTabStrip(tabs, actives);
|
|
3259
|
-
const
|
|
3341
|
+
const entryTabs = (entry) => entry.type === "tab" ? [entry.tab] : entry.tabs;
|
|
3342
|
+
const current = Math.max(
|
|
3343
|
+
0,
|
|
3344
|
+
entries.findIndex((entry) => entryTabs(entry).some((tab) => tab.itemId === activeItemId))
|
|
3345
|
+
);
|
|
3346
|
+
const fit = fitCanvasTabWindow(
|
|
3347
|
+
entries.map((entry) => entryTabs(entry).length),
|
|
3348
|
+
current,
|
|
3349
|
+
width,
|
|
3350
|
+
windowStart.current
|
|
3351
|
+
);
|
|
3352
|
+
useIsoLayoutEffect4(() => {
|
|
3353
|
+
windowStart.current = fit.start;
|
|
3354
|
+
});
|
|
3355
|
+
const shown = entries.slice(fit.start, fit.end);
|
|
3356
|
+
const hidden = tabs.length - shown.reduce((sum, entry) => sum + entryTabs(entry).length, 0);
|
|
3357
|
+
const overflow = hidden > 0;
|
|
3260
3358
|
const joined = entries.some((entry) => entry.type === "split");
|
|
3359
|
+
const order = entries.flatMap(entryTabs).map((tab) => tab.itemId);
|
|
3261
3360
|
const isActive = (tab) => actives ? actives.get(tab.paneId) === tab.itemId : tab.itemId === activeItemId;
|
|
3262
3361
|
const tabView = (tab) => /* @__PURE__ */ jsx7(
|
|
3263
3362
|
ItemTab,
|
|
@@ -3270,6 +3369,22 @@ function TabStrip({
|
|
|
3270
3369
|
},
|
|
3271
3370
|
tab.itemId
|
|
3272
3371
|
);
|
|
3372
|
+
const onKeyDown = (event) => {
|
|
3373
|
+
const keys = ["ArrowLeft", "ArrowRight", "Home", "End"];
|
|
3374
|
+
if (!keys.includes(event.key) || event.altKey || event.ctrlKey || event.metaKey || order.length < 2) return;
|
|
3375
|
+
const from = event.target.closest("[data-mxc-tab]")?.dataset.mxcTab;
|
|
3376
|
+
const at = from ? order.indexOf(from) : -1;
|
|
3377
|
+
if (at < 0) return;
|
|
3378
|
+
event.preventDefault();
|
|
3379
|
+
const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
|
|
3380
|
+
const step = event.key === "ArrowRight" !== rtl ? 1 : -1;
|
|
3381
|
+
const next = event.key === "Home" ? 0 : event.key === "End" ? order.length - 1 : (at + step + order.length) % order.length;
|
|
3382
|
+
const target = order[next];
|
|
3383
|
+
if (!target) return;
|
|
3384
|
+
focusAfterMove.current = true;
|
|
3385
|
+
canvas.activate(target);
|
|
3386
|
+
tabElement(list.current, target)?.focus();
|
|
3387
|
+
};
|
|
3273
3388
|
return /* @__PURE__ */ jsxs6(
|
|
3274
3389
|
"div",
|
|
3275
3390
|
{
|
|
@@ -3286,8 +3401,9 @@ function TabStrip({
|
|
|
3286
3401
|
role: "tablist",
|
|
3287
3402
|
"aria-label": "Canvas tabs",
|
|
3288
3403
|
"aria-multiselectable": joined ? true : void 0,
|
|
3289
|
-
|
|
3290
|
-
|
|
3404
|
+
"data-squeezed": fit.squeezed ? "" : void 0,
|
|
3405
|
+
onKeyDown,
|
|
3406
|
+
children: shown.map(
|
|
3291
3407
|
(entry) => entry.type === "tab" ? tabView(entry.tab) : /* @__PURE__ */ jsx7(
|
|
3292
3408
|
"div",
|
|
3293
3409
|
{
|
|
@@ -3302,32 +3418,24 @@ function TabStrip({
|
|
|
3302
3418
|
)
|
|
3303
3419
|
}
|
|
3304
3420
|
),
|
|
3305
|
-
overflow ? /* @__PURE__ */ jsx7(TabListMenu, { tabs, activeItemId }) : null
|
|
3421
|
+
overflow ? /* @__PURE__ */ jsx7(TabListMenu, { tabs, activeItemId, hidden }) : null
|
|
3306
3422
|
]
|
|
3307
3423
|
}
|
|
3308
3424
|
);
|
|
3309
3425
|
}
|
|
3310
|
-
function
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
const at = tabs.findIndex((tab) => tab === event.target || tab.contains(event.target));
|
|
3315
|
-
if (at < 0 || tabs.length < 2) return;
|
|
3316
|
-
event.preventDefault();
|
|
3317
|
-
const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
|
|
3318
|
-
const step = event.key === "ArrowRight" !== rtl ? 1 : -1;
|
|
3319
|
-
const next = event.key === "Home" ? 0 : event.key === "End" ? tabs.length - 1 : (at + step + tabs.length) % tabs.length;
|
|
3320
|
-
const target = tabs[next];
|
|
3321
|
-
target?.focus();
|
|
3322
|
-
target?.click();
|
|
3323
|
-
}
|
|
3324
|
-
function TabListMenu({ tabs, activeItemId }) {
|
|
3426
|
+
function tabElement(list, itemId) {
|
|
3427
|
+
return [...list?.querySelectorAll("[data-mxc-tab]") ?? []].find((node) => node.dataset.mxcTab === itemId);
|
|
3428
|
+
}
|
|
3429
|
+
function TabListMenu({ tabs, activeItemId, hidden }) {
|
|
3325
3430
|
const canvas = useCanvas();
|
|
3326
3431
|
const { ports, layer } = useCanvasInternals();
|
|
3327
3432
|
const blocking = useRef7(null);
|
|
3328
3433
|
useEffect7(() => () => blocking.current?.(), []);
|
|
3329
3434
|
const label = `All tabs (${tabs.length})`;
|
|
3330
|
-
const trigger = (props) => /* @__PURE__ */
|
|
3435
|
+
const trigger = (props) => /* @__PURE__ */ jsxs6(CanvasHeaderButton, { className: "mxc-tab-list", label, ...props, children: [
|
|
3436
|
+
/* @__PURE__ */ jsx7("span", { className: "mxc-tab-list-count", "aria-hidden": "true", children: hidden }),
|
|
3437
|
+
/* @__PURE__ */ jsx7(ChevronDownIcon, {})
|
|
3438
|
+
] });
|
|
3331
3439
|
const showMenu = ports.showMenu;
|
|
3332
3440
|
if (showMenu) {
|
|
3333
3441
|
return trigger({
|
|
@@ -3445,10 +3553,9 @@ function PaneMenu({
|
|
|
3445
3553
|
return [
|
|
3446
3554
|
[...kindItems, ...bodyItems, ...hostItems].map((entry) => ({ ...entry, id: `item:${entry.id}` })),
|
|
3447
3555
|
[
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
] : [],
|
|
3556
|
+
// Only a split that leaves both panes usable (the layout rules' minimums) is offered.
|
|
3557
|
+
...canSplit && canvas.fitSplit(paneId, "horizontal") === "horizontal" ? [{ id: "split-right", label: "Split right", icon: /* @__PURE__ */ jsx7(SplitRightIcon, {}), onSelect: () => canvas.splitPane(paneId, "horizontal", moveId) }] : [],
|
|
3558
|
+
...canSplit && canvas.fitSplit(paneId, "vertical") === "vertical" ? [{ id: "split-down", label: "Split down", icon: /* @__PURE__ */ jsx7(SplitDownIcon, {}), onSelect: () => canvas.splitPane(paneId, "vertical", moveId) }] : [],
|
|
3452
3559
|
...activeItem && role === "pop-out" ? [{ id: "dock-back", label: "Dock back", icon: /* @__PURE__ */ jsx7(DockIcon, {}), onSelect: () => void canvas.dockBack(activeItem.id) }] : activeItem && ports.popOut ? [{ id: "pop-out", label: "Pop out", icon: /* @__PURE__ */ jsx7(PopOutIcon, {}), onSelect: () => void canvas.popOut(activeItem.id) }] : []
|
|
3453
3560
|
],
|
|
3454
3561
|
activeItem ? [
|
|
@@ -3557,6 +3664,15 @@ function CanvasColumn({ className, style, onLiveWidth, placement = "docked" }) {
|
|
|
3557
3664
|
const [slots] = useState6(createCanvasSlots);
|
|
3558
3665
|
const ref = useRef8(null);
|
|
3559
3666
|
useRegisterCanvasPresence();
|
|
3667
|
+
useEffect8(
|
|
3668
|
+
() => controller.registerPaneMeasure((paneId) => {
|
|
3669
|
+
const section = slots.section(paneId);
|
|
3670
|
+
if (!section?.isConnected) return null;
|
|
3671
|
+
const rect = section.getBoundingClientRect();
|
|
3672
|
+
return rect.width > 0 && rect.height > 0 ? { width: rect.width, height: rect.height } : null;
|
|
3673
|
+
}),
|
|
3674
|
+
[controller, slots]
|
|
3675
|
+
);
|
|
3560
3676
|
useEffect8(() => {
|
|
3561
3677
|
const el = ref.current;
|
|
3562
3678
|
if (!el) return void 0;
|
|
@@ -3678,28 +3794,42 @@ function LayoutNodeView({ node }) {
|
|
|
3678
3794
|
if (node.type === "pane") return /* @__PURE__ */ jsx8(CanvasPaneView, { paneId: node.paneId });
|
|
3679
3795
|
return /* @__PURE__ */ jsx8(SplitView, { node });
|
|
3680
3796
|
}
|
|
3681
|
-
function
|
|
3682
|
-
const [
|
|
3797
|
+
function useMeasuredSize(ref, enabled) {
|
|
3798
|
+
const [size, setSize] = useState6({ width: 0, height: 0 });
|
|
3683
3799
|
useIsoLayoutEffect5(() => {
|
|
3684
3800
|
const el = ref.current;
|
|
3685
3801
|
if (!enabled || !el) return;
|
|
3686
|
-
|
|
3802
|
+
const read = () => {
|
|
3803
|
+
const rect = el.getBoundingClientRect();
|
|
3804
|
+
setSize((prev) => prev.width === rect.width && prev.height === rect.height ? prev : { width: rect.width, height: rect.height });
|
|
3805
|
+
};
|
|
3806
|
+
read();
|
|
3687
3807
|
if (typeof ResizeObserver === "undefined") return;
|
|
3688
|
-
const observer = new ResizeObserver(
|
|
3808
|
+
const observer = new ResizeObserver(read);
|
|
3689
3809
|
observer.observe(el);
|
|
3690
3810
|
return () => observer.disconnect();
|
|
3691
3811
|
}, [enabled]);
|
|
3692
|
-
return
|
|
3812
|
+
return size;
|
|
3813
|
+
}
|
|
3814
|
+
function panesAlong(node, orientation) {
|
|
3815
|
+
if (node.type === "pane") return 1;
|
|
3816
|
+
const counts = node.children.map((child) => panesAlong(child, orientation));
|
|
3817
|
+
return node.orientation === orientation ? counts.reduce((a, b) => a + b, 0) : Math.max(...counts);
|
|
3818
|
+
}
|
|
3819
|
+
function childMinimums(node, orientation, rules) {
|
|
3820
|
+
const unit = orientation === "vertical" ? rules.paneMinHeight : rules.paneMinWidth;
|
|
3821
|
+
return node.children.map((child) => unit * panesAlong(child, orientation));
|
|
3693
3822
|
}
|
|
3694
3823
|
function SplitView({ node }) {
|
|
3695
3824
|
const canvas = useCanvas();
|
|
3696
3825
|
const ref = useRef8(null);
|
|
3697
3826
|
const [liveSizes, setLiveSizes] = useState6(null);
|
|
3698
|
-
const
|
|
3699
|
-
const
|
|
3700
|
-
const stacked = node.orientation === "horizontal" && shouldStackSplit(measuredWidth, node.children.length, canvas.rules);
|
|
3827
|
+
const measured = useMeasuredSize(ref, canvas.rules.paneMinWidth > 0 || canvas.rules.paneMinHeight > 0);
|
|
3828
|
+
const stacked = node.orientation === "horizontal" && shouldStackSplit(measured.width, node.children.length, canvas.rules);
|
|
3701
3829
|
const orientation = stacked ? "vertical" : node.orientation;
|
|
3702
3830
|
const horizontal = orientation === "horizontal";
|
|
3831
|
+
const mins = childMinimums(node, orientation, canvas.rules);
|
|
3832
|
+
const sizes = fitCanvasSplitSizes(liveSizes ?? node.sizes, horizontal ? measured.width : measured.height, mins);
|
|
3703
3833
|
const beginDrag = (index) => (event) => {
|
|
3704
3834
|
const el = ref.current;
|
|
3705
3835
|
if (!el) return;
|
|
@@ -3708,14 +3838,15 @@ function SplitView({ node }) {
|
|
|
3708
3838
|
const rect = el.getBoundingClientRect();
|
|
3709
3839
|
const total = horizontal ? rect.width : rect.height;
|
|
3710
3840
|
const origin = horizontal ? event.clientX : event.clientY;
|
|
3711
|
-
const base = [...
|
|
3841
|
+
const base = [...sizes];
|
|
3712
3842
|
const pair = (base[index] ?? 0) + (base[index + 1] ?? 0);
|
|
3843
|
+
const minFirst = Math.max(CANVAS_MIN_SPLIT_FRACTION, total > 0 ? (mins[index] ?? 0) / total : 0);
|
|
3844
|
+
const minSecond = Math.max(CANVAS_MIN_SPLIT_FRACTION, total > 0 ? (mins[index + 1] ?? 0) / total : 0);
|
|
3713
3845
|
const target = event.currentTarget;
|
|
3714
3846
|
const end = beginCanvasResize(horizontal ? "col" : "row");
|
|
3715
3847
|
const compute = (pos) => {
|
|
3716
3848
|
const delta = (pos - origin) / total;
|
|
3717
|
-
const
|
|
3718
|
-
const first = Math.min(pair - min, Math.max(min, (base[index] ?? 0) + delta));
|
|
3849
|
+
const first = Math.max(Math.min(pair - minSecond, Math.max(minFirst, (base[index] ?? 0) + delta)), Math.min(minFirst, pair / 2));
|
|
3719
3850
|
const next = [...base];
|
|
3720
3851
|
next[index] = first;
|
|
3721
3852
|
next[index + 1] = pair - first;
|
|
@@ -3830,6 +3961,7 @@ function CanvasFrame({ children, className }) {
|
|
|
3830
3961
|
export {
|
|
3831
3962
|
BrowserBar,
|
|
3832
3963
|
BrowserTabs,
|
|
3964
|
+
CANVAS_TAB_LIST_ROOM,
|
|
3833
3965
|
CANVAS_TAB_MIN_WIDTH,
|
|
3834
3966
|
CanvasColumn,
|
|
3835
3967
|
CanvasFrame,
|
|
@@ -3846,8 +3978,8 @@ export {
|
|
|
3846
3978
|
CanvasWindowTabs,
|
|
3847
3979
|
beginCanvasResize,
|
|
3848
3980
|
canvasSessionKey,
|
|
3849
|
-
canvasTabsOverflow,
|
|
3850
3981
|
defineCanvasKind,
|
|
3982
|
+
fitCanvasTabWindow,
|
|
3851
3983
|
getCanvasKind,
|
|
3852
3984
|
itemTitle,
|
|
3853
3985
|
listCanvasKinds,
|