@ai-matrx/canvas 0.6.0 → 0.7.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 +30 -0
- package/README.md +17 -2
- package/dist/index.cjs +76 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.js +76 -7
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +441 -199
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +77 -44
- package/dist/react.d.ts +77 -44
- package/dist/react.js +396 -153
- package/dist/react.js.map +1 -1
- package/dist/styles.css +88 -37
- package/dist/{toggleKind-DhDmWExm.d.cts → toggleKind-CAI7pndj.d.cts} +21 -2
- package/dist/{toggleKind-DhDmWExm.d.ts → toggleKind-CAI7pndj.d.ts} +21 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
Second independent live sweep (admin, clone):
|
|
6
|
+
|
|
7
|
+
- **Splits never make unusable panes.** A 3-way split of a 640px canvas gave 154/154/308px panes whose headers overlapped. The web now has `paneMinWidth` = `CANVAS_PANE_MIN_WIDTH` (280px): a side-by-side split whose panes would be narrower renders STACKED (the layout tree is unchanged; a wider canvas gets the columns back).
|
|
8
|
+
- **Tabs: a name in full when there is room, an ellipsis when there is not, a menu past that** (Chrome / VS Code). Tabs shrink — the active tab least (`flex-shrink: 0.2`) — to a floor of icon + ~6 letters + "…" (`CANVAS_TAB_MIN_WIDTH` 78px; active `CANVAS_ACTIVE_TAB_MIN_WIDTH` 100px). A name really cut (`data-truncated`, measured >1px) ends in "…" (a lone tab's title too). Tabs that do not fit at their floor go into **"All tabs (N)"** (every tab by name, the current one marked; host-drawn through `showMenu` when the host draws menus); the active tab is always shown. At the narrowest the strip is the active tab's title and that menu. The strip no longer scrolls: the scroll chevrons (which sat over "+" and "⋮") are gone, the strip is `flex-basis: 0` + `overflow: hidden`, and nothing in the header overlaps another control. The wheel over an overflowing strip steps through tabs. Pure rule: `fitCanvasTabs(count, activeIndex, width)`.
|
|
9
|
+
- **Phones: one pane, the header always describes it.** The pane menu offers no Split on a phone. The presented pane is the focused one only when it holds a tab, else the first pane that does (`selectCanvasPresentedPaneId`) — header and body read the same pane (a split used to show "Quick Chat" over an empty launcher, with no Back).
|
|
10
|
+
- **A toggle never leaves an empty canvas.** `toggleKind` (header toggles, `CanvasKindIcons`) closing the canvas's LAST tab also puts the canvas away.
|
|
11
|
+
- **Edge drags select nothing.** The width handle and split handles run through `beginCanvasResize`: the page's selection is cleared and `selectstart` is refused for the drag (on top of `user-select: none`); a lost pointer capture ends the drag.
|
|
12
|
+
- **Forward.** `CanvasItem.forward` (JSON, persisted): Back pushes what it leaves; Forward (`controller.forward`, reducer `forward`, `selectCanvasCanGoForward`) returns the SAME live body Back left; a navigate elsewhere ends the trail. The header shows Forward beside Back only when there is somewhere to go. Shortcut data: `canvas.back` (⌘[) and `canvas.forward` (⌘]) — `web: false` (browsers own those keys).
|
|
13
|
+
- **One pane menu.** A body offers its own actions with `useCanvasMenuItems(itemId, items)`; they join the pane's one "⋮" menu after the kind's `menuItems` and before the host's `itemActions` and the pane actions (Split, Pop out, Close). Read when the menu opens. A body never draws a second overflow button in the header.
|
|
14
|
+
|
|
15
|
+
### Consumer action
|
|
16
|
+
|
|
17
|
+
- A kind body that portals its own overflow ("…") into the pane header: offer those actions through `useCanvasMenuItems` instead.
|
|
18
|
+
- A kind `HeaderAction` drawn with tap-target buttons: the pane header is a solid bar — use `TapTargetButtonTransparent` (the placement rules: glass only floats).
|
|
19
|
+
- CSS that targeted `.mxc-tabs-scroll` or the strip's fade: gone (the strip no longer scrolls).
|
|
20
|
+
|
|
21
|
+
## 0.6.1
|
|
22
|
+
|
|
23
|
+
Live measurement of 0.6.0 (a 756px pane, five tabs, Quick Chat's header actions present):
|
|
24
|
+
|
|
25
|
+
- **The strip takes the header's free width** (`flex: 1`). Sized to its own intrinsic width, the browser undercut every text tab by 2–3px and cut each name; now tabs lay out in real room ("+" sits at the strip's end).
|
|
26
|
+
- **No ellipsis.** System fonts overflow their intrinsic width by a fraction of a pixel, which put "…" on names that fit ("Quick Ch…"). Titles clip; a name really cut (`data-truncated` on the tab, measured: more than 1px) fades at its end.
|
|
27
|
+
- **Room for names:** only the active tab reserves its (always shown) close's room — a background tab's close overlays on hover with the name fading under it; icon gap 4px; an empty leading slot takes no room and no gap (the actions keep the end with `margin-left: auto`).
|
|
28
|
+
|
|
29
|
+
### Consumer action
|
|
30
|
+
|
|
31
|
+
- None.
|
|
32
|
+
|
|
3
33
|
## 0.6.0
|
|
4
34
|
|
|
5
35
|
Independent live sweep (admin, clone):
|
package/README.md
CHANGED
|
@@ -58,15 +58,30 @@ Every pane header has "+" (a launcher tab). Choosing in the launcher NAVIGATES t
|
|
|
58
58
|
```ts
|
|
59
59
|
canvas.navigate(item.id, { kind: "doc", key: docId, data: { docId } }); // records history; Back returns
|
|
60
60
|
canvas.back(item.id);
|
|
61
|
+
canvas.forward(item.id); // after Back: the same live body Back left
|
|
61
62
|
```
|
|
62
63
|
|
|
63
64
|
Navigating to something already open elsewhere focuses it there (a launcher tab closes; any other tab keeps what it shows). History is plain JSON and persists. A lone tab shown as a title keeps its own close.
|
|
64
65
|
|
|
65
66
|
**Back never throws a body away.** The newest `CANVAS_KEEP_ALIVE_DEPTH` (5) steps Back can reach, and the 5 most recent contents Back stepped away from (`item.kept`), keep their bodies mounted and hidden. Back returns the SAME live body; choosing a content Back left (Quick Chat again from the launcher) revives its body — a typed draft is still there. Older steps still work but mount fresh. Closing the tab disposes every kept body.
|
|
66
67
|
|
|
67
|
-
**
|
|
68
|
+
**Forward.** After Back the header also shows Forward (`selectCanvasCanGoForward`); it returns the same live body Back left. A navigate somewhere new ends the forward trail. Shortcut data `canvas.back` / `canvas.forward` (⌘[ / ⌘]) is for a desktop host; browsers own those keys.
|
|
68
69
|
|
|
69
|
-
**
|
|
70
|
+
**The strip.** A tab is as wide as its name (up to 12rem). Short of room, tabs shrink — the active tab least — to icon + ~6 letters + "…" (`CANVAS_TAB_MIN_WIDTH`; the active tab keeps `CANVAS_ACTIVE_TAB_MIN_WIDTH`); a name really cut ends in an ellipsis and every tab's tooltip is its full name. Tabs that do not fit at their floor are reached from "All tabs (N)"; the active tab is always shown. At the narrowest the strip is the active tab's title and that menu. Nothing scrolls and nothing in the header overlaps another control (`fitCanvasTabs` is the rule).
|
|
71
|
+
|
|
72
|
+
**Splits.** A side-by-side split whose panes would be narrower than `paneMinWidth` (web: `CANVAS_PANE_MIN_WIDTH`, 280px) renders stacked; the tree is unchanged.
|
|
73
|
+
|
|
74
|
+
**Phones** (`CANVAS_PHONE_MAX_WIDTH`, 767px): the column presents ONE pane — the focused one when it holds a tab, else the first that does (`selectCanvasPresentedPaneId`) — whose strip carries every pane's tabs; choosing a tab from another pane presents that pane. The pane menu offers no Split. The layout tree is untouched, so a wider screen gets its split back.
|
|
75
|
+
|
|
76
|
+
## One pane menu
|
|
77
|
+
|
|
78
|
+
A pane has ONE menu: the kind's `menuItems`, then what the body offers right now, then the host's `itemActions`, then Split / Pop out / Close. A body adds its own actions — never a second overflow button in the header:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
useCanvasMenuItems(item.id, [{ id: "next", label: "Next record", onSelect: goNext }]);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
A kind's `HeaderAction` sits on the solid pane header: draw tap-target buttons with `TapTargetButtonTransparent` (glass only floats).
|
|
70
85
|
|
|
71
86
|
## A page's own launcher entries
|
|
72
87
|
|
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ __export(src_exports, {
|
|
|
30
30
|
CANVAS_MIN_SPLIT_FRACTION: () => CANVAS_MIN_SPLIT_FRACTION,
|
|
31
31
|
CANVAS_MIN_WIDTH: () => CANVAS_MIN_WIDTH,
|
|
32
32
|
CANVAS_NARROW_WIDTH: () => CANVAS_NARROW_WIDTH,
|
|
33
|
+
CANVAS_PANE_MIN_WIDTH: () => CANVAS_PANE_MIN_WIDTH,
|
|
33
34
|
CANVAS_PHONE_MAX_WIDTH: () => CANVAS_PHONE_MAX_WIDTH,
|
|
34
35
|
CANVAS_SHORTCUTS: () => CANVAS_SHORTCUTS,
|
|
35
36
|
CANVAS_STORAGE_KEY: () => CANVAS_STORAGE_KEY,
|
|
@@ -70,6 +71,7 @@ __export(src_exports, {
|
|
|
70
71
|
sanitizeCanvasSnapshot: () => sanitizeCanvasSnapshot,
|
|
71
72
|
selectCanvasActiveItem: () => selectCanvasActiveItem,
|
|
72
73
|
selectCanvasCanGoBack: () => selectCanvasCanGoBack,
|
|
74
|
+
selectCanvasCanGoForward: () => selectCanvasCanGoForward,
|
|
73
75
|
selectCanvasExtraWindow: () => selectCanvasExtraWindow,
|
|
74
76
|
selectCanvasFocusedPaneId: () => selectCanvasFocusedPaneId,
|
|
75
77
|
selectCanvasIsFullscreen: () => selectCanvasIsFullscreen,
|
|
@@ -82,6 +84,7 @@ __export(src_exports, {
|
|
|
82
84
|
selectCanvasLayout: () => selectCanvasLayout,
|
|
83
85
|
selectCanvasPane: () => selectCanvasPane,
|
|
84
86
|
selectCanvasPaneCount: () => selectCanvasPaneCount,
|
|
87
|
+
selectCanvasPresentedPaneId: () => selectCanvasPresentedPaneId,
|
|
85
88
|
selectCanvasWidth: () => selectCanvasWidth,
|
|
86
89
|
selectCanvasWindowItemCount: () => selectCanvasWindowItemCount,
|
|
87
90
|
selectCanvasWindowOfItem: () => selectCanvasWindowOfItem,
|
|
@@ -267,6 +270,8 @@ var canvasActions = {
|
|
|
267
270
|
navigate: (itemId, to) => ({ type: `${P}navigate`, payload: { itemId, to, now: Date.now() } }),
|
|
268
271
|
/** Returns a tab to what it showed before its last navigate. */
|
|
269
272
|
back: (itemId) => ({ type: `${P}back`, payload: { itemId, now: Date.now() } }),
|
|
273
|
+
/** Re-does the last Back (the step Back left), with that step's live body. */
|
|
274
|
+
forward: (itemId) => ({ type: `${P}forward`, payload: { itemId, now: Date.now() } }),
|
|
270
275
|
closeItem: (itemId) => ({ type: `${P}closeItem`, payload: { itemId } }),
|
|
271
276
|
closeOthers: (itemId) => ({ type: `${P}closeOthers`, payload: { itemId } }),
|
|
272
277
|
activate: (itemId) => ({ type: `${P}activate`, payload: { itemId } }),
|
|
@@ -467,6 +472,7 @@ function newItem(kind, key, title, data, now, steps) {
|
|
|
467
472
|
const id = canvasItemId(kind, key);
|
|
468
473
|
const history = steps?.history ?? [];
|
|
469
474
|
const kept = steps?.kept ?? [];
|
|
475
|
+
const forward = steps?.forward ?? [];
|
|
470
476
|
return {
|
|
471
477
|
id,
|
|
472
478
|
kind,
|
|
@@ -478,7 +484,8 @@ function newItem(kind, key, title, data, now, steps) {
|
|
|
478
484
|
// A reused instance is the SAME live body coming back (Back, or returning to a kept step).
|
|
479
485
|
instance: steps?.instance ?? `${now.toString(36)}:${id}`,
|
|
480
486
|
...history.length > 0 ? { history: history.slice(-CANVAS_HISTORY_LIMIT) } : {},
|
|
481
|
-
...kept.length > 0 ? { kept: kept.slice(-CANVAS_KEEP_ALIVE_DEPTH) } : {}
|
|
487
|
+
...kept.length > 0 ? { kept: kept.slice(-CANVAS_KEEP_ALIVE_DEPTH) } : {},
|
|
488
|
+
...forward.length > 0 ? { forward: forward.slice(-CANVAS_HISTORY_LIMIT) } : {}
|
|
482
489
|
};
|
|
483
490
|
}
|
|
484
491
|
function historyEntry(item) {
|
|
@@ -524,12 +531,14 @@ function sanitizeHistory(raw) {
|
|
|
524
531
|
function sanitizeItem(item) {
|
|
525
532
|
const history = sanitizeHistory(item.history);
|
|
526
533
|
const kept = sanitizeHistory(item.kept).slice(-CANVAS_KEEP_ALIVE_DEPTH);
|
|
527
|
-
const
|
|
534
|
+
const forward = sanitizeHistory(item.forward);
|
|
535
|
+
const { history: _history, kept: _kept, forward: _forward, instance, ...rest } = item;
|
|
528
536
|
return {
|
|
529
537
|
...rest,
|
|
530
538
|
...typeof instance === "string" && instance ? { instance } : {},
|
|
531
539
|
...history.length > 0 ? { history } : {},
|
|
532
|
-
...kept.length > 0 ? { kept } : {}
|
|
540
|
+
...kept.length > 0 ? { kept } : {},
|
|
541
|
+
...forward.length > 0 ? { forward } : {}
|
|
533
542
|
};
|
|
534
543
|
}
|
|
535
544
|
function clampWidth(width) {
|
|
@@ -720,6 +729,9 @@ function canvasReducer(state = createInitialCanvasState(), action) {
|
|
|
720
729
|
const keptIndex = keptAll.findIndex((entry) => canvasItemId(entry.kind, entry.key) === targetId);
|
|
721
730
|
const revived = keptIndex >= 0 ? keptAll[keptIndex] : void 0;
|
|
722
731
|
const kept = keptAll.filter((_, i) => i !== keptIndex);
|
|
732
|
+
const forwardAll = current.forward ?? [];
|
|
733
|
+
const nextForward = forwardAll[forwardAll.length - 1];
|
|
734
|
+
const forward = nextForward && canvasItemId(nextForward.kind, nextForward.key) === targetId ? forwardAll.slice(0, -1) : [];
|
|
723
735
|
return replaceInPlace(
|
|
724
736
|
state,
|
|
725
737
|
current,
|
|
@@ -729,7 +741,7 @@ function canvasReducer(state = createInitialCanvasState(), action) {
|
|
|
729
741
|
to.title !== void 0 ? to.title : revived?.title ?? null,
|
|
730
742
|
to.data !== void 0 ? to.data : revived?.data ?? null,
|
|
731
743
|
now,
|
|
732
|
-
{ history, kept, instance: revived?.instance }
|
|
744
|
+
{ history, kept, forward, instance: revived?.instance }
|
|
733
745
|
)
|
|
734
746
|
);
|
|
735
747
|
}
|
|
@@ -745,10 +757,36 @@ function canvasReducer(state = createInitialCanvasState(), action) {
|
|
|
745
757
|
return focusExisting(canvasReducer(state, canvasActions.closeItem(itemId)), targetId);
|
|
746
758
|
}
|
|
747
759
|
const kept = [...(current.kept ?? []).filter((k) => canvasItemId(k.kind, k.key) !== itemId), historyEntry(current)];
|
|
760
|
+
const forward = [...current.forward ?? [], historyEntry(current)];
|
|
748
761
|
return replaceInPlace(
|
|
749
762
|
state,
|
|
750
763
|
current,
|
|
751
|
-
newItem(entry.kind, entry.key, entry.title, entry.data, now, { history: history.slice(0, -1), kept, instance: entry.instance })
|
|
764
|
+
newItem(entry.kind, entry.key, entry.title, entry.data, now, { history: history.slice(0, -1), kept, forward, instance: entry.instance })
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
case `${P}forward`: {
|
|
768
|
+
const { itemId, now } = action.payload;
|
|
769
|
+
const current = state.items[itemId];
|
|
770
|
+
const forwardAll = current?.forward ?? [];
|
|
771
|
+
const entry = forwardAll[forwardAll.length - 1];
|
|
772
|
+
if (!current || !entry || !paneOf(state, itemId)) return state;
|
|
773
|
+
const targetId = canvasItemId(entry.kind, entry.key);
|
|
774
|
+
if (targetId === itemId) return state;
|
|
775
|
+
if (state.items[targetId]) {
|
|
776
|
+
return focusExisting(canvasReducer(state, canvasActions.closeItem(itemId)), targetId);
|
|
777
|
+
}
|
|
778
|
+
const keptAll = current.kept ?? [];
|
|
779
|
+
const revived = keptAll.find((k) => canvasItemId(k.kind, k.key) === targetId);
|
|
780
|
+
const kept = keptAll.filter((k) => canvasItemId(k.kind, k.key) !== targetId);
|
|
781
|
+
return replaceInPlace(
|
|
782
|
+
state,
|
|
783
|
+
current,
|
|
784
|
+
newItem(entry.kind, entry.key, entry.title, entry.data, now, {
|
|
785
|
+
history: [...current.history ?? [], historyEntry(current)],
|
|
786
|
+
kept,
|
|
787
|
+
forward: forwardAll.slice(0, -1),
|
|
788
|
+
instance: revived?.instance ?? entry.instance
|
|
789
|
+
})
|
|
752
790
|
);
|
|
753
791
|
}
|
|
754
792
|
case `${P}closeItem`: {
|
|
@@ -1077,6 +1115,7 @@ function toPersistableSnapshot(state, isRestorable) {
|
|
|
1077
1115
|
}
|
|
1078
1116
|
|
|
1079
1117
|
// src/core/rules.ts
|
|
1118
|
+
var CANVAS_PANE_MIN_WIDTH = 280;
|
|
1080
1119
|
var CANVAS_WEB_LAYOUT_RULES = {
|
|
1081
1120
|
defaultWidth: CANVAS_DEFAULT_WIDTH,
|
|
1082
1121
|
minWidth: CANVAS_MIN_WIDTH,
|
|
@@ -1084,7 +1123,9 @@ var CANVAS_WEB_LAYOUT_RULES = {
|
|
|
1084
1123
|
// Split down 70/30 like the desktop: what opens below (notifications, a feed) is the smaller pane.
|
|
1085
1124
|
stackSplit: 0.7,
|
|
1086
1125
|
sideSplit: 0.5,
|
|
1087
|
-
|
|
1126
|
+
// A pane narrower than this cannot show a header (Back, a readable tab, "+", the menu, close) and a
|
|
1127
|
+
// body side by side with another: a split right that would go under it stacks instead.
|
|
1128
|
+
paneMinWidth: CANVAS_PANE_MIN_WIDTH
|
|
1088
1129
|
};
|
|
1089
1130
|
var CANVAS_DESKTOP_LAYOUT_RULES = {
|
|
1090
1131
|
defaultWidth: { fraction: 0.36 },
|
|
@@ -1131,6 +1172,9 @@ var CANVAS_SHORTCUTS = [
|
|
|
1131
1172
|
{ id: "canvas.toggleFullscreen", accelerator: "CmdOrCtrl+Shift+Enter", when: "canvas-focus", label: "Expand canvas", web: true },
|
|
1132
1173
|
{ id: "canvas.splitRight", accelerator: "CmdOrCtrl+Alt+\\", when: "canvas-focus", label: "Split right", web: true },
|
|
1133
1174
|
{ id: "canvas.splitDown", accelerator: "CmdOrCtrl+Alt+Shift+\\", when: "canvas-focus", label: "Split down", web: true },
|
|
1175
|
+
// Browsers own ⌘[ / ⌘] for their own history: data for a desktop host (and the header's Back / Forward).
|
|
1176
|
+
{ id: "canvas.back", accelerator: "CmdOrCtrl+[", when: "canvas-focus", label: "Back", web: false },
|
|
1177
|
+
{ id: "canvas.forward", accelerator: "CmdOrCtrl+]", when: "canvas-focus", label: "Forward", web: false },
|
|
1134
1178
|
{ id: "canvas.closeTab", accelerator: "CmdOrCtrl+W", when: "canvas-focus", label: "Close tab", web: false },
|
|
1135
1179
|
{ id: "canvas.nextTab", accelerator: "Ctrl+Tab", when: "canvas-focus", label: "Next tab", web: false },
|
|
1136
1180
|
{ id: "canvas.previousTab", accelerator: "Ctrl+Shift+Tab", when: "canvas-focus", label: "Previous tab", web: false }
|
|
@@ -1180,11 +1224,17 @@ function runCanvasCommand(controller, id, windowId = controller.windowId) {
|
|
|
1180
1224
|
if (!frame2?.isOpen || !pane) return false;
|
|
1181
1225
|
controller.splitPane(pane.id, id === "canvas.splitRight" ? "horizontal" : "vertical", pane.itemIds.length > 1 ? pane.activeItemId ?? void 0 : void 0);
|
|
1182
1226
|
return true;
|
|
1227
|
+
case "canvas.back":
|
|
1228
|
+
if (!frame2?.isOpen || !pane?.activeItemId) return false;
|
|
1229
|
+
return controller.back(pane.activeItemId) !== null;
|
|
1230
|
+
case "canvas.forward":
|
|
1231
|
+
if (!frame2?.isOpen || !pane?.activeItemId) return false;
|
|
1232
|
+
return controller.forward(pane.activeItemId) !== null;
|
|
1183
1233
|
default:
|
|
1184
1234
|
return false;
|
|
1185
1235
|
}
|
|
1186
1236
|
}
|
|
1187
|
-
var CODE_FOR = { "\\": "Backslash", Tab: "Tab", Escape: "Escape", Enter: "Enter" };
|
|
1237
|
+
var CODE_FOR = { "\\": "Backslash", "[": "BracketLeft", "]": "BracketRight", Tab: "Tab", Escape: "Escape", Enter: "Enter" };
|
|
1188
1238
|
function matchesCanvasAccelerator(input, accelerator, isMac) {
|
|
1189
1239
|
const parts = accelerator.split("+");
|
|
1190
1240
|
const key = parts.pop() ?? "";
|
|
@@ -1301,6 +1351,13 @@ function view(shared, windowId) {
|
|
|
1301
1351
|
dispatch(canvasActions.back(itemId));
|
|
1302
1352
|
return canvasItemId(entry.kind, entry.key);
|
|
1303
1353
|
},
|
|
1354
|
+
forward(itemId) {
|
|
1355
|
+
const forward = store.getState().items[itemId]?.forward ?? [];
|
|
1356
|
+
const entry = forward[forward.length - 1];
|
|
1357
|
+
if (!entry) return null;
|
|
1358
|
+
dispatch(canvasActions.forward(itemId));
|
|
1359
|
+
return canvasItemId(entry.kind, entry.key);
|
|
1360
|
+
},
|
|
1304
1361
|
newTab(paneId) {
|
|
1305
1362
|
const state = store.getState();
|
|
1306
1363
|
const frame2 = canvasWindowFrame(state, windowId);
|
|
@@ -1584,6 +1641,16 @@ function selectCanvasItemHistory(s, itemId) {
|
|
|
1584
1641
|
return s.items[itemId]?.history ?? NO_HISTORY;
|
|
1585
1642
|
}
|
|
1586
1643
|
var selectCanvasCanGoBack = (s, itemId) => (s.items[itemId]?.history?.length ?? 0) > 0;
|
|
1644
|
+
var selectCanvasCanGoForward = (s, itemId) => (s.items[itemId]?.forward?.length ?? 0) > 0;
|
|
1645
|
+
function selectCanvasPresentedPaneId(s, windowId) {
|
|
1646
|
+
const f = frame(s, windowId);
|
|
1647
|
+
if (!f) return null;
|
|
1648
|
+
const ids = listPaneIds(f.layout);
|
|
1649
|
+
const holds = (id) => (s.panes[id]?.itemIds.length ?? 0) > 0;
|
|
1650
|
+
const focused = ids.find((id) => id === f.focusedPaneId);
|
|
1651
|
+
if (focused && holds(focused)) return focused;
|
|
1652
|
+
return ids.find(holds) ?? focused ?? ids[0] ?? null;
|
|
1653
|
+
}
|
|
1587
1654
|
|
|
1588
1655
|
// src/core/presentation.ts
|
|
1589
1656
|
var CANVAS_PHONE_MAX_WIDTH = 767;
|
|
@@ -1633,7 +1700,9 @@ function toggleKind(controller, input) {
|
|
|
1633
1700
|
const before = selectCanvasKindVisibility(controller.getState(), input.kind, key);
|
|
1634
1701
|
if (before === "visible") {
|
|
1635
1702
|
if ((input.whenVisible ?? "close") === "close") {
|
|
1703
|
+
const windowId = canvasWindowOfItem(controller.getState(), itemId);
|
|
1636
1704
|
controller.close(itemId);
|
|
1705
|
+
if (windowId && selectCanvasWindowItemCount(controller.getState(), windowId) === 0) controller.hide(windowId);
|
|
1637
1706
|
} else {
|
|
1638
1707
|
controller.hide(canvasWindowOfItem(controller.getState(), itemId) ?? void 0);
|
|
1639
1708
|
}
|