termcast 1.3.54 → 1.4.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/dist/action-utils.d.ts.map +1 -1
- package/dist/action-utils.js +17 -132
- package/dist/action-utils.js.map +1 -1
- package/dist/apis/cache.d.ts +8 -30
- package/dist/apis/cache.d.ts.map +1 -1
- package/dist/apis/cache.js +9 -271
- package/dist/apis/cache.js.map +1 -1
- package/dist/apis/clipboard.d.ts +4 -2
- package/dist/apis/clipboard.d.ts.map +1 -1
- package/dist/apis/clipboard.js +18 -31
- package/dist/apis/clipboard.js.map +1 -1
- package/dist/apis/environment.d.ts.map +1 -1
- package/dist/apis/environment.js +14 -49
- package/dist/apis/environment.js.map +1 -1
- package/dist/apis/localstorage.d.ts +7 -12
- package/dist/apis/localstorage.d.ts.map +1 -1
- package/dist/apis/localstorage.js +7 -184
- package/dist/apis/localstorage.js.map +1 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +16 -15
- package/dist/app.js.map +1 -1
- package/dist/cli.js +7 -6
- package/dist/cli.js.map +1 -1
- package/dist/components/actions.d.ts.map +1 -1
- package/dist/components/actions.js +13 -2
- package/dist/components/actions.js.map +1 -1
- package/dist/components/extension-preferences.d.ts.map +1 -1
- package/dist/components/extension-preferences.js +7 -8
- package/dist/components/extension-preferences.js.map +1 -1
- package/dist/components/form/file-autocomplete.js +2 -2
- package/dist/components/form/file-autocomplete.js.map +1 -1
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +242 -14
- package/dist/components/list.js.map +1 -1
- package/dist/e2e-node.d.ts.map +1 -1
- package/dist/e2e-node.js +5 -4
- package/dist/e2e-node.js.map +1 -1
- package/dist/extensions/dev.d.ts.map +1 -1
- package/dist/extensions/dev.js +5 -2
- package/dist/extensions/dev.js.map +1 -1
- package/dist/globals.d.ts.map +1 -1
- package/dist/globals.js +2 -1
- package/dist/globals.js.map +1 -1
- package/dist/internal/error-handler.d.ts.map +1 -1
- package/dist/internal/error-handler.js +21 -19
- package/dist/internal/error-handler.js.map +1 -1
- package/dist/internal/providers.d.ts.map +1 -1
- package/dist/internal/providers.js +41 -1
- package/dist/internal/providers.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +31 -29
- package/dist/logger.js.map +1 -1
- package/dist/platform/browser/cache.d.ts +41 -0
- package/dist/platform/browser/cache.d.ts.map +1 -0
- package/dist/platform/browser/cache.js +262 -0
- package/dist/platform/browser/cache.js.map +1 -0
- package/dist/platform/browser/localstorage.d.ts +20 -0
- package/dist/platform/browser/localstorage.d.ts.map +1 -0
- package/dist/platform/browser/localstorage.js +102 -0
- package/dist/platform/browser/localstorage.js.map +1 -0
- package/dist/platform/browser/runtime.d.ts +51 -0
- package/dist/platform/browser/runtime.d.ts.map +1 -0
- package/dist/platform/browser/runtime.js +164 -0
- package/dist/platform/browser/runtime.js.map +1 -0
- package/dist/platform/bun/sqlite.d.ts +17 -0
- package/dist/platform/bun/sqlite.d.ts.map +1 -0
- package/dist/platform/bun/sqlite.js +6 -0
- package/dist/platform/bun/sqlite.js.map +1 -0
- package/dist/platform/node/cache.d.ts +35 -0
- package/dist/platform/node/cache.d.ts.map +1 -0
- package/dist/platform/node/cache.js +269 -0
- package/dist/platform/node/cache.js.map +1 -0
- package/dist/platform/node/localstorage.d.ts +17 -0
- package/dist/platform/node/localstorage.d.ts.map +1 -0
- package/dist/platform/node/localstorage.js +186 -0
- package/dist/platform/node/localstorage.js.map +1 -0
- package/dist/platform/node/runtime.d.ts +52 -0
- package/dist/platform/node/runtime.d.ts.map +1 -0
- package/dist/platform/node/runtime.js +230 -0
- package/dist/platform/node/runtime.js.map +1 -0
- package/dist/platform/node/sqlite.d.ts +27 -0
- package/dist/platform/node/sqlite.d.ts.map +1 -0
- package/dist/platform/node/sqlite.js +21 -0
- package/dist/platform/node/sqlite.js.map +1 -0
- package/dist/state.d.ts +5 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +6 -28
- package/dist/state.js.map +1 -1
- package/dist/utils/file-system.d.ts.map +1 -1
- package/dist/utils/file-system.js +17 -22
- package/dist/utils/file-system.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +42 -47
- package/dist/utils.js.map +1 -1
- package/dist/vim-mode.d.ts +40 -0
- package/dist/vim-mode.d.ts.map +1 -0
- package/dist/vim-mode.js +135 -0
- package/dist/vim-mode.js.map +1 -0
- package/fonts/Inconsolata.otf +0 -0
- package/fonts/SIL Open Font License.txt +41 -0
- package/package.json +60 -8
- package/src/action-utils.tsx +27 -124
- package/src/apis/cache.test.ts +1 -1
- package/src/apis/cache.tsx +9 -373
- package/src/apis/clipboard.tsx +29 -38
- package/src/apis/environment.tsx +25 -52
- package/src/apis/localstorage.tsx +8 -214
- package/src/app.tsx +16 -15
- package/src/cli.tsx +14 -15
- package/src/compile.vitest.tsx +2 -2
- package/src/components/actions.tsx +19 -1
- package/src/components/extension-preferences.tsx +7 -8
- package/src/components/form/file-autocomplete.tsx +2 -2
- package/src/components/list.tsx +279 -14
- package/src/e2e-node.tsx +7 -7
- package/src/examples/action-shortcut.vitest.tsx +2 -2
- package/src/examples/actions-context.vitest.tsx +1 -1
- package/src/examples/bar-graph-weekly.vitest.tsx +10 -36
- package/src/examples/detail-metadata-showcase.vitest.tsx +36 -36
- package/src/examples/form-basic.vitest.tsx +21 -17
- package/src/examples/github.vitest.tsx +4 -4
- package/src/examples/graph-bar-chart.vitest.tsx +13 -11
- package/src/examples/graph-polymarket.vitest.tsx +2 -2
- package/src/examples/graph-row.vitest.tsx +66 -66
- package/src/examples/graph-styles.vitest.tsx +12 -12
- package/src/examples/internal/simple-scrollbox.vitest.tsx +14 -48
- package/src/examples/list-detail-metadata.vitest.tsx +5 -5
- package/src/examples/list-fetch-data.vitest.tsx +3 -3
- package/src/examples/list-item-accessories.vitest.tsx +2 -2
- package/src/examples/list-loading-empty-view.vitest.tsx +1 -1
- package/src/examples/list-no-actions.vitest.tsx +2 -2
- package/src/examples/list-scrollbox.vitest.tsx +5 -5
- package/src/examples/list-spacing-mode.vitest.tsx +3 -3
- package/src/examples/list-with-detail.vitest.tsx +68 -68
- package/src/examples/list-with-dropdown.vitest.tsx +5 -5
- package/src/examples/list-with-sections.vitest.tsx +27 -27
- package/src/examples/simple-candle-chart.vitest.tsx +7 -7
- package/src/examples/simple-detail-markdown.vitest.tsx +8 -8
- package/src/examples/simple-detail-table.vitest.tsx +8 -8
- package/src/examples/simple-graph.vitest.tsx +3 -3
- package/src/examples/simple-grid.vitest.tsx +14 -14
- package/src/examples/simple-heatmap.vitest.tsx +1 -1
- package/src/examples/simple-navigation.vitest.tsx +17 -17
- package/src/examples/simple-progress-bar.vitest.tsx +1 -1
- package/src/examples/store.vitest.tsx +1 -1
- package/src/examples/swift-extension.vitest.tsx +2 -2
- package/src/examples/table-edge-cases.vitest.tsx +18 -18
- package/src/examples/toast-action.vitest.tsx +2 -2
- package/src/extensions/dev.tsx +5 -2
- package/src/extensions/dev.vitest.tsx +3 -3
- package/src/globals.ts +2 -1
- package/src/internal/error-handler.tsx +19 -21
- package/src/internal/providers.tsx +39 -0
- package/src/logger.tsx +38 -41
- package/src/platform/browser/cache.ts +327 -0
- package/src/platform/browser/localstorage.ts +119 -0
- package/src/platform/browser/runtime.ts +209 -0
- package/src/platform/bun/sqlite.ts +19 -0
- package/src/platform/node/cache.ts +372 -0
- package/src/platform/node/localstorage.ts +214 -0
- package/src/platform/node/runtime.ts +264 -0
- package/src/platform/node/sqlite.ts +43 -0
- package/src/state.tsx +17 -28
- package/src/utils/file-system.ts +17 -22
- package/src/utils.test.tsx +1 -1
- package/src/utils.tsx +56 -47
- package/src/vim-mode.tsx +153 -0
- package/src/apis/sqlite.ts +0 -14
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js / Bun platform runtime.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports native APIs behind a platform-agnostic interface so the rest of
|
|
5
|
+
* termcast never imports node:* directly (except CLI / build tooling which is
|
|
6
|
+
* excluded from the browser bundle).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'node:fs'
|
|
10
|
+
import path from 'node:path'
|
|
11
|
+
import os from 'node:os'
|
|
12
|
+
import util from 'node:util'
|
|
13
|
+
import { exec as _exec } from 'node:child_process'
|
|
14
|
+
import { promisify } from 'node:util'
|
|
15
|
+
|
|
16
|
+
const execAsync = promisify(_exec)
|
|
17
|
+
|
|
18
|
+
// ── filesystem ──────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
export function ensureDir(dir: string): void {
|
|
21
|
+
if (!fs.existsSync(dir)) {
|
|
22
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function fileExists(p: string): boolean {
|
|
27
|
+
return fs.existsSync(p)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function readFileSync(p: string): string {
|
|
31
|
+
return fs.readFileSync(p, 'utf-8')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function appendToFile(p: string, data: string): void {
|
|
35
|
+
fs.appendFileSync(p, data)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function unlinkIfExists(p: string): void {
|
|
39
|
+
if (fs.existsSync(p)) {
|
|
40
|
+
fs.unlinkSync(p)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function readdirSync(dir: string): Array<{ name: string; isDirectory(): boolean }> {
|
|
45
|
+
return fs.readdirSync(dir, { withFileTypes: true })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function rmSync(p: string): void {
|
|
49
|
+
fs.rmSync(p, { recursive: true, force: true })
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function mkdirSync(dir: string): void {
|
|
53
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function cpSync(src: string, dest: string): void {
|
|
57
|
+
fs.cpSync(src, dest, { recursive: true })
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── async filesystem ────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
import fsPromises from 'node:fs/promises'
|
|
63
|
+
|
|
64
|
+
export async function readdirAsync(dir: string): Promise<Array<{ name: string; isDirectory(): boolean }>> {
|
|
65
|
+
return fsPromises.readdir(dir, { withFileTypes: true })
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function accessAsync(p: string): Promise<boolean> {
|
|
69
|
+
try {
|
|
70
|
+
await fsPromises.access(p)
|
|
71
|
+
return true
|
|
72
|
+
} catch {
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── path ────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
export const joinPath: (...parts: string[]) => string = path.join
|
|
80
|
+
export const dirname: (p: string) => string = path.dirname
|
|
81
|
+
export const basename: (p: string) => string = path.basename
|
|
82
|
+
export const resolvePath: (...parts: string[]) => string = path.resolve
|
|
83
|
+
export const isAbsolute: (p: string) => boolean = path.isAbsolute
|
|
84
|
+
export const relativePath: (from: string, to: string) => string = path.relative
|
|
85
|
+
|
|
86
|
+
// ── os ──────────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
export function homedir(): string {
|
|
89
|
+
return os.homedir()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── process ─────────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
export const platform: string = process.platform
|
|
95
|
+
|
|
96
|
+
export function exit(code?: number): void {
|
|
97
|
+
process.exit(code)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function getEnv(key: string): string | undefined {
|
|
101
|
+
return process.env[key]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function setEnv(key: string, value: string): void {
|
|
105
|
+
process.env[key] = value
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function cwd(): string {
|
|
109
|
+
return process.cwd()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function stdoutWrite(data: string): void {
|
|
113
|
+
process.stdout.write(data)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function isTTY(): boolean {
|
|
117
|
+
return !!process.stdout.isTTY
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function getArgv(): string[] {
|
|
121
|
+
return process.argv
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── appearance ──────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
export function getSystemAppearance(): 'dark' | 'light' {
|
|
127
|
+
if (process.platform !== 'darwin') {
|
|
128
|
+
return 'light'
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const { execFileSync } = require('node:child_process') as typeof import('node:child_process')
|
|
132
|
+
const result = execFileSync('defaults', ['read', '-g', 'AppleInterfaceStyle'], {
|
|
133
|
+
encoding: 'utf-8',
|
|
134
|
+
timeout: 500,
|
|
135
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
136
|
+
})
|
|
137
|
+
return result.trim().toLowerCase() === 'dark' ? 'dark' : 'light'
|
|
138
|
+
} catch {
|
|
139
|
+
// "AppleInterfaceStyle" key doesn't exist when in light mode
|
|
140
|
+
return 'light'
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ── util ────────────────────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
export function byteLength(str: string): number {
|
|
147
|
+
return Buffer.byteLength(str, 'utf-8')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function inspectValue(val: unknown, depth = 3): string {
|
|
151
|
+
return util.inspect(val, { depth })
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ── error handling ──────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
export function setupErrorHandlers(handler: (err: Error, type: string) => void): void {
|
|
157
|
+
process.on('uncaughtException', (err) => {
|
|
158
|
+
handler(err, 'uncaughtException')
|
|
159
|
+
})
|
|
160
|
+
process.on('unhandledRejection', (reason) => {
|
|
161
|
+
handler(
|
|
162
|
+
reason instanceof Error ? reason : new Error(String(reason)),
|
|
163
|
+
'unhandledRejection',
|
|
164
|
+
)
|
|
165
|
+
})
|
|
166
|
+
process.on('uncaughtExceptionMonitor', (err, origin) => {
|
|
167
|
+
handler(err, origin)
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ── shell / clipboard ───────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
export function execWithInput(command: string, input: string): Promise<void> {
|
|
174
|
+
return new Promise((resolve, reject) => {
|
|
175
|
+
const child = _exec(command, (error) => {
|
|
176
|
+
if (error) {
|
|
177
|
+
reject(error)
|
|
178
|
+
} else {
|
|
179
|
+
resolve()
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
child.stdin?.write(input)
|
|
183
|
+
child.stdin?.end()
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export async function execCommand(command: string): Promise<string> {
|
|
188
|
+
const { stdout } = await execAsync(command)
|
|
189
|
+
return stdout
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export async function copyToClipboard(text: string): Promise<void> {
|
|
193
|
+
if (process.platform === 'darwin') {
|
|
194
|
+
await execWithInput('pbcopy', text)
|
|
195
|
+
} else if (process.platform === 'linux') {
|
|
196
|
+
await execWithInput('xclip -selection clipboard', text)
|
|
197
|
+
} else if (process.platform === 'win32') {
|
|
198
|
+
await execWithInput('clip', text)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export async function readClipboard(): Promise<string> {
|
|
203
|
+
if (process.platform === 'darwin') {
|
|
204
|
+
return (await execAsync('pbpaste')).stdout
|
|
205
|
+
} else if (process.platform === 'linux') {
|
|
206
|
+
return (await execAsync('xclip -selection clipboard -o')).stdout
|
|
207
|
+
} else if (process.platform === 'win32') {
|
|
208
|
+
return (await execAsync('powershell -command "Get-Clipboard"')).stdout
|
|
209
|
+
}
|
|
210
|
+
return ''
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export async function openUrl(url: string): Promise<void> {
|
|
214
|
+
if (process.platform === 'darwin') {
|
|
215
|
+
await execAsync(`open "${url}"`)
|
|
216
|
+
} else if (process.platform === 'linux') {
|
|
217
|
+
await execAsync(`xdg-open "${url}"`)
|
|
218
|
+
} else if (process.platform === 'win32') {
|
|
219
|
+
await execAsync(`start "${url}"`)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export async function openFile(target: string, app?: string): Promise<void> {
|
|
224
|
+
if (process.platform === 'darwin') {
|
|
225
|
+
if (app) {
|
|
226
|
+
await execAsync(`open -a "${app}" "${target}"`)
|
|
227
|
+
} else {
|
|
228
|
+
await execAsync(`open "${target}"`)
|
|
229
|
+
}
|
|
230
|
+
} else if (process.platform === 'linux') {
|
|
231
|
+
await execAsync(`xdg-open "${target}"`)
|
|
232
|
+
} else if (process.platform === 'win32') {
|
|
233
|
+
await execAsync(`start "" "${target}"`)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export async function showInFileManager(filePath: string): Promise<void> {
|
|
238
|
+
if (process.platform === 'darwin') {
|
|
239
|
+
await execAsync(`open -R "${filePath}"`)
|
|
240
|
+
} else if (process.platform === 'linux') {
|
|
241
|
+
await execAsync(`xdg-open "$(dirname "${filePath}")"`)
|
|
242
|
+
} else if (process.platform === 'win32') {
|
|
243
|
+
await execAsync(`explorer /select,"${filePath}"`)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export async function moveToTrash(filePath: string): Promise<void> {
|
|
248
|
+
if (process.platform === 'darwin') {
|
|
249
|
+
await execAsync(
|
|
250
|
+
`osascript -e 'tell application "Finder" to delete POSIX file "${filePath}"'`,
|
|
251
|
+
)
|
|
252
|
+
} else if (process.platform === 'linux') {
|
|
253
|
+
try {
|
|
254
|
+
await execAsync(`gio trash "${filePath}"`)
|
|
255
|
+
} catch {
|
|
256
|
+
const trashDir = `${os.homedir()}/.local/share/Trash/files`
|
|
257
|
+
await execAsync(`mkdir -p "${trashDir}" && mv "${filePath}" "${trashDir}/"`)
|
|
258
|
+
}
|
|
259
|
+
} else if (process.platform === 'win32') {
|
|
260
|
+
await execAsync(
|
|
261
|
+
`powershell -command "Add-Type -AssemblyName Microsoft.VisualBasic; [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile('${filePath}','OnlyErrorDialogs','SendToRecycleBin')"`,
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite Database for Node.js — uses better-sqlite3.
|
|
3
|
+
*
|
|
4
|
+
* Translates bun:sqlite constructor options ({ create, readwrite })
|
|
5
|
+
* to better-sqlite3 options ({ fileMustExist, readonly }).
|
|
6
|
+
* All other APIs (prepare/get/all/run/exec/transaction/close) are
|
|
7
|
+
* identical between the two libraries.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import BetterSqlite3 from 'better-sqlite3'
|
|
11
|
+
|
|
12
|
+
interface DatabaseOptions {
|
|
13
|
+
create?: boolean
|
|
14
|
+
readwrite?: boolean
|
|
15
|
+
readonly?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function NodeDatabase(path: string, options?: DatabaseOptions) {
|
|
19
|
+
const betterOpts: { fileMustExist?: boolean; readonly?: boolean } = {}
|
|
20
|
+
|
|
21
|
+
if (options?.create === false) {
|
|
22
|
+
betterOpts.fileMustExist = true
|
|
23
|
+
}
|
|
24
|
+
if (options?.readonly === true || options?.readwrite === false) {
|
|
25
|
+
betterOpts.readonly = true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return new BetterSqlite3(path, betterOpts)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const Database: new (path: string, options?: DatabaseOptions) => DatabaseInstance = NodeDatabase as any
|
|
32
|
+
|
|
33
|
+
/** Instance type returned by `new Database(...)` */
|
|
34
|
+
export type DatabaseInstance = {
|
|
35
|
+
exec(sql: string): void
|
|
36
|
+
prepare(sql: string): {
|
|
37
|
+
get(...params: any[]): any
|
|
38
|
+
all(...params: any[]): any[]
|
|
39
|
+
run(...params: any[]): any
|
|
40
|
+
}
|
|
41
|
+
transaction<T>(fn: () => T): () => T
|
|
42
|
+
close(): void
|
|
43
|
+
}
|
package/src/state.tsx
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
1
|
import { create } from 'zustand'
|
|
3
2
|
import { type ReactNode } from 'react'
|
|
4
3
|
import type { TextareaRenderable } from '@opentui/core'
|
|
@@ -6,6 +5,7 @@ import type { RaycastPackageJson } from './package-json'
|
|
|
6
5
|
import type { KeyboardKeyEquivalent, KeyboardKeyModifier } from 'termcast/src/keyboard'
|
|
7
6
|
import { getResolvedTheme } from './themes'
|
|
8
7
|
import { logger } from './logger'
|
|
8
|
+
import { getEnv } from '#platform/runtime'
|
|
9
9
|
|
|
10
10
|
// Registered action shortcuts for global keyboard handling
|
|
11
11
|
// Stored by ActionPanel, consumed by List/Detail/Form keyboard handlers
|
|
@@ -52,7 +52,17 @@ export interface NavigationStackItem {
|
|
|
52
52
|
searchText?: string
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// Vim mode types
|
|
56
|
+
// inputMode: global mode persisted across sessions
|
|
57
|
+
// vimInputSubMode: transient sub-state within the current mode
|
|
58
|
+
export type InputMode = 'raycast' | 'vim'
|
|
59
|
+
export type VimInputSubMode = 'default' | 'search' | 'command'
|
|
60
|
+
|
|
55
61
|
interface AppState {
|
|
62
|
+
// Vim mode state
|
|
63
|
+
inputMode: InputMode
|
|
64
|
+
vimInputSubMode: VimInputSubMode
|
|
65
|
+
vimCommandText: string
|
|
56
66
|
toast: ToastData | null
|
|
57
67
|
toastWithPrimaryAction: boolean
|
|
58
68
|
dialogStack: DialogStackItem[]
|
|
@@ -96,6 +106,10 @@ interface AppState {
|
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
export const useStore = create<AppState>(() => ({
|
|
109
|
+
// Vim mode — initialized from persistence in initializeVimMode()
|
|
110
|
+
inputMode: 'raycast',
|
|
111
|
+
vimInputSubMode: 'default',
|
|
112
|
+
vimCommandText: '',
|
|
99
113
|
toast: null,
|
|
100
114
|
toastWithPrimaryAction: false,
|
|
101
115
|
dialogStack: [],
|
|
@@ -120,36 +134,11 @@ export const useStore = create<AppState>(() => ({
|
|
|
120
134
|
showActionsDialog: false,
|
|
121
135
|
actionsPortalTarget: null,
|
|
122
136
|
// Theme state — TERMCAST_DEFAULT_THEME env var is set by the app launcher
|
|
123
|
-
currentThemeName:
|
|
137
|
+
currentThemeName: getEnv('TERMCAST_DEFAULT_THEME') || 'nerv',
|
|
124
138
|
// Active search input ref
|
|
125
139
|
activeSearchInputRef: null,
|
|
126
140
|
// Registered action shortcuts
|
|
127
141
|
registeredActionShortcuts: [],
|
|
128
142
|
}))
|
|
129
143
|
|
|
130
|
-
|
|
131
|
-
// When the theme changes, rewrite the background color in wezterm.lua.
|
|
132
|
-
// WezTerm auto-reloads the config on file change, updating the window edges/padding.
|
|
133
|
-
// The config path is passed from the launcher via TERMCAST_WEZTERM_CONFIG env var.
|
|
134
|
-
const weztermConfigPath = process.env.TERMCAST_WEZTERM_CONFIG
|
|
135
|
-
if (weztermConfigPath) {
|
|
136
|
-
useStore.subscribe((state, prevState) => {
|
|
137
|
-
if (state.currentThemeName === prevState.currentThemeName) {
|
|
138
|
-
return
|
|
139
|
-
}
|
|
140
|
-
try {
|
|
141
|
-
const theme = getResolvedTheme(state.currentThemeName)
|
|
142
|
-
const content = fs.readFileSync(weztermConfigPath, 'utf-8')
|
|
143
|
-
// Replace the background hex in: config.colors = { background = '#xxxxxx' }
|
|
144
|
-
const updated = content.replace(
|
|
145
|
-
/background\s*=\s*'#[0-9a-fA-F]{6}'/,
|
|
146
|
-
`background = '${theme.background}'`,
|
|
147
|
-
)
|
|
148
|
-
if (updated !== content) {
|
|
149
|
-
fs.writeFileSync(weztermConfigPath, updated)
|
|
150
|
-
}
|
|
151
|
-
} catch (e) {
|
|
152
|
-
logger.log('Failed to update wezterm config background:', e)
|
|
153
|
-
}
|
|
154
|
-
})
|
|
155
|
-
}
|
|
144
|
+
|
package/src/utils/file-system.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as path from 'path'
|
|
3
|
-
import * as os from 'os'
|
|
1
|
+
import { homedir, isAbsolute, resolvePath, joinPath, cwd, relativePath, readdirAsync, accessAsync } from '#platform/runtime'
|
|
4
2
|
|
|
5
3
|
export interface FileSystemItem {
|
|
6
4
|
name: string
|
|
@@ -26,18 +24,18 @@ export async function listAllFiles({
|
|
|
26
24
|
|
|
27
25
|
// Resolve ~ to home directory
|
|
28
26
|
if (basePath.startsWith('~')) {
|
|
29
|
-
basePath = basePath.replace('~',
|
|
27
|
+
basePath = basePath.replace('~', homedir())
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
const resolvedBase =
|
|
30
|
+
const resolvedBase = isAbsolute(basePath)
|
|
33
31
|
? basePath
|
|
34
|
-
:
|
|
32
|
+
: resolvePath(cwd(), basePath)
|
|
35
33
|
|
|
36
34
|
async function walk(dir: string, depth: number) {
|
|
37
35
|
if (depth > maxDepth || results.length >= maxFiles) return
|
|
38
36
|
|
|
39
37
|
try {
|
|
40
|
-
const entries = await
|
|
38
|
+
const entries = await readdirAsync(dir)
|
|
41
39
|
|
|
42
40
|
for (const entry of entries) {
|
|
43
41
|
if (results.length >= maxFiles) break
|
|
@@ -48,16 +46,16 @@ export async function listAllFiles({
|
|
|
48
46
|
if (entry.name === 'dist') continue
|
|
49
47
|
if (entry.name === 'build') continue
|
|
50
48
|
|
|
51
|
-
const fullPath =
|
|
52
|
-
const
|
|
49
|
+
const fullPath = joinPath(dir, entry.name)
|
|
50
|
+
const relPath = relativePath(resolvedBase, fullPath)
|
|
53
51
|
|
|
54
52
|
if (entry.isDirectory()) {
|
|
55
53
|
if (includeDirectories) {
|
|
56
|
-
results.push(
|
|
54
|
+
results.push(relPath + '/')
|
|
57
55
|
}
|
|
58
56
|
await walk(fullPath, depth + 1)
|
|
59
57
|
} else {
|
|
60
|
-
results.push(
|
|
58
|
+
results.push(relPath)
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
61
|
} catch {
|
|
@@ -76,23 +74,20 @@ export async function searchFiles(
|
|
|
76
74
|
try {
|
|
77
75
|
// Resolve ~ to home directory
|
|
78
76
|
if (searchPath.startsWith('~')) {
|
|
79
|
-
searchPath = searchPath.replace('~',
|
|
77
|
+
searchPath = searchPath.replace('~', homedir())
|
|
80
78
|
}
|
|
81
79
|
|
|
82
80
|
// Handle absolute vs relative paths
|
|
83
|
-
const basePath =
|
|
81
|
+
const basePath = isAbsolute(searchPath)
|
|
84
82
|
? searchPath
|
|
85
|
-
:
|
|
83
|
+
: resolvePath(cwd(), searchPath || '.')
|
|
86
84
|
|
|
87
85
|
// Check if directory exists
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
} catch {
|
|
91
|
-
return []
|
|
92
|
-
}
|
|
86
|
+
const exists = await accessAsync(basePath)
|
|
87
|
+
if (!exists) return []
|
|
93
88
|
|
|
94
89
|
// List files and directories
|
|
95
|
-
const entries = await
|
|
90
|
+
const entries = await readdirAsync(basePath)
|
|
96
91
|
|
|
97
92
|
const items: FileSystemItem[] = []
|
|
98
93
|
|
|
@@ -107,7 +102,7 @@ export async function searchFiles(
|
|
|
107
102
|
) {
|
|
108
103
|
items.push({
|
|
109
104
|
name: entry.name,
|
|
110
|
-
path:
|
|
105
|
+
path: joinPath(basePath, entry.name),
|
|
111
106
|
isDirectory: entry.isDirectory(),
|
|
112
107
|
})
|
|
113
108
|
}
|
|
@@ -130,7 +125,7 @@ export async function searchFiles(
|
|
|
130
125
|
export function parsePath(input: string): { basePath: string; prefix: string } {
|
|
131
126
|
// Handle ~ expansion
|
|
132
127
|
if (input.startsWith('~')) {
|
|
133
|
-
input = input.replace('~',
|
|
128
|
+
input = input.replace('~', homedir())
|
|
134
129
|
}
|
|
135
130
|
|
|
136
131
|
const lastSlashIndex = input.lastIndexOf('/')
|