@ai-matrx/canvas 0.8.4 → 0.9.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/CHANGELOG.md +24 -1
- 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 +182 -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 +182 -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)),
|
|
@@ -2332,7 +2386,7 @@ function BrowserBar({ url, canGoBack, canGoForward, onBack, onForward, onReload,
|
|
|
2332
2386
|
"data-slot": "browser-address",
|
|
2333
2387
|
className: "mxc-address",
|
|
2334
2388
|
value: draft,
|
|
2335
|
-
placeholder: "Search or enter
|
|
2389
|
+
placeholder: "Search or enter website",
|
|
2336
2390
|
spellCheck: false,
|
|
2337
2391
|
onFocus: (event) => {
|
|
2338
2392
|
setEditing(true);
|
|
@@ -3169,9 +3223,36 @@ function PaneTitle({ item, kind }) {
|
|
|
3169
3223
|
}
|
|
3170
3224
|
var CANVAS_TAB_MIN_WIDTH = 120;
|
|
3171
3225
|
var TAB_GAP = 2;
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3226
|
+
var CANVAS_TAB_LIST_ROOM = 44;
|
|
3227
|
+
function fitCanvasTabWindow(units, current, width, previousStart = 0) {
|
|
3228
|
+
const n = units.length;
|
|
3229
|
+
const need = (i) => Math.max(1, units[i] ?? 1) * CANVAS_TAB_MIN_WIDTH;
|
|
3230
|
+
const all = units.reduce((sum, _, i) => sum + need(i), 0) + Math.max(0, n - 1) * TAB_GAP;
|
|
3231
|
+
if (n <= 1 || width <= 0 || all <= width) return { start: 0, end: n, squeezed: n === 1 && width > 0 && all > width };
|
|
3232
|
+
const room = width - CANVAS_TAB_LIST_ROOM;
|
|
3233
|
+
const at = Math.min(n - 1, Math.max(0, current));
|
|
3234
|
+
let start = Math.min(Math.max(0, previousStart), at);
|
|
3235
|
+
let used = 0;
|
|
3236
|
+
let end = start;
|
|
3237
|
+
while (end < n && used + need(end) + (end > start ? TAB_GAP : 0) <= room) {
|
|
3238
|
+
used += need(end) + (end > start ? TAB_GAP : 0);
|
|
3239
|
+
end += 1;
|
|
3240
|
+
}
|
|
3241
|
+
if (end <= at) {
|
|
3242
|
+
start = at;
|
|
3243
|
+
end = at + 1;
|
|
3244
|
+
used = need(at);
|
|
3245
|
+
while (start > 0 && used + TAB_GAP + need(start - 1) <= room) {
|
|
3246
|
+
start -= 1;
|
|
3247
|
+
used += TAB_GAP + need(start);
|
|
3248
|
+
}
|
|
3249
|
+
} else {
|
|
3250
|
+
while (start > 0 && used + TAB_GAP + need(start - 1) <= room) {
|
|
3251
|
+
start -= 1;
|
|
3252
|
+
used += TAB_GAP + need(start);
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
return { start, end, squeezed: need(at) > room && end - start === 1 };
|
|
3175
3256
|
}
|
|
3176
3257
|
function planCanvasTabStrip(tabs, actives) {
|
|
3177
3258
|
const joined = actives ? tabs.filter((tab) => actives.get(tab.paneId) === tab.itemId) : [];
|
|
@@ -3206,9 +3287,12 @@ function TabStrip({
|
|
|
3206
3287
|
activeItemId,
|
|
3207
3288
|
actives
|
|
3208
3289
|
}) {
|
|
3290
|
+
const canvas = useCanvas();
|
|
3209
3291
|
const strip = useRef7(null);
|
|
3210
3292
|
const list = useRef7(null);
|
|
3211
3293
|
const [width, setWidth] = useState5(0);
|
|
3294
|
+
const windowStart = useRef7(0);
|
|
3295
|
+
const focusAfterMove = useRef7(false);
|
|
3212
3296
|
const read = useRef7(() => {
|
|
3213
3297
|
});
|
|
3214
3298
|
useIsoLayoutEffect4(() => {
|
|
@@ -3233,31 +3317,31 @@ function TabStrip({
|
|
|
3233
3317
|
useIsoLayoutEffect4(() => {
|
|
3234
3318
|
read.current();
|
|
3235
3319
|
}, [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
3320
|
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]);
|
|
3321
|
+
if (!focusAfterMove.current || !activeItemId) return;
|
|
3322
|
+
focusAfterMove.current = false;
|
|
3323
|
+
tabElement(list.current, activeItemId)?.focus();
|
|
3324
|
+
}, [activeItemId]);
|
|
3258
3325
|
const entries = planCanvasTabStrip(tabs, actives);
|
|
3259
|
-
const
|
|
3326
|
+
const entryTabs = (entry) => entry.type === "tab" ? [entry.tab] : entry.tabs;
|
|
3327
|
+
const current = Math.max(
|
|
3328
|
+
0,
|
|
3329
|
+
entries.findIndex((entry) => entryTabs(entry).some((tab) => tab.itemId === activeItemId))
|
|
3330
|
+
);
|
|
3331
|
+
const fit = fitCanvasTabWindow(
|
|
3332
|
+
entries.map((entry) => entryTabs(entry).length),
|
|
3333
|
+
current,
|
|
3334
|
+
width,
|
|
3335
|
+
windowStart.current
|
|
3336
|
+
);
|
|
3337
|
+
useIsoLayoutEffect4(() => {
|
|
3338
|
+
windowStart.current = fit.start;
|
|
3339
|
+
});
|
|
3340
|
+
const shown = entries.slice(fit.start, fit.end);
|
|
3341
|
+
const hidden = tabs.length - shown.reduce((sum, entry) => sum + entryTabs(entry).length, 0);
|
|
3342
|
+
const overflow = hidden > 0;
|
|
3260
3343
|
const joined = entries.some((entry) => entry.type === "split");
|
|
3344
|
+
const order = entries.flatMap(entryTabs).map((tab) => tab.itemId);
|
|
3261
3345
|
const isActive = (tab) => actives ? actives.get(tab.paneId) === tab.itemId : tab.itemId === activeItemId;
|
|
3262
3346
|
const tabView = (tab) => /* @__PURE__ */ jsx7(
|
|
3263
3347
|
ItemTab,
|
|
@@ -3270,6 +3354,22 @@ function TabStrip({
|
|
|
3270
3354
|
},
|
|
3271
3355
|
tab.itemId
|
|
3272
3356
|
);
|
|
3357
|
+
const onKeyDown = (event) => {
|
|
3358
|
+
const keys = ["ArrowLeft", "ArrowRight", "Home", "End"];
|
|
3359
|
+
if (!keys.includes(event.key) || event.altKey || event.ctrlKey || event.metaKey || order.length < 2) return;
|
|
3360
|
+
const from = event.target.closest("[data-mxc-tab]")?.dataset.mxcTab;
|
|
3361
|
+
const at = from ? order.indexOf(from) : -1;
|
|
3362
|
+
if (at < 0) return;
|
|
3363
|
+
event.preventDefault();
|
|
3364
|
+
const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
|
|
3365
|
+
const step = event.key === "ArrowRight" !== rtl ? 1 : -1;
|
|
3366
|
+
const next = event.key === "Home" ? 0 : event.key === "End" ? order.length - 1 : (at + step + order.length) % order.length;
|
|
3367
|
+
const target = order[next];
|
|
3368
|
+
if (!target) return;
|
|
3369
|
+
focusAfterMove.current = true;
|
|
3370
|
+
canvas.activate(target);
|
|
3371
|
+
tabElement(list.current, target)?.focus();
|
|
3372
|
+
};
|
|
3273
3373
|
return /* @__PURE__ */ jsxs6(
|
|
3274
3374
|
"div",
|
|
3275
3375
|
{
|
|
@@ -3286,8 +3386,9 @@ function TabStrip({
|
|
|
3286
3386
|
role: "tablist",
|
|
3287
3387
|
"aria-label": "Canvas tabs",
|
|
3288
3388
|
"aria-multiselectable": joined ? true : void 0,
|
|
3289
|
-
|
|
3290
|
-
|
|
3389
|
+
"data-squeezed": fit.squeezed ? "" : void 0,
|
|
3390
|
+
onKeyDown,
|
|
3391
|
+
children: shown.map(
|
|
3291
3392
|
(entry) => entry.type === "tab" ? tabView(entry.tab) : /* @__PURE__ */ jsx7(
|
|
3292
3393
|
"div",
|
|
3293
3394
|
{
|
|
@@ -3302,32 +3403,24 @@ function TabStrip({
|
|
|
3302
3403
|
)
|
|
3303
3404
|
}
|
|
3304
3405
|
),
|
|
3305
|
-
overflow ? /* @__PURE__ */ jsx7(TabListMenu, { tabs, activeItemId }) : null
|
|
3406
|
+
overflow ? /* @__PURE__ */ jsx7(TabListMenu, { tabs, activeItemId, hidden }) : null
|
|
3306
3407
|
]
|
|
3307
3408
|
}
|
|
3308
3409
|
);
|
|
3309
3410
|
}
|
|
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 }) {
|
|
3411
|
+
function tabElement(list, itemId) {
|
|
3412
|
+
return [...list?.querySelectorAll("[data-mxc-tab]") ?? []].find((node) => node.dataset.mxcTab === itemId);
|
|
3413
|
+
}
|
|
3414
|
+
function TabListMenu({ tabs, activeItemId, hidden }) {
|
|
3325
3415
|
const canvas = useCanvas();
|
|
3326
3416
|
const { ports, layer } = useCanvasInternals();
|
|
3327
3417
|
const blocking = useRef7(null);
|
|
3328
3418
|
useEffect7(() => () => blocking.current?.(), []);
|
|
3329
3419
|
const label = `All tabs (${tabs.length})`;
|
|
3330
|
-
const trigger = (props) => /* @__PURE__ */
|
|
3420
|
+
const trigger = (props) => /* @__PURE__ */ jsxs6(CanvasHeaderButton, { className: "mxc-tab-list", label, ...props, children: [
|
|
3421
|
+
/* @__PURE__ */ jsx7("span", { className: "mxc-tab-list-count", "aria-hidden": "true", children: hidden }),
|
|
3422
|
+
/* @__PURE__ */ jsx7(ChevronDownIcon, {})
|
|
3423
|
+
] });
|
|
3331
3424
|
const showMenu = ports.showMenu;
|
|
3332
3425
|
if (showMenu) {
|
|
3333
3426
|
return trigger({
|
|
@@ -3445,10 +3538,9 @@ function PaneMenu({
|
|
|
3445
3538
|
return [
|
|
3446
3539
|
[...kindItems, ...bodyItems, ...hostItems].map((entry) => ({ ...entry, id: `item:${entry.id}` })),
|
|
3447
3540
|
[
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
] : [],
|
|
3541
|
+
// Only a split that leaves both panes usable (the layout rules' minimums) is offered.
|
|
3542
|
+
...canSplit && canvas.fitSplit(paneId, "horizontal") === "horizontal" ? [{ id: "split-right", label: "Split right", icon: /* @__PURE__ */ jsx7(SplitRightIcon, {}), onSelect: () => canvas.splitPane(paneId, "horizontal", moveId) }] : [],
|
|
3543
|
+
...canSplit && canvas.fitSplit(paneId, "vertical") === "vertical" ? [{ id: "split-down", label: "Split down", icon: /* @__PURE__ */ jsx7(SplitDownIcon, {}), onSelect: () => canvas.splitPane(paneId, "vertical", moveId) }] : [],
|
|
3452
3544
|
...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
3545
|
],
|
|
3454
3546
|
activeItem ? [
|
|
@@ -3557,6 +3649,15 @@ function CanvasColumn({ className, style, onLiveWidth, placement = "docked" }) {
|
|
|
3557
3649
|
const [slots] = useState6(createCanvasSlots);
|
|
3558
3650
|
const ref = useRef8(null);
|
|
3559
3651
|
useRegisterCanvasPresence();
|
|
3652
|
+
useEffect8(
|
|
3653
|
+
() => controller.registerPaneMeasure((paneId) => {
|
|
3654
|
+
const section = slots.section(paneId);
|
|
3655
|
+
if (!section?.isConnected) return null;
|
|
3656
|
+
const rect = section.getBoundingClientRect();
|
|
3657
|
+
return rect.width > 0 && rect.height > 0 ? { width: rect.width, height: rect.height } : null;
|
|
3658
|
+
}),
|
|
3659
|
+
[controller, slots]
|
|
3660
|
+
);
|
|
3560
3661
|
useEffect8(() => {
|
|
3561
3662
|
const el = ref.current;
|
|
3562
3663
|
if (!el) return void 0;
|
|
@@ -3678,28 +3779,42 @@ function LayoutNodeView({ node }) {
|
|
|
3678
3779
|
if (node.type === "pane") return /* @__PURE__ */ jsx8(CanvasPaneView, { paneId: node.paneId });
|
|
3679
3780
|
return /* @__PURE__ */ jsx8(SplitView, { node });
|
|
3680
3781
|
}
|
|
3681
|
-
function
|
|
3682
|
-
const [
|
|
3782
|
+
function useMeasuredSize(ref, enabled) {
|
|
3783
|
+
const [size, setSize] = useState6({ width: 0, height: 0 });
|
|
3683
3784
|
useIsoLayoutEffect5(() => {
|
|
3684
3785
|
const el = ref.current;
|
|
3685
3786
|
if (!enabled || !el) return;
|
|
3686
|
-
|
|
3787
|
+
const read = () => {
|
|
3788
|
+
const rect = el.getBoundingClientRect();
|
|
3789
|
+
setSize((prev) => prev.width === rect.width && prev.height === rect.height ? prev : { width: rect.width, height: rect.height });
|
|
3790
|
+
};
|
|
3791
|
+
read();
|
|
3687
3792
|
if (typeof ResizeObserver === "undefined") return;
|
|
3688
|
-
const observer = new ResizeObserver(
|
|
3793
|
+
const observer = new ResizeObserver(read);
|
|
3689
3794
|
observer.observe(el);
|
|
3690
3795
|
return () => observer.disconnect();
|
|
3691
3796
|
}, [enabled]);
|
|
3692
|
-
return
|
|
3797
|
+
return size;
|
|
3798
|
+
}
|
|
3799
|
+
function panesAlong(node, orientation) {
|
|
3800
|
+
if (node.type === "pane") return 1;
|
|
3801
|
+
const counts = node.children.map((child) => panesAlong(child, orientation));
|
|
3802
|
+
return node.orientation === orientation ? counts.reduce((a, b) => a + b, 0) : Math.max(...counts);
|
|
3803
|
+
}
|
|
3804
|
+
function childMinimums(node, orientation, rules) {
|
|
3805
|
+
const unit = orientation === "vertical" ? rules.paneMinHeight : rules.paneMinWidth;
|
|
3806
|
+
return node.children.map((child) => unit * panesAlong(child, orientation));
|
|
3693
3807
|
}
|
|
3694
3808
|
function SplitView({ node }) {
|
|
3695
3809
|
const canvas = useCanvas();
|
|
3696
3810
|
const ref = useRef8(null);
|
|
3697
3811
|
const [liveSizes, setLiveSizes] = useState6(null);
|
|
3698
|
-
const
|
|
3699
|
-
const
|
|
3700
|
-
const stacked = node.orientation === "horizontal" && shouldStackSplit(measuredWidth, node.children.length, canvas.rules);
|
|
3812
|
+
const measured = useMeasuredSize(ref, canvas.rules.paneMinWidth > 0 || canvas.rules.paneMinHeight > 0);
|
|
3813
|
+
const stacked = node.orientation === "horizontal" && shouldStackSplit(measured.width, node.children.length, canvas.rules);
|
|
3701
3814
|
const orientation = stacked ? "vertical" : node.orientation;
|
|
3702
3815
|
const horizontal = orientation === "horizontal";
|
|
3816
|
+
const mins = childMinimums(node, orientation, canvas.rules);
|
|
3817
|
+
const sizes = fitCanvasSplitSizes(liveSizes ?? node.sizes, horizontal ? measured.width : measured.height, mins);
|
|
3703
3818
|
const beginDrag = (index) => (event) => {
|
|
3704
3819
|
const el = ref.current;
|
|
3705
3820
|
if (!el) return;
|
|
@@ -3708,14 +3823,15 @@ function SplitView({ node }) {
|
|
|
3708
3823
|
const rect = el.getBoundingClientRect();
|
|
3709
3824
|
const total = horizontal ? rect.width : rect.height;
|
|
3710
3825
|
const origin = horizontal ? event.clientX : event.clientY;
|
|
3711
|
-
const base = [...
|
|
3826
|
+
const base = [...sizes];
|
|
3712
3827
|
const pair = (base[index] ?? 0) + (base[index + 1] ?? 0);
|
|
3828
|
+
const minFirst = Math.max(CANVAS_MIN_SPLIT_FRACTION, total > 0 ? (mins[index] ?? 0) / total : 0);
|
|
3829
|
+
const minSecond = Math.max(CANVAS_MIN_SPLIT_FRACTION, total > 0 ? (mins[index + 1] ?? 0) / total : 0);
|
|
3713
3830
|
const target = event.currentTarget;
|
|
3714
3831
|
const end = beginCanvasResize(horizontal ? "col" : "row");
|
|
3715
3832
|
const compute = (pos) => {
|
|
3716
3833
|
const delta = (pos - origin) / total;
|
|
3717
|
-
const
|
|
3718
|
-
const first = Math.min(pair - min, Math.max(min, (base[index] ?? 0) + delta));
|
|
3834
|
+
const first = Math.max(Math.min(pair - minSecond, Math.max(minFirst, (base[index] ?? 0) + delta)), Math.min(minFirst, pair / 2));
|
|
3719
3835
|
const next = [...base];
|
|
3720
3836
|
next[index] = first;
|
|
3721
3837
|
next[index + 1] = pair - first;
|
|
@@ -3830,6 +3946,7 @@ function CanvasFrame({ children, className }) {
|
|
|
3830
3946
|
export {
|
|
3831
3947
|
BrowserBar,
|
|
3832
3948
|
BrowserTabs,
|
|
3949
|
+
CANVAS_TAB_LIST_ROOM,
|
|
3833
3950
|
CANVAS_TAB_MIN_WIDTH,
|
|
3834
3951
|
CanvasColumn,
|
|
3835
3952
|
CanvasFrame,
|
|
@@ -3846,8 +3963,8 @@ export {
|
|
|
3846
3963
|
CanvasWindowTabs,
|
|
3847
3964
|
beginCanvasResize,
|
|
3848
3965
|
canvasSessionKey,
|
|
3849
|
-
canvasTabsOverflow,
|
|
3850
3966
|
defineCanvasKind,
|
|
3967
|
+
fitCanvasTabWindow,
|
|
3851
3968
|
getCanvasKind,
|
|
3852
3969
|
itemTitle,
|
|
3853
3970
|
listCanvasKinds,
|