tuiboard 0.9.0 → 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 +18 -0
- package/package.json +1 -1
- package/src/ui/Modal.tsx +39 -4
- package/src/views/Dashboard.tsx +10 -26
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.1] - 2026-09-08
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Modals were unreadable in single-pane.** With one zone filling the screen a
|
|
14
|
+
dialog floated over it as an absolute overlay — and had nothing to paint over,
|
|
15
|
+
because the theme leaves panel backgrounds transparent so the terminal shows
|
|
16
|
+
through. The keyboard reference interleaved with the pane character by
|
|
17
|
+
character; the new-task and edit dialogs were effectively invisible. A modal
|
|
18
|
+
now takes the pane's place, exactly as the four-zone layout drops it into the
|
|
19
|
+
Agenda's slot: with one zone on screen, that zone *is* the slot.
|
|
20
|
+
- **A dialog standing in for a pane now fills it**, in both axes, like the zone
|
|
21
|
+
it replaces — instead of keeping a slot-sized box while the rest of the strip
|
|
22
|
+
sits empty, which read as a window that had failed to open. In the four-zone
|
|
23
|
+
layout it still matches the Agenda's slot to the cell, or the whole dashboard
|
|
24
|
+
would shift when a modal opens; verified by the frame borders landing on
|
|
25
|
+
identical columns with and without a modal.
|
|
26
|
+
|
|
10
27
|
## [0.9.0] - 2026-09-08
|
|
11
28
|
|
|
12
29
|
### Added
|
|
@@ -273,6 +290,7 @@ First public release on npm. This entry captures the full feature set at launch.
|
|
|
273
290
|
|
|
274
291
|
Built with [OpenTUI](https://opentui.com) + SolidJS on Bun.
|
|
275
292
|
|
|
293
|
+
[0.9.1]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.9.1
|
|
276
294
|
[0.9.0]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.9.0
|
|
277
295
|
[0.8.5]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.8.5
|
|
278
296
|
[0.8.4]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.8.4
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tuiboard",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Terminal kanban for markdown task boards, with optional Today/Tomorrow planner, 24h agenda + calendar overlay, and a live Claude Code agent view. Use only the panels you want.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/ui/Modal.tsx
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* checks `ui.modal` first and bails if set (only Escape passes through).
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { For, Show, createMemo, createSignal } from "solid-js";
|
|
14
|
+
import { For, Show, createContext, createMemo, createSignal, useContext } from "solid-js";
|
|
15
15
|
|
|
16
16
|
import { isTask } from "~/parser/markdown";
|
|
17
17
|
import {
|
|
@@ -29,14 +29,23 @@ import type { PriorityLevel, TimeBlock } from "~/types";
|
|
|
29
29
|
* slot (the Dashboard renders it there while a modal is open) with no reflow. */
|
|
30
30
|
const MODAL_WIDTH = AGENDA_WIDTH;
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Whether the dialog is standing in for a whole pane (single-pane) or sitting
|
|
34
|
+
* in the Agenda's slot (four-zone). It decides how wide the dialog may be, and
|
|
35
|
+
* only ModalLayer is in a position to know.
|
|
36
|
+
*/
|
|
37
|
+
const SinglePaneContext = createContext<() => boolean>(() => false);
|
|
38
|
+
|
|
32
39
|
export function ModalLayer(props: { store: TuiStore }) {
|
|
33
40
|
const modal = createMemo(() => props.store.state.ui.modal);
|
|
34
41
|
return (
|
|
42
|
+
<SinglePaneContext.Provider value={() => props.store.singlePane()}>
|
|
35
43
|
<Show when={modal()}>
|
|
36
44
|
{/* Each modal's DialogShell IS the panel box (border + title + slot
|
|
37
45
|
dimensions), so it drops into the Agenda's slot directly. */}
|
|
38
46
|
<ModalRouter store={props.store} modal={modal()!} />
|
|
39
47
|
</Show>
|
|
48
|
+
</SinglePaneContext.Provider>
|
|
40
49
|
);
|
|
41
50
|
}
|
|
42
51
|
|
|
@@ -194,7 +203,30 @@ function BoardNewModal(props: { store: TuiStore }) {
|
|
|
194
203
|
);
|
|
195
204
|
}
|
|
196
205
|
|
|
206
|
+
/**
|
|
207
|
+
* How wide the dialog may be.
|
|
208
|
+
*
|
|
209
|
+
* In the four-zone layout it takes the Agenda's slot and must match it to the
|
|
210
|
+
* cell, or the whole dashboard shifts when a modal opens — that invariant is
|
|
211
|
+
* why the requested width used to be ignored outright. In single-pane the
|
|
212
|
+
* dialog takes a whole pane's place instead, so there it can use what the
|
|
213
|
+
* terminal actually offers: the keyboard reference asks for 92 columns and is
|
|
214
|
+
* unreadable squeezed into 50, while a 60-column strip needs it to shrink
|
|
215
|
+
* rather than overflow.
|
|
216
|
+
*/
|
|
217
|
+
function dialogWidth(singlePane: boolean): number {
|
|
218
|
+
if (!singlePane) return MODAL_WIDTH;
|
|
219
|
+
// Standing in for a pane means behaving like one: take the strip. A dialog
|
|
220
|
+
// that keeps its slot-sized box while the rest of the screen sits empty
|
|
221
|
+
// reads as a window that failed to open, not as a panel.
|
|
222
|
+
const terminal = process.stdout.columns ?? 80;
|
|
223
|
+
return Math.max(20, terminal - 4);
|
|
224
|
+
}
|
|
225
|
+
|
|
197
226
|
function DialogShell(props: DialogShellProps) {
|
|
227
|
+
const singlePane = useContext(SinglePaneContext);
|
|
228
|
+
const width = () => dialogWidth(singlePane());
|
|
229
|
+
// `width` survives as the four-zone hint it always was; single-pane fills.
|
|
198
230
|
void props.width;
|
|
199
231
|
return (
|
|
200
232
|
<box
|
|
@@ -204,9 +236,12 @@ function DialogShell(props: DialogShellProps) {
|
|
|
204
236
|
// slot at the exact same size and the dashboard doesn't shift when a
|
|
205
237
|
// modal opens. The title rides in the top border like the columns/zones.
|
|
206
238
|
flexDirection: "column",
|
|
207
|
-
width:
|
|
208
|
-
minWidth: MODAL_WIDTH,
|
|
209
|
-
|
|
239
|
+
width: width(),
|
|
240
|
+
minWidth: Math.min(MODAL_WIDTH, width()),
|
|
241
|
+
// In the Agenda's slot the box must not grow, or the dashboard shifts.
|
|
242
|
+
// Filling a pane, it must — in both axes, like the zone it replaces.
|
|
243
|
+
flexGrow: singlePane() ? 1 : 0,
|
|
244
|
+
height: singlePane() ? "100%" : undefined,
|
|
210
245
|
marginLeft: 1,
|
|
211
246
|
backgroundColor: T.panelBgActive,
|
|
212
247
|
border: true,
|
package/src/views/Dashboard.tsx
CHANGED
|
@@ -55,17 +55,19 @@ export function Dashboard(props: { store: TuiStore }) {
|
|
|
55
55
|
* zone of the normal layout and BoardOnly already respects ui.zoomed
|
|
56
56
|
* to render only the active panel between them.
|
|
57
57
|
*
|
|
58
|
-
* Modals
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
58
|
+
* Modals take the pane's place, exactly as the four-zone layout drops them
|
|
59
|
+
* into the Agenda's slot: with one zone filling the screen, that zone IS the
|
|
60
|
+
* slot. They used to float as an absolute overlay, which had nothing to paint
|
|
61
|
+
* over — the theme leaves panel backgrounds transparent so the terminal shows
|
|
62
|
+
* through — so the dialog's text interleaved with the pane underneath and both
|
|
63
|
+
* became unreadable. Closing returns to the pane exactly as it was.
|
|
62
64
|
*/
|
|
63
65
|
function ZoomedLayout(props: { store: TuiStore }) {
|
|
64
66
|
const ui = () => props.store.state.ui;
|
|
65
67
|
const zone = () => ui().activeZone;
|
|
66
68
|
|
|
67
69
|
return (
|
|
68
|
-
|
|
70
|
+
<Show when={ui().modal} fallback={
|
|
69
71
|
<Show when={zone() === "timeline"} fallback={
|
|
70
72
|
<Show when={zone() === "agents"} fallback={<BoardOnly store={props.store} />}>
|
|
71
73
|
<AgentsOnly store={props.store} />
|
|
@@ -73,27 +75,9 @@ function ZoomedLayout(props: { store: TuiStore }) {
|
|
|
73
75
|
}>
|
|
74
76
|
<TimelineOnly store={props.store} />
|
|
75
77
|
</Show>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<Show when={ui().modal}>
|
|
80
|
-
<box
|
|
81
|
-
style={{
|
|
82
|
-
position: "absolute",
|
|
83
|
-
top: 0,
|
|
84
|
-
left: 0,
|
|
85
|
-
right: 0,
|
|
86
|
-
bottom: 0,
|
|
87
|
-
zIndex: 100,
|
|
88
|
-
flexDirection: "column",
|
|
89
|
-
alignItems: "center",
|
|
90
|
-
justifyContent: "center",
|
|
91
|
-
}}
|
|
92
|
-
>
|
|
93
|
-
<ModalLayer store={props.store} />
|
|
94
|
-
</box>
|
|
95
|
-
</Show>
|
|
96
|
-
</>
|
|
78
|
+
}>
|
|
79
|
+
<ModalLayer store={props.store} />
|
|
80
|
+
</Show>
|
|
97
81
|
);
|
|
98
82
|
}
|
|
99
83
|
|