opencode-sesh 2.1.1 → 2.1.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/AGENTS.md +8 -0
- package/CHANGELOG.md +14 -0
- package/README.md +22 -18
- package/dist/tui.js +533 -453
- package/package.json +13 -12
- package/tui-plugins/sesh-panel/tui.tsx +65 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-sesh",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "OpenCode session browser: search, preview, and resume every session across every project from the terminal or TUI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,14 +50,15 @@
|
|
|
50
50
|
"packageManager": "bun@1.4.2",
|
|
51
51
|
"engines": {
|
|
52
52
|
"node": ">=20",
|
|
53
|
-
"opencode": ">=2.
|
|
53
|
+
"opencode": ">=2.2.0"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"prepare": "husky",
|
|
57
57
|
"build": "node scripts/build-tui.mjs",
|
|
58
58
|
"prepack": "node scripts/build-tui.mjs",
|
|
59
59
|
"test": "bun run test:unit && bash tests/changelog.sh",
|
|
60
|
-
"test:unit": "node scripts/build-tui.mjs && bash tests/sesh.sh && bash tests/install.sh",
|
|
60
|
+
"test:unit": "node scripts/build-tui.mjs && bun run test:tui-home && bash tests/sesh.sh && bash tests/install.sh",
|
|
61
|
+
"test:tui-home": "bun --conditions=browser tests/tui-home.mjs",
|
|
61
62
|
"test:changelog": "bash tests/changelog.sh",
|
|
62
63
|
"test:picker": "python3 tests/picker.py",
|
|
63
64
|
"typecheck": "tsc --noEmit",
|
|
@@ -66,9 +67,9 @@
|
|
|
66
67
|
"prepublishOnly": "bun run test && bun run typecheck"
|
|
67
68
|
},
|
|
68
69
|
"peerDependencies": {
|
|
69
|
-
"@opencode/plugin": ">=2.
|
|
70
|
-
"@opentui/core": ">=0.
|
|
71
|
-
"@opentui/solid": ">=0.
|
|
70
|
+
"@opencode/plugin": ">=2.2.0",
|
|
71
|
+
"@opentui/core": ">=0.7.0",
|
|
72
|
+
"@opentui/solid": ">=0.7.0",
|
|
72
73
|
"solid-js": ">=1.9.0"
|
|
73
74
|
},
|
|
74
75
|
"devDependencies": {
|
|
@@ -76,12 +77,12 @@
|
|
|
76
77
|
"@babel/preset-typescript": "7.29.7",
|
|
77
78
|
"@commitlint/cli": "^21.2.2",
|
|
78
79
|
"@commitlint/config-conventional": "^21.2.2",
|
|
79
|
-
"@opencode/client": "2.
|
|
80
|
-
"@opencode/plugin": "2.
|
|
81
|
-
"@opencode/theme": "2.
|
|
82
|
-
"@opentui/core": "^0.
|
|
83
|
-
"@opentui/keymap": "^0.
|
|
84
|
-
"@opentui/solid": "^0.
|
|
80
|
+
"@opencode/client": "2.2.5",
|
|
81
|
+
"@opencode/plugin": "2.2.5",
|
|
82
|
+
"@opencode/theme": "2.2.5",
|
|
83
|
+
"@opentui/core": "^0.8.0",
|
|
84
|
+
"@opentui/keymap": "^0.8.0",
|
|
85
|
+
"@opentui/solid": "^0.8.0",
|
|
85
86
|
"@types/node": "26.6.5",
|
|
86
87
|
"babel-preset-solid": "1.9.16",
|
|
87
88
|
"husky": "^9.1.7",
|
|
@@ -1819,10 +1819,25 @@ function SidebarSessions(props: { api: TuiPluginApi } & PinProps) {
|
|
|
1819
1819
|
}
|
|
1820
1820
|
|
|
1821
1821
|
const HOME_SESSION_LIMIT = 6
|
|
1822
|
+
// Layout baseline: OpenCode 2.2.4. Re-check these assumptions and the matching
|
|
1823
|
+
// tests/tui-home.mjs fixture when upgrading OpenCode: the fixture mirrors the
|
|
1824
|
+
// host layout, so it cannot detect upstream layout changes on its own.
|
|
1825
|
+
// Sources pinned to that release:
|
|
1826
|
+
// https://github.com/anomalyco/opencode/blob/45898d0f8569225eb1ddb4ee13f8581b34dff311/packages/tui/src/routes/home.tsx
|
|
1827
|
+
// https://github.com/anomalyco/opencode/blob/45898d0f8569225eb1ddb4ee13f8581b34dff311/packages/tui/src/component/prompt/index.tsx#L1676
|
|
1828
|
+
// https://github.com/anomalyco/opencode/blob/45898d0f8569225eb1ddb4ee13f8581b34dff311/packages/tui/src/component/logo.tsx
|
|
1829
|
+
// https://github.com/anomalyco/opencode/blob/45898d0f8569225eb1ddb4ee13f8581b34dff311/packages/tui/src/feature-plugins/home/footer.tsx
|
|
1830
|
+
// The native home needs room for its logo, composer chrome and status footer.
|
|
1831
|
+
// Reserve the editor's *maximum* height (one third of the terminal, at least
|
|
1832
|
+
// six lines), so typing/wrapping a prompt cannot push it into our footer.
|
|
1833
|
+
// Chrome: logo (4) + gap (1) + composer (6) + status (3). Below 44 columns,
|
|
1834
|
+
// the logo stacks to seven lines but the three-line status footer disappears.
|
|
1835
|
+
const HOME_CHROME_ROWS = 14
|
|
1822
1836
|
|
|
1823
1837
|
function HomeSessions(props: { api: TuiPluginApi } & PinProps) {
|
|
1824
1838
|
const theme = () => props.api.theme.current
|
|
1825
1839
|
const home = process.env.HOME ?? ""
|
|
1840
|
+
const [dimensions, setDimensions] = createSignal({ width: props.api.renderer.width, height: props.api.renderer.height })
|
|
1826
1841
|
const [entries, setEntries] = createSignal<Entry[]>([])
|
|
1827
1842
|
const [query, setQuery] = createSignal("")
|
|
1828
1843
|
const [hovered, setHovered] = createSignal<string>()
|
|
@@ -1930,7 +1945,12 @@ function HomeSessions(props: { api: TuiPluginApi } & PinProps) {
|
|
|
1930
1945
|
onMount(() => {
|
|
1931
1946
|
ensureRootMouse(props.api.renderer)
|
|
1932
1947
|
rootMouseHandlers.add(onRootMouseDown)
|
|
1933
|
-
|
|
1948
|
+
const resize = () => setDimensions({ width: props.api.renderer.width, height: props.api.renderer.height })
|
|
1949
|
+
props.api.renderer.on("resize", resize)
|
|
1950
|
+
onCleanup(() => {
|
|
1951
|
+
rootMouseHandlers.delete(onRootMouseDown)
|
|
1952
|
+
props.api.renderer.off("resize", resize)
|
|
1953
|
+
})
|
|
1934
1954
|
})
|
|
1935
1955
|
|
|
1936
1956
|
const appendQuery = (input: string) => {
|
|
@@ -1991,37 +2011,57 @@ function HomeSessions(props: { api: TuiPluginApi } & PinProps) {
|
|
|
1991
2011
|
setLoadFailed(snapshot.failed)
|
|
1992
2012
|
})))
|
|
1993
2013
|
|
|
1994
|
-
const
|
|
2014
|
+
const filtered = createMemo(() => {
|
|
1995
2015
|
const q = query().trim().toLowerCase()
|
|
1996
2016
|
const list = q
|
|
1997
2017
|
? entries().filter(
|
|
1998
2018
|
(entry) => entry.title.toLowerCase().includes(q) || entry.dir.toLowerCase().includes(q),
|
|
1999
2019
|
)
|
|
2000
2020
|
: entries()
|
|
2001
|
-
return pinnedFirst(list, props.pins())
|
|
2021
|
+
return pinnedFirst(list, props.pins())
|
|
2022
|
+
})
|
|
2023
|
+
const layout = createMemo(() => {
|
|
2024
|
+
const rows = dimensions().height
|
|
2025
|
+
const budget = Math.max(0, rows - Math.max(6, Math.floor(rows / 3)) - HOME_CHROME_ROWS)
|
|
2026
|
+
// Drop decorative padding/spacing before sessions. Keep the full list in
|
|
2027
|
+
// the picker when even a heading and one session cannot fit.
|
|
2028
|
+
const padding = budget >= HOME_SESSION_LIMIT + 2 ? 1 : 0
|
|
2029
|
+
const limit = Math.min(HOME_SESSION_LIMIT, Math.max(0, budget - padding - 1))
|
|
2030
|
+
const count = Math.max(1, Math.min(filtered().length, limit))
|
|
2031
|
+
return { budget, limit, padding, gap: budget >= 1 + padding + count * 2 ? 1 : 0 }
|
|
2032
|
+
})
|
|
2033
|
+
const visible = createMemo(() => filtered().slice(0, layout().limit))
|
|
2034
|
+
createEffect(() => {
|
|
2035
|
+
const ids = new Set(visible().map((entry) => entry.id))
|
|
2036
|
+
if (hovered() && !ids.has(hovered()!)) setHovered(undefined)
|
|
2037
|
+
if (pendingDelete() && !ids.has(pendingDelete()!)) cancelDelete()
|
|
2038
|
+
if (layout().limit === 0) setSearching(false)
|
|
2002
2039
|
})
|
|
2003
2040
|
|
|
2004
2041
|
return (
|
|
2005
|
-
<box width="100%" alignItems="center">
|
|
2006
|
-
<box flexDirection="column" width="100%" maxWidth={72} paddingLeft={1} paddingRight={1} paddingTop={
|
|
2042
|
+
<box visible={layout().limit > 0} width="100%" flexShrink={0} maxHeight={layout().budget} overflow="hidden" alignItems="center">
|
|
2043
|
+
<box flexDirection="column" width="100%" maxWidth={72} paddingLeft={1} paddingRight={1} paddingTop={layout().padding} gap={layout().gap}>
|
|
2007
2044
|
<Show
|
|
2008
2045
|
when={entries().length > 0}
|
|
2009
2046
|
fallback={
|
|
2010
|
-
<box
|
|
2011
|
-
<text style={{ fg: theme().textMuted }}>
|
|
2012
|
-
|
|
2013
|
-
{loadFailed() ? "· list unavailable, will retry" : "· none yet, option+o to browse"}
|
|
2047
|
+
<box height={1} flexShrink={0}>
|
|
2048
|
+
<text wrapMode="none" overflow="hidden" style={{ fg: theme().textMuted }}>
|
|
2049
|
+
Recent sessions {loadFailed() ? "· list unavailable, will retry" : "· none yet, option+o to browse"}
|
|
2014
2050
|
</text>
|
|
2015
2051
|
</box>
|
|
2016
2052
|
}
|
|
2017
2053
|
>
|
|
2018
|
-
<box flexDirection="row" justifyContent="space-between" gap={2}>
|
|
2019
|
-
<box flexDirection="row" gap={1}>
|
|
2020
|
-
<text style={{ fg: theme().textMuted }}>Recent sessions</text>
|
|
2021
|
-
<
|
|
2054
|
+
<box height={1} flexShrink={0} flexDirection="row" justifyContent="space-between" gap={2} overflow="hidden">
|
|
2055
|
+
<box flexDirection="row" gap={1} flexShrink={0}>
|
|
2056
|
+
<text wrapMode="none" style={{ fg: theme().textMuted }}>Recent sessions</text>
|
|
2057
|
+
<Show when={dimensions().width >= 68}>
|
|
2058
|
+
<text style={{ fg: theme().textMuted }}>· option+o for all</text>
|
|
2059
|
+
</Show>
|
|
2022
2060
|
</box>
|
|
2023
2061
|
<box
|
|
2024
2062
|
flexDirection="row"
|
|
2063
|
+
flexShrink={1}
|
|
2064
|
+
minWidth={0}
|
|
2025
2065
|
gap={1}
|
|
2026
2066
|
paddingLeft={1}
|
|
2027
2067
|
paddingRight={1}
|
|
@@ -2031,7 +2071,7 @@ function HomeSessions(props: { api: TuiPluginApi } & PinProps) {
|
|
|
2031
2071
|
setSearching(true)
|
|
2032
2072
|
}}
|
|
2033
2073
|
>
|
|
2034
|
-
<box width={24}>
|
|
2074
|
+
<box width={24} flexShrink={1} minWidth={0}>
|
|
2035
2075
|
<text
|
|
2036
2076
|
flexGrow={1}
|
|
2037
2077
|
flexShrink={1}
|
|
@@ -2053,6 +2093,9 @@ function HomeSessions(props: { api: TuiPluginApi } & PinProps) {
|
|
|
2053
2093
|
<For each={visible()}>
|
|
2054
2094
|
{(entry) => (
|
|
2055
2095
|
<box
|
|
2096
|
+
height={1}
|
|
2097
|
+
flexShrink={0}
|
|
2098
|
+
overflow="hidden"
|
|
2056
2099
|
flexDirection="row"
|
|
2057
2100
|
gap={1}
|
|
2058
2101
|
backgroundColor={entry.id === hovered() ? theme().backgroundElement : RGBA.fromInts(0, 0, 0, 0)}
|
|
@@ -2073,7 +2116,13 @@ function HomeSessions(props: { api: TuiPluginApi } & PinProps) {
|
|
|
2073
2116
|
color={theme().text}
|
|
2074
2117
|
matchColor={theme().warning}
|
|
2075
2118
|
/>
|
|
2076
|
-
<text
|
|
2119
|
+
<text
|
|
2120
|
+
flexShrink={1}
|
|
2121
|
+
maxWidth={Math.max(0, Math.floor((Math.min(72, dimensions().width) - 4) / 2))}
|
|
2122
|
+
wrapMode="none"
|
|
2123
|
+
overflow="hidden"
|
|
2124
|
+
style={{ fg: pendingDelete() === entry.id ? theme().error : theme().textMuted }}
|
|
2125
|
+
>
|
|
2077
2126
|
{pendingDelete() === entry.id
|
|
2078
2127
|
? "option+d again"
|
|
2079
2128
|
: `${props.pins().directories.includes(entry.dir) ? "★ " : ""}${prettyDir(entry.dir, home)} · ${ago(entry.updated)}`}
|
|
@@ -2082,7 +2131,7 @@ function HomeSessions(props: { api: TuiPluginApi } & PinProps) {
|
|
|
2082
2131
|
)}
|
|
2083
2132
|
</For>
|
|
2084
2133
|
<Show when={visible().length === 0}>
|
|
2085
|
-
<text style={{ fg: theme().textMuted }}>No title or directory matches · option+o to search transcripts</text>
|
|
2134
|
+
<text height={1} flexShrink={0} wrapMode="none" overflow="hidden" style={{ fg: theme().textMuted }}>No title or directory matches · option+o to search transcripts</text>
|
|
2086
2135
|
</Show>
|
|
2087
2136
|
</Show>
|
|
2088
2137
|
</box>
|