tuiboard 0.7.2 → 0.7.3

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 CHANGED
@@ -5,6 +5,21 @@ All notable changes to **tuiboard** are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.7.3] - 2026-06-02
9
+
10
+ ### Fixed
11
+ - Opening a modal no longer shifts the whole dashboard up by a row. The Agenda's
12
+ tall scrollbox was inflating the main row one line past the terminal (flex
13
+ basis `auto` takes the content height); a modal in its place removed that
14
+ overflow, which read as a jump. The main row now grows purely from the
15
+ available space (`flex-basis: 0`), so the layout is steady whatever's on
16
+ screen. Modals also render in the Agenda's exact slot, so there's no
17
+ horizontal reflow either.
18
+
19
+ ### Changed
20
+ - Reclaimed a row: the board and agenda are one row taller, and the keyboard
21
+ cheat-sheet sits flush on the bottom line.
22
+
8
23
  ## [0.7.2] - 2026-06-02
9
24
 
10
25
  ### Changed
@@ -98,6 +113,7 @@ First public release on npm. This entry captures the full feature set at launch.
98
113
 
99
114
  Built with [OpenTUI](https://opentui.com) + SolidJS on Bun.
100
115
 
116
+ [0.7.3]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.7.3
101
117
  [0.7.2]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.7.2
102
118
  [0.7.1]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.7.1
103
119
  [0.7.0]: https://github.com/NazzarenoGiannelli/tuiboard/releases/tag/v0.7.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tuiboard",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
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/app.tsx CHANGED
@@ -115,23 +115,35 @@ function App() {
115
115
  width: "100%",
116
116
  height: "100%",
117
117
  backgroundColor: T.bg,
118
- padding: 1,
118
+ // No bottom padding: the keyboard cheat-sheet sits flush on the last
119
+ // row, which gives the board + agenda one extra row of height. Safe
120
+ // thanks to the flexBasis:0 main row — the content just fills the space.
121
+ paddingTop: 1,
122
+ paddingLeft: 1,
123
+ paddingRight: 1,
124
+ paddingBottom: 0,
119
125
  }}
120
126
  >
121
127
  <TopBar store={store} />
122
128
  <box style={{ height: 1 }} />
123
129
  {/*
124
- rootView + ModalLayer are flex-row siblings. The modal panel is exactly
125
- the Agenda's width, and the Dashboard hides the Agenda while a modal is
126
- open so the modal drops into the Agenda's slot with zero reflow of the
127
- left side (board / planner / agents). When no modal is open ModalLayer
128
- renders nothing and rootView gets the whole row.
130
+ The default Dashboard renders its modal inside the Agenda's slot, so the
131
+ layout stays put when a modal opens. The standalone `--view=` modes have
132
+ no Agenda slot, so they get the modal here as a right-hand panel.
133
+ (`view` is fixed at launch, never reactive.)
134
+
135
+ `flexBasis: 0` + `minHeight: 0` are load-bearing: without them the row's
136
+ flex-basis is `auto`, so the Agenda's tall scrollbox inflates the row to
137
+ its content height (1 row past the terminal). That overflow vanishes
138
+ when a modal (no scrollbox) takes the slot — which is exactly the 1-row
139
+ "shift" the whole bottom strip used to do. Basis 0 makes the row grow
140
+ purely from the available space, ignoring content, so it's stable.
129
141
  */}
130
- <box style={{ flexDirection: "row", flexGrow: 1 }}>
142
+ <box style={{ flexDirection: "row", flexGrow: 1, flexBasis: 0, minHeight: 0 }}>
131
143
  <box style={{ flexDirection: "column", flexGrow: 1 }}>
132
144
  {rootViewFor(view, store)}
133
145
  </box>
134
- <ModalLayer store={store} />
146
+ {view ? <ModalLayer store={store} /> : null}
135
147
  </box>
136
148
  <BottomBar store={store} />
137
149
  </box>
package/src/ui/Modal.tsx CHANGED
@@ -20,13 +20,13 @@ import {
20
20
  parseTimeBlockShortcut,
21
21
  } from "~/store/parsers";
22
22
  import { ATTR, T } from "~/ui/glyphs";
23
- import { TIMELINE_WIDTH } from "~/views/Dashboard";
23
+ import { AGENDA_WIDTH } from "~/ui/layout";
24
24
  import type { TuiStore } from "~/store/index";
25
25
  import type { PriorityLevel, TimeBlock } from "~/types";
26
26
 
27
27
  /** The modal panel matches the Agenda's width so it can drop into the Agenda's
28
- * slot (which the Dashboard vacates while a modal is open) with no reflow. */
29
- const MODAL_WIDTH = TIMELINE_WIDTH;
28
+ * slot (the Dashboard renders it there while a modal is open) with no reflow. */
29
+ const MODAL_WIDTH = AGENDA_WIDTH;
30
30
 
