opencode-sesh 0.1.6 → 0.1.7

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
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.7] - 2026-09-13
11
+
12
+ ### Fixed
13
+
14
+ - Sidebar and home session titles are capped to a consistent width so long
15
+ names no longer run into the timestamp. Removed OpenTUI's `truncate` prop,
16
+ which middle-truncated the already-shortened titles.
17
+ - The TUI picker no longer re-selects a session when the list scrolls under the
18
+ cursor, and its preview keeps the current transcript visible while the next
19
+ one loads instead of blanking on every cursor move.
20
+
21
+ ### Changed
22
+
23
+ - Sidebar rows use muted `├`/`└` tree glyphs and directory headers get a blank
24
+ line, so sessions are grouped clearly: a gap between directories, tight rows
25
+ within one.
26
+ - `npm install` and upgrades refresh an already-installed TUI panel
27
+ (`install.sh --sync-panel`), and `sesh --check` reports when the installed
28
+ panel is out of date. Because opencode imports plugins once at startup,
29
+ install now states that a full quit/reopen is required (a window reload reuses
30
+ the running process).
31
+
10
32
  ## [0.1.6] - 2026-09-13
11
33
 
12
34
  ### Changed
@@ -119,7 +141,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
119
141
  - Fixture-database regression suite, TypeScript typechecking, and ShellCheck in
120
142
  CI.
121
143
 
122
- [Unreleased]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.1.6...HEAD
144
+ [Unreleased]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.1.7...HEAD
145
+ [0.1.7]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.1.6...v0.1.7
123
146
  [0.1.6]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.1.5...v0.1.6
124
147
  [0.1.5]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.1.4...v0.1.5
125
148
  [0.1.4]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.1.3...v0.1.4
package/README.md CHANGED
@@ -68,7 +68,12 @@ file it would overwrite is backed up first.
68
68
  The npm package ships the same scripts and installer; the runtime tools below
69
69
  are still required.
70
70
 
71
- Restart opencode to load the panel, then:
71
+ Fully quit opencode and reopen it to load the panel — opencode imports plugins
72
+ once at startup, so reloading a window reuses the running process. Upgrades
73
+ refresh an already-installed panel automatically (the npm `postinstall` syncs
74
+ it), and `sesh --check` reports when the installed panel is out of date.
75
+
76
+ Then:
72
77
 
