@zinn-dev/opencode 0.1.0 → 0.2.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/README.md CHANGED
@@ -7,7 +7,7 @@ Requires OpenCode 2.2.0 and Zinn CLI 0.41.0 or newer on `PATH`.
7
7
  Add this entry to the `plugins` array in `~/.config/opencode/cli.json`:
8
8
 
9
9
  ```json
10
- { "package": "@zinn-dev/opencode@0.1.0", "options": { "rows": 2, "current": "glyph" } }
10
+ { "package": "@zinn-dev/opencode@0.2.0", "options": { "rows": 2, "current": "glyph" } }
11
11
  ```
12
12
 
13
13
  Read the [setup, options, compatibility, update and removal guide](https://zinn.sh/integrations/opencode/inspector).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zinn-dev/opencode",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "Experimental Zinn task inspector for the OpenCode TUI.",
6
6
  "keywords": [
@@ -15,7 +15,7 @@ import {
15
15
  PRIORITY_COLOR,
16
16
  labelColors,
17
17
  } from "./data.ts";
18
- import { MARKER_COLUMNS, RAIL_COLUMNS, SESSION_GLYPH, priorityLetter } from "./options.ts";
18
+ import { RAIL_COLUMNS, SESSION_GLYPH, priorityLetter } from "./options.ts";
19
19
  import { truncate } from "./rows.ts";
20
20
  import { TINT_ALPHA_STRONG, TINT_ALPHA_SUBTLE, inspectorTheme, toneColor } from "./theme.ts";
21
21
 
@@ -38,16 +38,39 @@ export function Overflow(props: {
38
38
  return (
39
39
  <box
40
40
  flexDirection="row"
41
+ gap={1}
41
42
  backgroundColor={hover() ? theme().backgroundElement : "transparent"}
42
43
  onMouseOver={() => setHover(true)}
43
44
  onMouseOut={() => setHover(false)}
44
45
  onMouseUp={onClick}
45
46
  >
47
+ <text width={RAIL_COLUMNS} flexShrink={0} fg={theme().borderSubtle}>
48
+ │
49
+ </text>
46
50
  <text flexShrink={0} fg={theme().textMuted}>{`+${props.count} ${props.label}`}</text>
47
51
  </box>
48
52
  );
49
53
  }
50
54
 
55
+ export function RailLine(props: {
56
+ ctx: Ctx;
57
+ glyph: string;
58
+ color?: RGBA;
59
+ onMouseDown?: () => void;
60
+ children: JSX.Element;
61
+ }): JSX.Element {
62
+ const theme = () => inspectorTheme(props.ctx);
63
+
64
+ return (
65
+ <box flexDirection="row" gap={1} onMouseDown={props.onMouseDown}>
66
+ <text width={RAIL_COLUMNS} flexShrink={0} fg={props.color ?? theme().borderSubtle}>
67
+ {props.glyph}
68
+ </text>
69
+ {props.children}
70
+ </box>
71
+ );
72
+ }
73
+
51
74
  type MetaPart = {
52
75
  text: string;
53
76
  color: string | null;
@@ -69,15 +92,54 @@ export function Row(props: {
69
92
  const keyColor = toneColor(props.ctx, () => props.row.tone);
70
93
  const metaColor = () => theme().textMuted;
71
94
  const glyph = () => props.current === "glyph";
72
- const markerColumns = () => {
95
+ const done = () => props.row.column === "Done";
96
+ const mark = () => {
97
+ if (glyph() && props.row.mine) {
98
+ return props.markGlyph ?? SESSION_GLYPH;
99
+ }
100
+
101
+ if (props.row.created) {
102
+ return "+";
103
+ }
104
+
105
+ if (done()) {
106
+ return "│";
107
+ }
108
+
73
109
  if (!props.dense) {
74
- return MARKER_COLUMNS + (glyph() ? RAIL_COLUMNS : 0);
110
+ return props.row.glyph || "│";
111
+ }
112
+
113
+ return "│";
114
+ };
115
+ const markColor = () => {
116
+ if (glyph() && props.row.mine) {
117
+ return theme().accent;
118
+ }
119
+
120
+ if (props.row.created) {
121
+ return theme().info;
122
+ }
123
+
124
+ if (props.row.unknown) {
125
+ return theme().warning;
75
126
  }
76
- return (glyph() ? RAIL_COLUMNS : 0) + (props.row.created ? 1 : 0);
127
+
128
+ if (done()) {
129
+ return theme().success;
130
+ }
131
+
132
+ if (!props.dense && props.row.glyph.length > 0) {
133
+ return keyColor();
134
+ }
135
+
136
+ return theme().borderSubtle;
77
137
  };
78
- const metaIndent = () => markerColumns() + 1;
79
- const sessionMark = () => props.markGlyph ?? (props.row.mine ? SESSION_GLYPH : " ");
80
- const statusGlyph = () => (props.dense ? (props.row.created ? "+" : "") : props.row.glyph);
138
+ const createdBadge = () => !props.dense && props.row.created && mark() !== "+";
139
+ const railColor = () => (done() ? theme().success : theme().borderSubtle);
140
+ const metaMark = () => (props.row.created && mark() !== "+" ? "+" : "│");
141
+ const metaMarkColor = () =>
142
+ props.row.created && mark() !== "+" ? theme().info : railColor();
81
143
  const [hover, setHover] = createSignal(false);
82
144
  const onEnter = () => setHover(true);
83
145
  const onLeave = () => setHover(false);
@@ -86,11 +148,8 @@ export function Row(props: {
86
148
  const byte = (n: number) => n.toString(16).padStart(2, "0");
87
149
  return `#${byte(r)}${byte(g)}${byte(b)}${hover() ? TINT_ALPHA_STRONG : TINT_ALPHA_SUBTLE}`;
88
150
  };
89
- const done = () => props.row.column === "Done";
90
151
  const background = () =>
91
152
  done() ? wash(theme().success) : hover() ? theme().backgroundElement : "transparent";
92
- // Every row reserves the left border, coloured only for Done rows so nothing shifts.
93
- const railColor = () => (done() ? theme().success : "transparent");
94
153
  const onClick = (event: { stopPropagation: () => void }) => {
95
154
  event.stopPropagation();
96
155
  if (hover()) {
@@ -100,13 +159,15 @@ export function Row(props: {
100
159
 
101
160
  const head = (
102
161
  <box flexDirection="row" gap={1}>
103
- <text width={markerColumns()} flexShrink={0} fg={keyColor()}>
104
- {glyph() ? <span style={{ fg: theme().accent }}>{sessionMark()}</span> : null}
105
- {statusGlyph()}
162
+ <text width={RAIL_COLUMNS} flexShrink={0} fg={markColor()}>
163
+ {mark()}
106
164
  </text>
107
165
 
108
166
  <text flexShrink={0} fg={keyColor()}>
109
167
  {props.row.key}
168
+ <Show when={createdBadge()}>
169
+ <span style={{ fg: theme().info }}>+</span>
170
+ </Show>
110
171
  </text>
111
172
  {!props.dense && (
112
173
  <text flexShrink={0} fg={metaColor()}>
@@ -115,7 +176,7 @@ export function Row(props: {
115
176
  )}
116
177
 
117
178
  <text wrapMode="none" flexShrink={1} fg={metaColor()}>
118
- {truncate(props.row.title, props.titleColumns)}
179
+ {truncate(props.row.title, props.titleColumns - (createdBadge() ? 1 : 0))}
119
180
  </text>
120
181
  </box>
121
182
  );
@@ -177,8 +238,10 @@ export function Row(props: {
177
238
  });
178
239
 
179
240
  const meta = () => (
180
- <box flexDirection="row">
181
- <text flexShrink={0}>{` `.repeat(metaIndent())}</text>
241
+ <box flexDirection="row" gap={1}>
242
+ <text width={RAIL_COLUMNS} flexShrink={0} fg={metaMarkColor()}>
243
+ {metaMark()}
244
+ </text>
182
245
  <text wrapMode="none" flexShrink={1} fg={metaColor()}>
183
246
  <For each={metaPlan()}>
184
247
  {(part) => (
@@ -208,8 +271,6 @@ export function Row(props: {
208
271
  <box
209
272
  flexDirection={props.dense ? "column" : "row"}
210
273
  backgroundColor={background()}
211
- border={["left"]}
212
- borderColor={railColor()}
213
274
  onMouseOver={onEnter}
214
275
  onMouseOut={onLeave}
215
276
  onMouseUp={onClick}
package/pkg/sidebar.tsx CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  } from "solid-js";
19
19
 
20
20
  import { clearPanel, publishPanel } from "./commands.ts";
21
- import { Overflow, Row } from "./components.tsx";
21
+ import { Overflow, RailLine, Row } from "./components.tsx";
22
22
  import {
23
23
  BOARD_TTL_MS,
24
24
  errorText,
@@ -30,7 +30,6 @@ import {
30
30
  } from "./data.ts";
31
31
  import { openDetail, openPicker } from "./dialogs.tsx";
32
32
  import {
33
- MARKER_COLUMNS,
34
33
  PRIORITY_COLUMNS,
35
34
  RAIL_COLUMNS,
36
35
  RUNNING_FRAMES,
@@ -395,18 +394,17 @@ export function Sidebar(props: {
395
394
  const knownOverflow = createMemo(() => knownRows().length - visibleKnown().length);
396
395
  const visibleUnknown = createMemo(() => unknownRows().slice(0, SECTION_ROWS));
397
396
  const unknownOverflow = createMemo(() => unknownRows().length - visibleUnknown().length);
398
- const contentIndent = () => RAIL_COLUMNS + (props.options.current === "glyph" ? 1 : 0) + 1;
399
397
  const titleColumns = createMemo(() => {
400
398
  const rows = [...visibleKnown(), ...visibleUnknown()];
401
399
  let key = 0;
402
400
  for (const row of rows) {
403
401
  key = Math.max(key, row.key.length);
404
402
  }
405
- const gaps = props.options.rows === 2 ? 3 : 4;
403
+ const gaps = props.options.rows === 2 ? 2 : 3;
406
404
  const priority = props.options.rows === 2 ? 0 : PRIORITY_COLUMNS;
407
- return Math.max(8, SLOT_COLUMNS - RAIL_COLUMNS - MARKER_COLUMNS - priority - key - gaps);
405
+ return Math.max(8, SLOT_COLUMNS - RAIL_COLUMNS - priority - key - gaps);
408
406
  });
409
- const metaColumns = Math.max(8, SLOT_COLUMNS - RAIL_COLUMNS - MARKER_COLUMNS);
407
+ const metaColumns = Math.max(8, SLOT_COLUMNS - RAIL_COLUMNS - 1);
410
408
  let published: PanelSnapshot | undefined;
411
409
  createEffect(() => {
412
410
  const loaded = map();
@@ -434,6 +432,7 @@ export function Sidebar(props: {
434
432
  const loaded = map();
435
433
  return loaded.ok ? undefined : loaded.error;
436
434
  });
435
+ const sectionHasRows = () => knownRows().length + unknownRows().length > 0;
437
436
  const knownKeys = () => scan().known.map((mention) => mention.key);
438
437
  const unknownKeys = () => scan().unknown.map((mention) => mention.key);
439
438
  const knownPicker = () => {
@@ -461,29 +460,40 @@ export function Sidebar(props: {
461
460
 
462
461
  return (
463
462
  <box flexDirection="column">
464
- <box flexDirection="row" gap={1} paddingLeft={contentIndent()} onMouseDown={reload}>
463
+ <RailLine ctx={props.ctx} glyph="┏" onMouseDown={reload}>
465
464
  <text fg={theme().text}>
466
465
  <b>ZINN</b>
467
466
  </text>
468
467
  <text fg={theme().textMuted}>[{headerDetail()}]</text>
469
- </box>
468
+ </RailLine>
469
+
470
470
  <Show when={failure()}>
471
- <text fg={theme().error}>{truncate(failure() ?? "", 40)}</text>
471
+ <RailLine ctx={props.ctx} glyph={sectionHasRows() ? "│" : "┗"}>
472
+ <text wrapMode="none" flexShrink={1} fg={theme().error}>
473
+ {truncate(failure() ?? "", 40)}
474
+ </text>
475
+ </RailLine>
472
476
  </Show>
473
- <Show when={!loading() && map().ok && knownRows().length === 0 && unknownRows().length === 0}>
474
- <text fg={theme().textMuted}>no task keys in this chat</text>
477
+
478
+ <Show when={!loading() && map().ok && !sectionHasRows()}>
479
+ <RailLine ctx={props.ctx} glyph="┗">
480
+ <text fg={theme().textMuted}>no task keys in this chat</text>
481
+ </RailLine>
475
482
  </Show>
483
+
476
484
  <For each={visibleKnown()}>
477
485
  {(row, index) => (
478
486
  <>
479
487
  <Show when={props.options.rows === 2 && index() > 0}>
480
- <box
481
- height={1}
482
- borderStyle="single"
483
- border={["top"]}
484
- borderColor={theme().borderSubtle}
485
- marginLeft={contentIndent()}
486
- />
488
+ <RailLine ctx={props.ctx} glyph="│">
489
+ <box
490
+ flexGrow={1}
491
+ height={1}
492
+ borderStyle="single"
493
+ border={["top"]}
494
+ borderColor={theme().borderSubtle}
495
+ />
496
+ </RailLine>
487
497
  </Show>
488
498
  <Row
489
499
  ctx={props.ctx}
@@ -502,14 +512,21 @@ export function Sidebar(props: {
502
512
  </>
503
513
  )}
504
514
  </For>
515
+
505
516
  <Show when={knownOverflow() > 0}>
506
517
  <Overflow ctx={props.ctx} count={knownOverflow()} label="more" onOpen={knownPicker} />
507
518
  </Show>
519
+
508
520
  <Show when={createdKeys().size > 0}>
509
- <text fg={theme().info}>{`+ created this session (${createdKeys().size})`}</text>
521
+ <RailLine ctx={props.ctx} glyph="│">
522
+ <text fg={theme().info}>{`+ created this session (${createdKeys().size})`}</text>
523
+ </RailLine>
510
524
  </Show>
525
+
511
526
  <Show when={unknownRows().length > 0}>
512
- <text fg={theme().warning}>unknown keys (not in store)</text>
527
+ <RailLine ctx={props.ctx} glyph="│">
528
+ <text fg={theme().warning}>unknown keys (not in store)</text>
529
+ </RailLine>
513
530
  <For each={visibleUnknown()}>
514
531
  {(row) => (
515
532
  <Row
@@ -533,10 +550,10 @@ export function Sidebar(props: {
533
550
  </Show>
534
551
  </Show>
535
552
 
536
- <Show when={!loading() && map().ok && knownRows().length + unknownRows().length > 0}>
537
- <box paddingLeft={contentIndent()}>
553
+ <Show when={!loading() && map().ok && sectionHasRows()}>
554
+ <RailLine ctx={props.ctx} glyph="┗">
538
555
  <text fg={theme().textMuted}>recent mentions</text>
539
- </box>
556
+ </RailLine>
540
557
  </Show>
541
558
  </box>
542
559
  );