31
31
  export function ModalLayer(props: { store: TuiStore }) {
32
32
  const modal = createMemo(() => props.store.state.ui.modal);
@@ -69,16 +69,14 @@ function DialogShell(props: DialogShellProps) {
69
69
  return (
70
70
  <box
71
71
  style={{
72
- // The modal panel: same width + marginLeft as the Agenda so it occupies
73
- // the Agenda's slot (the Dashboard hides the Agenda while a modal is
74
- // open) with no reflow. The title rides in the top border, exactly like
75
- // the board columns and the other zones.
72
+ // Byte-identical layout to the Agenda panel (TimelineView): same width,
73
+ // marginLeft, flexGrow, border and padding, so it occupies the Agenda's
74
+ // slot at the exact same size and the dashboard doesn't shift when a
75
+ // modal opens. The title rides in the top border like the columns/zones.
76
76
  flexDirection: "column",
77
77
  width: MODAL_WIDTH,
78
78
  minWidth: MODAL_WIDTH,
79
79
  flexGrow: 0,
80
- flexShrink: 0,
81
- alignSelf: "stretch",
82
80
  marginLeft: 1,
83
81
  backgroundColor: T.panelBgActive,
84
82
  border: true,
@@ -86,8 +84,6 @@ function DialogShell(props: DialogShellProps) {
86
84
  borderColor: T.borderActive,
87
85
  paddingLeft: 1,
88
86
  paddingRight: 1,
89
- paddingTop: 1,
90
- paddingBottom: 1,
91
87
  }}
92
88
  title={`┤ ${props.title} ├`}
93
89
  titleAlignment="left"
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Shared dashboard layout constants. Kept in a leaf module so both the
3
+ * Dashboard and the Modal can import them without an import cycle (the modal
4
+ * panel matches the Agenda's width so it can drop into the Agenda's slot).
5
+ */
6
+
7
+ /** Width (in cells) of the right-hand Agenda panel — and of the modal panel,
8
+ * which takes the Agenda's slot while a modal is open. */
9
+ export const AGENDA_WIDTH = 50;
10
+
11
+ /** Row height of the bottom Agents strip — enough for ~5 sessions. */
12
+ export const AGENTS_HEIGHT = 7;
@@ -25,19 +25,13 @@ import { AgentsBar } from "~/ui/AgentsBar";
25
25
  import { BoardView } from "~/ui/BoardView";
26
26
  import { TimelineView } from "~/ui/TimelineView";
27
27
  import { PlannerPanel } from "~/ui/PlannerPanel";
28
+ import { ModalLayer } from "~/ui/Modal";
29
+ import { AGENDA_WIDTH, AGENTS_HEIGHT } from "~/ui/layout";
28
30
  import { AgentsOnly } from "~/views/AgentsOnly";
29
31
  import { BoardOnly } from "~/views/BoardOnly";
30
32
  import { TimelineOnly } from "~/views/TimelineOnly";
31
33
  import type { TuiStore } from "~/store/index";
32
34
 
33
- /**
34
- * Width (in cells) for the right-column Agenda panel on a wide terminal. The
35
- * modal panel matches this so it can drop into the Agenda's slot (see
36
- * ModalLayer) without reflowing the rest of the dashboard.
37
- */
38
- export const TIMELINE_WIDTH = 50;
39
- /** Row height for the bottom Agents strip — enough for ~5 sessions. */
40
- const AGENTS_HEIGHT = 7;
41
35
 
42
36
  export function Dashboard(props: { store: TuiStore }) {
43
37
  const ui = () => props.store.state.ui;
@@ -100,10 +94,19 @@ function FourZoneLayout(props: { store: TuiStore }) {
100
94
  <AgentsBar store={props.store} height={AGENTS_HEIGHT} />
101
95
  </Show>
102
96
  </box>
103
- {/* Right column: Agenda (full height). Hidden while a modal is open so
104
- the modal panel takes its slot with no reflow. */}
105
- <Show when={visible().timeline && !ui().modal}>
106
- <TimelineView store={props.store} width={TIMELINE_WIDTH} />
97
+ {/* Right column: the Agenda or, while a modal is open, the modal panel
98
+ in the Agenda's exact slot (same parent, same width). Swapping them in
99
+ place keeps the whole layout and every height constant; nothing
100
+ shifts. The modal still appears here even if the Agenda is disabled. */}
101
+ <Show
102
+ when={ui().modal}
103
+ fallback={
104
+ <Show when={visible().timeline}>
105
+ <TimelineView store={props.store} width={AGENDA_WIDTH} />
106
+ </Show>
107
+ }
108
+ >
109
+ <ModalLayer store={props.store} />
107
110
  </Show>
108
111
  {/* ModalLayer rendered at App level so it can sit beside any view */}
109
112
  </box>