73
78
  ```bash
74
79
  sesh # terminal picker
package/bin/sesh.sh CHANGED
@@ -38,6 +38,18 @@ fi
38
38
  if [ "${1:-}" = --check ]; then
39
39
  "$SESH_JQ" -en '"2026-01-01T00:00:00Z" | fromdateiso8601 | type == "number"' >/dev/null || { echo 'jq capability check failed' >&2; exit 1; }
40
40
  command -v sqlite3 >/dev/null 2>&1 || command -v "$OPENCODE_BIN" >/dev/null 2>&1 || { echo 'neither sqlite3 nor opencode is available' >&2; exit 1; }
41
+ # The TUI panel is a copied file that opencode imports once at startup. Report
42
+ # when the installed copy has fallen behind this package so upgrades are not
43
+ # silently stale.
44
+ panel_bundled="$SCRIPT_DIR/../tui/sesh-panel.tsx"
45
+ panel_installed="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/plugins/sesh-panel.tsx"
46
+ if [ -f "$panel_installed" ] && [ -f "$panel_bundled" ]; then
47
+ if cmp -s "$panel_bundled" "$panel_installed"; then
48
+ echo 'sesh: sidebar panel is current'
49
+ else
50
+ echo 'sesh: sidebar panel is out of date; run `sesh install`, then fully quit and reopen opencode' >&2
51
+ fi
52
+ fi
41
53
  echo 'sesh: dependencies ready'
42
54
  exit 0
43
55
  fi
package/install.sh CHANGED
@@ -19,6 +19,7 @@ BIN_DIR="${XDG_BIN_HOME:-$HOME/.local/bin}"
19
19
  INSTALL_TOOL=1
20
20
  INSTALL_TUI=1
21
21
  UNINSTALL=0
22
+ SYNC_PANEL=0
22
23
 
23
24
  # Fallback plugin version for when the opencode CLI is unavailable; the
24
25
  # installer prefers the local opencode version so the panel matches its SDK.
@@ -43,6 +44,7 @@ Usage: bash install.sh [--bin-dir DIR] [--no-tool] [--no-tui] [--uninstall]
43
44
  --no-tool Skip installing the sesh-list custom tool.
44
45
  --no-tui Skip installing the in-TUI sessions panel plugin.
45
46
  --uninstall Remove everything this script installed.
47
+ --sync-panel Refresh an already-installed TUI panel only (used by postinstall).
46
48
  --help Show this help.
47
49
  USAGE
48
50
  }
@@ -53,6 +55,7 @@ while [ "$#" -gt 0 ]; do
53
55
  --no-tool) INSTALL_TOOL=0; shift ;;
54
56
  --no-tui) INSTALL_TUI=0; shift ;;
55
57
  --uninstall) UNINSTALL=1; shift ;;
58
+ --sync-panel) SYNC_PANEL=1; shift ;;
56
59
  --help) usage; exit 0 ;;
57
60
  *) fail "unknown option: $1" ;;
58
61
  esac
@@ -110,62 +113,6 @@ launcher_is_ours() {
110
113
  [ -f "$target" ] && grep -qF 'Pick an opencode session' "$target" 2>/dev/null
111
114
  }
112
115
 
113
- if [ "$UNINSTALL" = 1 ]; then
114
- if [ -L "$LAUNCHER" ]; then
115
- if launcher_is_ours "$LAUNCHER"; then
116
- rm -f "$LAUNCHER" && note "removed $LAUNCHER"
117
- else
118
- note "left $LAUNCHER untouched (not a sesh launcher: $(readlink "$LAUNCHER" 2>/dev/null))"
119
- fi
120
- fi
121
- remove_if_marker "$COMMAND_DST" "rich interactive picker" && note "removed $COMMAND_DST"
122
- remove_if_marker "$TOOL_DST" "list opencode sessions for the agent" && note "removed $TOOL_DST"
123
- remove_if_marker "$PANEL_DST" "@jsxImportSource @opentui/solid" && note "removed $PANEL_DST"
124
- remove_tui_entry && note "unregistered the panel from $TUI_JSON"
125
- note "uninstalled (extraction cache and plugin dependencies left intact)"
126
- exit 0
127
- fi
128
-
129
- for path in \
130
- bin/sesh \
131
- bin/sesh.sh \
132
- bin/sesh-list.sh \
133
- bin/sesh-preview.sh \
134
- bin/sesh-delete.sh \
135
- bin/sesh-refresh-worker.sh \
136
- bin/sesh-shortcuts.sh \
137
- opencode/tools/sesh-list.ts \
138
- tui/sesh-panel.tsx \
139
- themes/glow-dark-clean.json; do
140
- [ -f "$SOURCE_DIR/$path" ] || fail "installer bundle is incomplete: missing $path."
141
- done
142
-
143
- for tool in bash jq fzf; do
144
- command -v "$tool" >/dev/null 2>&1 || fail "required tool '$tool' is unavailable."
145
- done
146
- fzf_version=$(fzf --version 2>/dev/null || true)
147
- if [[ "$fzf_version" =~ ^([0-9]+)\.([0-9]+)(\.([0-9]+))? ]]; then
148
- if [ "$((10#${BASH_REMATCH[1]}))" -eq 0 ] && [ "$((10#${BASH_REMATCH[2]}))" -lt 73 ]; then
149
- fail "fzf >= 0.73.0 is required (found $fzf_version)."
150
- fi
151
- else
152
- fail "cannot parse fzf version from: $fzf_version."
153
- fi
154
- command -v sqlite3 >/dev/null 2>&1 || command -v opencode >/dev/null 2>&1 \
155
- || fail "either sqlite3 or the opencode CLI is required."
156
- command -v opencode >/dev/null 2>&1 || note "opencode CLI not found: listing and previews work via sqlite3, but resume and delete need opencode."
157
-
158
- umask 077
159
- mkdir -p "$BIN_DIR"
160
- if [ -e "$LAUNCHER" ] && [ ! -L "$LAUNCHER" ]; then
161
- fail "$LAUNCHER already exists and is not a symlink. Move it aside, then retry."
162
- fi
163
- if [ -L "$LAUNCHER" ] && ! launcher_is_ours "$LAUNCHER"; then
164
- fail "$LAUNCHER is a symlink to $(readlink "$LAUNCHER" 2>/dev/null), which is not a sesh install. Move it aside, then retry."
165
- fi
166
- ln -sfn "$SOURCE_DIR/bin/sesh" "$LAUNCHER"
167
- note "linked $LAUNCHER -> $SOURCE_DIR/bin/sesh"
168
-
169
116
  install_file() {
170
117
  local src=$1 dst=$2
171
118
  mkdir -p "$(dirname "$dst")"
@@ -181,26 +128,20 @@ install_file() {
181
128
  note "installed $dst"
182
129
  }
183
130
 
184
- # Earlier versions shipped a markdown `/sesh` command that could only prompt the
185
- # agent; the TUI plugin now registers the slash itself. Drop the old file so the
186
- # two do not collide.
187
- remove_if_marker "$COMMAND_DST" "rich interactive picker" && note "removed obsolete markdown command $COMMAND_DST"
188
- [ "$INSTALL_TOOL" = 1 ] && install_file "$SOURCE_DIR/opencode/tools/sesh-list.ts" "$TOOL_DST"
189
-
190
- opencode_version=''
191
- if command -v opencode >/dev/null 2>&1; then
192
- raw_version=$(opencode --version 2>/dev/null || true)
193
- if [[ "$raw_version" =~ ([0-9]+\.[0-9]+\.[0-9]+) ]]; then opencode_version="${BASH_REMATCH[1]}"; fi
194
- fi
195
- plugin_version="${opencode_version:-$PLUGIN_VERSION_FALLBACK}"
196
-
197
- if [ "$INSTALL_TUI" = 1 ]; then
131
+ install_tui_panel() {
198
132
  install_file "$SOURCE_DIR/tui/sesh-panel.tsx" "$PANEL_DST"
199
133
 
200
- # opencode has no persistent-sidebar API: the panel is an xlarge modal
201
- # dialog (see tui/sesh-panel.tsx). Register it idempotently and drop the
202
- # previous filename if an older install left it behind.
203
- tui_created=0
134
+ local opencode_version='' raw_version='' plugin_version=''
135
+ if command -v opencode >/dev/null 2>&1; then
136
+ raw_version=$(opencode --version 2>/dev/null || true)
137
+ if [[ "$raw_version" =~ ([0-9]+\.[0-9]+\.[0-9]+) ]]; then opencode_version="${BASH_REMATCH[1]}"; fi
138
+ fi
139
+ plugin_version="${opencode_version:-$PLUGIN_VERSION_FALLBACK}"
140
+
141
+ # opencode has no persistent-sidebar API: the panel is an xlarge modal dialog
142
+ # (see tui/sesh-panel.tsx). Register it idempotently and drop the previous
143
+ # filename if an older install left it behind.
144
+ local tui_created=0 tui_tmp=''
204
145
  if [ ! -f "$TUI_JSON" ]; then
205
146
  printf '{\n "$schema": "https://opencode.ai/tui.json",\n "plugin": []\n}\n' > "$TUI_JSON"
206
147
  tui_created=1
@@ -228,7 +169,7 @@ if [ "$INSTALL_TUI" = 1 ]; then
228
169
  # Local plugins resolve their imports from the config directory, and opencode
229
170
  # runs `bun install` there at startup. Keep the required packages declared
230
171
  # without clobbering versions the user already pinned.
231
- pkg_created=0
172
+ local pkg_created=0 pkg_tmp=''
232
173
  if [ ! -f "$PACKAGE_JSON" ]; then
233
174
  printf '{\n "dependencies": {}\n}\n' > "$PACKAGE_JSON"
234
175
  pkg_created=1
@@ -257,12 +198,104 @@ if [ "$INSTALL_TUI" = 1 ]; then
257
198
  rm -f "$pkg_tmp"
258
199
  note "warning: could not update $PACKAGE_JSON; the TUI panel may fail to load"
259
200
  fi
201
+ }
202
+
203
+ # opencode imports plugins once at startup and has no hot reload, so a running
204
+ # instance keeps the old panel until it is fully quit (reloading a window reuses
205
+ # the same process). Say so explicitly and name the processes still holding it.
206
+ note_restart() {
207
+ note "restart opencode to load the updated panel: quit it completely"
208
+ note " (all windows and background processes), then reopen."
209
+ if command -v pgrep >/dev/null 2>&1; then
210
+ local pids
211
+ pids=$(pgrep -x opencode 2>/dev/null | tr '\n' ' ' | sed 's/ $//' || true)
212
+ [ -n "$pids" ] && note " still running: opencode PID(s) $pids"
213
+ fi
214
+ return 0
215
+ }
216
+
217
+ if [ "$UNINSTALL" = 1 ]; then
218
+ if [ -L "$LAUNCHER" ]; then
219
+ if launcher_is_ours "$LAUNCHER"; then
220
+ rm -f "$LAUNCHER" && note "removed $LAUNCHER"
221
+ else
222
+ note "left $LAUNCHER untouched (not a sesh launcher: $(readlink "$LAUNCHER" 2>/dev/null))"
223
+ fi
224
+ fi
225
+ remove_if_marker "$COMMAND_DST" "rich interactive picker" && note "removed $COMMAND_DST"
226
+ remove_if_marker "$TOOL_DST" "list opencode sessions for the agent" && note "removed $TOOL_DST"
227
+ remove_if_marker "$PANEL_DST" "@jsxImportSource @opentui/solid" && note "removed $PANEL_DST"
228
+ remove_tui_entry && note "unregistered the panel from $TUI_JSON"
229
+ note "uninstalled (extraction cache and plugin dependencies left intact)"
230
+ exit 0
231
+ fi
232
+
233
+ for path in \
234
+ bin/sesh \
235
+ bin/sesh.sh \
236
+ bin/sesh-list.sh \
237
+ bin/sesh-preview.sh \
238
+ bin/sesh-delete.sh \
239
+ bin/sesh-refresh-worker.sh \
240
+ bin/sesh-shortcuts.sh \
241
+ opencode/tools/sesh-list.ts \
242
+ tui/sesh-panel.tsx \
243
+ themes/glow-dark-clean.json; do
244
+ [ -f "$SOURCE_DIR/$path" ] || fail "installer bundle is incomplete: missing $path."
245
+ done
246
+
247
+ if [ "$SYNC_PANEL" = 1 ]; then
248
+ # Postinstall path: refresh a panel the user already opted into, without
249
+ # touching the launcher or requiring fzf/sqlite. No-op when the panel is
250
+ # absent, so `npm install` never adds a TUI panel on its own.
251
+ [ -f "$PANEL_DST" ] || { note "sesh sidebar panel is not installed; nothing to sync."; exit 0; }
252
+ umask 077
253
+ install_tui_panel
254
+ note_restart
255
+ exit 0
256
+ fi
257
+
258
+ for tool in bash jq fzf; do
259
+ command -v "$tool" >/dev/null 2>&1 || fail "required tool '$tool' is unavailable."
260
+ done
261
+ fzf_version=$(fzf --version 2>/dev/null || true)
262
+ if [[ "$fzf_version" =~ ^([0-9]+)\.([0-9]+)(\.([0-9]+))? ]]; then
263
+ if [ "$((10#${BASH_REMATCH[1]}))" -eq 0 ] && [ "$((10#${BASH_REMATCH[2]}))" -lt 73 ]; then
264
+ fail "fzf >= 0.73.0 is required (found $fzf_version)."
265
+ fi
266
+ else
267
+ fail "cannot parse fzf version from: $fzf_version."
268
+ fi
269
+ command -v sqlite3 >/dev/null 2>&1 || command -v opencode >/dev/null 2>&1 \
270
+ || fail "either sqlite3 or the opencode CLI is required."
271
+ command -v opencode >/dev/null 2>&1 || note "opencode CLI not found: listing and previews work via sqlite3, but resume and delete need opencode."
272
+
273
+ umask 077
274
+ mkdir -p "$BIN_DIR"
275
+ if [ -e "$LAUNCHER" ] && [ ! -L "$LAUNCHER" ]; then
276
+ fail "$LAUNCHER already exists and is not a symlink. Move it aside, then retry."
277
+ fi
278
+ if [ -L "$LAUNCHER" ] && ! launcher_is_ours "$LAUNCHER"; then
279
+ fail "$LAUNCHER is a symlink to $(readlink "$LAUNCHER" 2>/dev/null), which is not a sesh install. Move it aside, then retry."
280
+ fi
281
+ ln -sfn "$SOURCE_DIR/bin/sesh" "$LAUNCHER"
282
+ note "linked $LAUNCHER -> $SOURCE_DIR/bin/sesh"
283
+
284
+ # Earlier versions shipped a markdown `/sesh` command that could only prompt the
285
+ # agent; the TUI plugin now registers the slash itself. Drop the old file so the
286
+ # two do not collide.
287
+ remove_if_marker "$COMMAND_DST" "rich interactive picker" && note "removed obsolete markdown command $COMMAND_DST"
288
+ [ "$INSTALL_TOOL" = 1 ] && install_file "$SOURCE_DIR/opencode/tools/sesh-list.ts" "$TOOL_DST"
289
+
290
+ if [ "$INSTALL_TUI" = 1 ]; then
291
+ install_tui_panel
260
292
  fi
261
293
 
262
294
  "$LAUNCHER" --check || fail "post-install check failed."
263
295
  note "done"
264
296
  note " picker: run 'sesh' in any terminal, or '/sesh' inside opencode"
265
- note " sidebar: restart opencode to load the panel (ctrl+o opens the full picker)"
297
+ note " sidebar: ctrl+o opens the full picker"
298
+ note_restart
266
299
  case ":$PATH:" in
267
300
  *":$BIN_DIR:"*) ;;
268
301
  *) note " note: $BIN_DIR is not on PATH; add it to use the launcher anywhere." ;;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-sesh",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "description": "Browse, search, preview and resume opencode sessions from the terminal and the TUI.",
6
6
  "license": "MIT",
@@ -41,6 +41,7 @@
41
41
  },
42
42
  "scripts": {
43
43
  "prepare": "husky",
44
+ "postinstall": "bash install.sh --sync-panel || true",
44
45
  "test": "bash tests/sesh.sh",
45
46
  "test:picker": "python3 tests/picker.py",
46
47
  "typecheck": "tsc -p tsconfig.json",
@@ -130,6 +130,13 @@ function truncate(text: string, width: number): string {
130
130
  return text.slice(0, width - 1) + "…"
131
131
  }
132
132
 
133
+ // Sidebar and home rows should read at a similar length. The sidebar title
134
+ // column is ~2 chars narrower than the group column (status dot + timestamp),
135
+ // so cap it a bit lower to guarantee separation.
136
+ const SIDEBAR_TITLE_WIDTH = 27
137
+ const SIDEBAR_GROUP_WIDTH = 30
138
+ const HOME_TITLE_WIDTH = 30
139
+
133
140
  const SEARCH_KEYS = "abcdefghijklmnopqrstuvwxyz0123456789-_.@/+".split("")
134
141
 
135
142
  function searchBindings(append: (input: string) => void, exit: () => void) {
@@ -445,7 +452,7 @@ function createTranscriptPreview(api: TuiPluginApi) {
445
452
 
446
453
  type TreeRow =
447
454
  | { kind: "group"; dir: string; label: string; count: number }
448
- | { kind: "item"; entry: Entry }
455
+ | { kind: "item"; entry: Entry; last: boolean }
449
456
 
450
457
  function SidebarSessions(props: { api: TuiPluginApi }) {
451
458
  const theme = () => props.api.theme.current
@@ -504,7 +511,9 @@ function SidebarSessions(props: { api: TuiPluginApi }) {
504
511
  for (const group of ordered) {
505
512
  rows.push({ kind: "group", dir: group.dir, label: prettyDir(group.dir, home), count: group.list.length })
506
513
  if (collapsed()[group.dir]) continue
507
- for (const entry of group.list) rows.push({ kind: "item", entry })
514
+ for (let i = 0; i < group.list.length; i++) {
515
+ rows.push({ kind: "item", entry: group.list[i], last: i === group.list.length - 1 })
516
+ }
508
517
  }
509
518
  return rows
510
519
  })
@@ -676,10 +685,10 @@ function SidebarSessions(props: { api: TuiPluginApi }) {
676
685
  <For each={tree()}>
677
686
  {(row) =>
678
687
  row.kind === "group" ? (
679
- <box flexDirection="row" onMouseDown={() => toggleGroup(row.dir)}>
688
+ <box flexDirection="row" paddingTop={1} onMouseDown={() => toggleGroup(row.dir)}>
680
689
  <text flexGrow={1} flexShrink={1} wrapMode="none">
681
690
  <span style={{ fg: theme().textMuted }}>{collapsed()[row.dir] ? "▸ " : "▾ "}</span>
682
- <b>{row.label}</b>
691
+ <b>{truncate(row.label, SIDEBAR_GROUP_WIDTH)}</b>
683
692
  </text>
684
693
  <text flexShrink={0} style={{ fg: theme().textMuted }}>
685
694
  {" "}({row.count})
@@ -700,14 +709,16 @@ function SidebarSessions(props: { api: TuiPluginApi }) {
700
709
  onMouseOut={() => setHovered(undefined)}
701
710
  onMouseDown={() => props.api.route.navigate("session", { sessionID: row.entry.id })}
702
711
  >
703
- <text
704
- flexShrink={0}
705
- style={{ fg: row.entry.id === currentID() ? theme().accent : statusColor(row.entry.id) }}
706
- >
707
- {row.entry.id === currentID() ? "●" : "○"}
712
+ <text flexShrink={0}>
713
+ <span style={{ fg: theme().textMuted }}>{row.last ? "└" : "├"}</span>
714
+ <span
715
+ style={{ fg: row.entry.id === currentID() ? theme().accent : statusColor(row.entry.id) }}
716
+ >
717
+ {row.entry.id === currentID() ? "●" : "○"}
718
+ </span>
708
719
  </text>
709
720
  <text flexGrow={1} flexShrink={1} overflow="hidden" wrapMode="none" style={{ fg: theme().text }}>
710
- {row.entry.title}
721
+ {truncate(row.entry.title, SIDEBAR_TITLE_WIDTH)}
711
722
  </text>
712
723
  <text flexShrink={0} style={{ fg: theme().textMuted }}>
713
724
  {ago(row.entry.updated)}
@@ -865,7 +876,7 @@ function HomeSessions(props: { api: TuiPluginApi }) {
865
876
  ○
866
877
  </text>
867
878
  <text flexGrow={1} flexShrink={1} overflow="hidden" wrapMode="none" style={{ fg: theme().text }}>
868
- {truncate(entry.title, 52)}
879
+ {truncate(entry.title, HOME_TITLE_WIDTH)}
869
880
  </text>
870
881
  <text flexShrink={0} style={{ fg: theme().textMuted }}>
871
882
  {prettyDir(entry.dir, home)} · {ago(entry.updated)}
@@ -1032,7 +1043,8 @@ const tui: TuiPlugin = async (api) => {
1032
1043
  setPreviewText(cached)
1033
1044
  return
1034
1045
  }
1035
- setPreviewText("")
1046
+ // Keep the previous transcript visible while the next one loads so the
1047
+ // preview does not blink away on every cursor move.
1036
1048
  previewTimer = setTimeout(() => void loadPreview(id), 120)
1037
1049
  })
1038
1050
 
@@ -1167,10 +1179,6 @@ const tui: TuiPlugin = async (api) => {
1167
1179
  ? api.theme.current.primary
1168
1180
  : RGBA.fromInts(0, 0, 0, 0)
1169
1181
  }
1170
- onMouseOver={() => {
1171
- const i = selectableEntries().findIndex((entry) => entry.id === row.entry.id)
1172
- if (i >= 0 && i !== cursor()) setCursor(i)
1173
- }}
1174
1182
  onMouseDown={() => choose(row.entry)}
1175
1183
  >
1176
1184
  <Show when={row.entry.id === currentSessionID()}>