@jonsoc/app 1.1.34
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 +30 -0
- package/README.md +51 -0
- package/bunfig.toml +2 -0
- package/e2e/context.spec.ts +45 -0
- package/e2e/file-open.spec.ts +23 -0
- package/e2e/file-viewer.spec.ts +35 -0
- package/e2e/fixtures.ts +40 -0
- package/e2e/home.spec.ts +21 -0
- package/e2e/model-picker.spec.ts +43 -0
- package/e2e/navigation.spec.ts +9 -0
- package/e2e/palette.spec.ts +15 -0
- package/e2e/prompt-mention.spec.ts +26 -0
- package/e2e/prompt-slash-open.spec.ts +22 -0
- package/e2e/prompt.spec.ts +62 -0
- package/e2e/session.spec.ts +21 -0
- package/e2e/settings.spec.ts +44 -0
- package/e2e/sidebar.spec.ts +21 -0
- package/e2e/terminal-init.spec.ts +25 -0
- package/e2e/terminal.spec.ts +16 -0
- package/e2e/tsconfig.json +8 -0
- package/e2e/utils.ts +38 -0
- package/happydom.ts +75 -0
- package/index.html +23 -0
- package/package.json +72 -0
- package/playwright.config.ts +43 -0
- package/public/_headers +17 -0
- package/public/apple-touch-icon-v3.png +1 -0
- package/public/apple-touch-icon.png +1 -0
- package/public/favicon-96x96-v3.png +1 -0
- package/public/favicon-96x96.png +1 -0
- package/public/favicon-v3.ico +1 -0
- package/public/favicon-v3.svg +1 -0
- package/public/favicon.ico +1 -0
- package/public/favicon.svg +1 -0
- package/public/oc-theme-preload.js +28 -0
- package/public/site.webmanifest +1 -0
- package/public/social-share-zen.png +1 -0
- package/public/social-share.png +1 -0
- package/public/web-app-manifest-192x192.png +1 -0
- package/public/web-app-manifest-512x512.png +1 -0
- package/script/e2e-local.ts +143 -0
- package/src/addons/serialize.test.ts +319 -0
- package/src/addons/serialize.ts +591 -0
- package/src/app.tsx +150 -0
- package/src/components/dialog-connect-provider.tsx +428 -0
- package/src/components/dialog-edit-project.tsx +259 -0
- package/src/components/dialog-fork.tsx +104 -0
- package/src/components/dialog-manage-models.tsx +59 -0
- package/src/components/dialog-select-directory.tsx +208 -0
- package/src/components/dialog-select-file.tsx +196 -0
- package/src/components/dialog-select-mcp.tsx +96 -0
- package/src/components/dialog-select-model-unpaid.tsx +130 -0
- package/src/components/dialog-select-model.tsx +162 -0
- package/src/components/dialog-select-provider.tsx +70 -0
- package/src/components/dialog-select-server.tsx +249 -0
- package/src/components/dialog-settings.tsx +112 -0
- package/src/components/file-tree.tsx +112 -0
- package/src/components/link.tsx +17 -0
- package/src/components/model-tooltip.tsx +91 -0
- package/src/components/prompt-input.tsx +2076 -0
- package/src/components/session/index.ts +5 -0
- package/src/components/session/session-context-tab.tsx +428 -0
- package/src/components/session/session-header.tsx +343 -0
- package/src/components/session/session-new-view.tsx +93 -0
- package/src/components/session/session-sortable-tab.tsx +56 -0
- package/src/components/session/session-sortable-terminal-tab.tsx +187 -0
- package/src/components/session-context-usage.tsx +113 -0
- package/src/components/session-lsp-indicator.tsx +42 -0
- package/src/components/session-mcp-indicator.tsx +34 -0
- package/src/components/settings-agents.tsx +15 -0
- package/src/components/settings-commands.tsx +15 -0
- package/src/components/settings-general.tsx +306 -0
- package/src/components/settings-keybinds.tsx +437 -0
- package/src/components/settings-mcp.tsx +15 -0
- package/src/components/settings-models.tsx +15 -0
- package/src/components/settings-permissions.tsx +234 -0
- package/src/components/settings-providers.tsx +15 -0
- package/src/components/terminal.tsx +315 -0
- package/src/components/titlebar.tsx +156 -0
- package/src/context/command.tsx +308 -0
- package/src/context/comments.tsx +140 -0
- package/src/context/file.tsx +409 -0
- package/src/context/global-sdk.tsx +106 -0
- package/src/context/global-sync.tsx +898 -0
- package/src/context/language.tsx +161 -0
- package/src/context/layout-scroll.test.ts +73 -0
- package/src/context/layout-scroll.ts +118 -0
- package/src/context/layout.tsx +648 -0
- package/src/context/local.tsx +578 -0
- package/src/context/notification.tsx +173 -0
- package/src/context/permission.tsx +167 -0
- package/src/context/platform.tsx +59 -0
- package/src/context/prompt.tsx +245 -0
- package/src/context/sdk.tsx +48 -0
- package/src/context/server.tsx +214 -0
- package/src/context/settings.tsx +166 -0
- package/src/context/sync.tsx +320 -0
- package/src/context/terminal.tsx +267 -0
- package/src/custom-elements.d.ts +17 -0
- package/src/entry.tsx +76 -0
- package/src/env.d.ts +8 -0
- package/src/hooks/use-providers.ts +31 -0
- package/src/i18n/ar.ts +656 -0
- package/src/i18n/br.ts +667 -0
- package/src/i18n/da.ts +582 -0
- package/src/i18n/de.ts +591 -0
- package/src/i18n/en.ts +665 -0
- package/src/i18n/es.ts +585 -0
- package/src/i18n/fr.ts +592 -0
- package/src/i18n/ja.ts +579 -0
- package/src/i18n/ko.ts +580 -0
- package/src/i18n/no.ts +602 -0
- package/src/i18n/pl.ts +661 -0
- package/src/i18n/ru.ts +664 -0
- package/src/i18n/zh.ts +574 -0
- package/src/i18n/zht.ts +570 -0
- package/src/index.css +57 -0
- package/src/index.ts +2 -0
- package/src/pages/directory-layout.tsx +57 -0
- package/src/pages/error.tsx +290 -0
- package/src/pages/home.tsx +125 -0
- package/src/pages/layout.tsx +2599 -0
- package/src/pages/session.tsx +2505 -0
- package/src/sst-env.d.ts +10 -0
- package/src/utils/dom.ts +51 -0
- package/src/utils/id.ts +99 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/perf.ts +135 -0
- package/src/utils/persist.ts +377 -0
- package/src/utils/prompt.ts +203 -0
- package/src/utils/same.ts +6 -0
- package/src/utils/solid-dnd.tsx +55 -0
- package/src/utils/sound.ts +110 -0
- package/src/utils/speech.ts +302 -0
- package/src/utils/worktree.ts +58 -0
- package/sst-env.d.ts +9 -0
- package/tsconfig.json +26 -0
- package/vite.config.ts +15 -0
- package/vite.js +26 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { createStore, produce } from "solid-js/store"
|
|
2
|
+
import { createSimpleContext } from "@jonsoc/ui/context"
|
|
3
|
+
import { batch, createEffect, createMemo, createRoot, onCleanup } from "solid-js"
|
|
4
|
+
import { useParams } from "@solidjs/router"
|
|
5
|
+
import { useSDK } from "./sdk"
|
|
6
|
+
import { Persist, persisted } from "@/utils/persist"
|
|
7
|
+
|
|
8
|
+
export type LocalPTY = {
|
|
9
|
+
id: string
|
|
10
|
+
title: string
|
|
11
|
+
titleNumber: number
|
|
12
|
+
rows?: number
|
|
13
|
+
cols?: number
|
|
14
|
+
buffer?: string
|
|
15
|
+
scrollY?: number
|
|
16
|
+
error?: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const WORKSPACE_KEY = "__workspace__"
|
|
20
|
+
const MAX_TERMINAL_SESSIONS = 20
|
|
21
|
+
|
|
22
|
+
type TerminalSession = ReturnType<typeof createTerminalSession>
|
|
23
|
+
|
|
24
|
+
type TerminalCacheEntry = {
|
|
25
|
+
value: TerminalSession
|
|
26
|
+
dispose: VoidFunction
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function createTerminalSession(sdk: ReturnType<typeof useSDK>, dir: string, session?: string) {
|
|
30
|
+
const legacy = session ? [`${dir}/terminal/${session}.v1`, `${dir}/terminal.v1`] : [`${dir}/terminal.v1`]
|
|
31
|
+
|
|
32
|
+
const numberFromTitle = (title: string) => {
|
|
33
|
+
const match = title.match(/^Terminal (\d+)$/)
|
|
34
|
+
if (!match) return
|
|
35
|
+
const value = Number(match[1])
|
|
36
|
+
if (!Number.isFinite(value) || value <= 0) return
|
|
37
|
+
return value
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const [store, setStore, _, ready] = persisted(
|
|
41
|
+
Persist.workspace(dir, "terminal", legacy),
|
|
42
|
+
createStore<{
|
|
43
|
+
active?: string
|
|
44
|
+
all: LocalPTY[]
|
|
45
|
+
}>({
|
|
46
|
+
all: [],
|
|
47
|
+
}),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
const unsub = sdk.event.on("pty.exited", (event) => {
|
|
51
|
+
const id = event.properties.id
|
|
52
|
+
if (!store.all.some((x) => x.id === id)) return
|
|
53
|
+
batch(() => {
|
|
54
|
+
setStore(
|
|
55
|
+
"all",
|
|
56
|
+
store.all.filter((x) => x.id !== id),
|
|
57
|
+
)
|
|
58
|
+
if (store.active === id) {
|
|
59
|
+
const remaining = store.all.filter((x) => x.id !== id)
|
|
60
|
+
setStore("active", remaining[0]?.id)
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
onCleanup(unsub)
|
|
65
|
+
|
|
66
|
+
const meta = { migrated: false }
|
|
67
|
+
|
|
68
|
+
createEffect(() => {
|
|
69
|
+
if (!ready()) return
|
|
70
|
+
if (meta.migrated) return
|
|
71
|
+
meta.migrated = true
|
|
72
|
+
|
|
73
|
+
setStore("all", (all) => {
|
|
74
|
+
const next = all.map((pty) => {
|
|
75
|
+
const direct = Number.isFinite(pty.titleNumber) && pty.titleNumber > 0 ? pty.titleNumber : undefined
|
|
76
|
+
if (direct !== undefined) return pty
|
|
77
|
+
const parsed = numberFromTitle(pty.title)
|
|
78
|
+
if (parsed === undefined) return pty
|
|
79
|
+
return { ...pty, titleNumber: parsed }
|
|
80
|
+
})
|
|
81
|
+
if (next.every((pty, index) => pty === all[index])) return all
|
|
82
|
+
return next
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
ready,
|
|
88
|
+
all: createMemo(() => Object.values(store.all)),
|
|
89
|
+
active: createMemo(() => store.active),
|
|
90
|
+
new() {
|
|
91
|
+
const existingTitleNumbers = new Set(
|
|
92
|
+
store.all.flatMap((pty) => {
|
|
93
|
+
const direct = Number.isFinite(pty.titleNumber) && pty.titleNumber > 0 ? pty.titleNumber : undefined
|
|
94
|
+
if (direct !== undefined) return [direct]
|
|
95
|
+
const parsed = numberFromTitle(pty.title)
|
|
96
|
+
if (parsed === undefined) return []
|
|
97
|
+
return [parsed]
|
|
98
|
+
}),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
const nextNumber =
|
|
102
|
+
Array.from({ length: existingTitleNumbers.size + 1 }, (_, index) => index + 1).find(
|
|
103
|
+
(number) => !existingTitleNumbers.has(number),
|
|
104
|
+
) ?? 1
|
|
105
|
+
|
|
106
|
+
sdk.client.pty
|
|
107
|
+
.create({ title: `Terminal ${nextNumber}` })
|
|
108
|
+
.then((pty) => {
|
|
109
|
+
const id = pty.data?.id
|
|
110
|
+
if (!id) return
|
|
111
|
+
const newTerminal = {
|
|
112
|
+
id,
|
|
113
|
+
title: pty.data?.title ?? "Terminal",
|
|
114
|
+
titleNumber: nextNumber,
|
|
115
|
+
}
|
|
116
|
+
setStore("all", (all) => {
|
|
117
|
+
const newAll = [...all, newTerminal]
|
|
118
|
+
return newAll
|
|
119
|
+
})
|
|
120
|
+
setStore("active", id)
|
|
121
|
+
})
|
|
122
|
+
.catch((e) => {
|
|
123
|
+
console.error("Failed to create terminal", e)
|
|
124
|
+
})
|
|
125
|
+
},
|
|
126
|
+
update(pty: Partial<LocalPTY> & { id: string }) {
|
|
127
|
+
const index = store.all.findIndex((x) => x.id === pty.id)
|
|
128
|
+
if (index !== -1) {
|
|
129
|
+
setStore("all", index, (existing) => ({ ...existing, ...pty }))
|
|
130
|
+
}
|
|
131
|
+
sdk.client.pty
|
|
132
|
+
.update({
|
|
133
|
+
ptyID: pty.id,
|
|
134
|
+
title: pty.title,
|
|
135
|
+
size: pty.cols && pty.rows ? { rows: pty.rows, cols: pty.cols } : undefined,
|
|
136
|
+
})
|
|
137
|
+
.catch((e) => {
|
|
138
|
+
console.error("Failed to update terminal", e)
|
|
139
|
+
})
|
|
140
|
+
},
|
|
141
|
+
async clone(id: string) {
|
|
142
|
+
const index = store.all.findIndex((x) => x.id === id)
|
|
143
|
+
const pty = store.all[index]
|
|
144
|
+
if (!pty) return
|
|
145
|
+
const clone = await sdk.client.pty
|
|
146
|
+
.create({
|
|
147
|
+
title: pty.title,
|
|
148
|
+
})
|
|
149
|
+
.catch((e) => {
|
|
150
|
+
console.error("Failed to clone terminal", e)
|
|
151
|
+
return undefined
|
|
152
|
+
})
|
|
153
|
+
if (!clone?.data) return
|
|
154
|
+
setStore("all", index, {
|
|
155
|
+
...pty,
|
|
156
|
+
...clone.data,
|
|
157
|
+
})
|
|
158
|
+
if (store.active === pty.id) {
|
|
159
|
+
setStore("active", clone.data.id)
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
open(id: string) {
|
|
163
|
+
setStore("active", id)
|
|
164
|
+
},
|
|
165
|
+
next() {
|
|
166
|
+
const index = store.all.findIndex((x) => x.id === store.active)
|
|
167
|
+
if (index === -1) return
|
|
168
|
+
const nextIndex = (index + 1) % store.all.length
|
|
169
|
+
setStore("active", store.all[nextIndex]?.id)
|
|
170
|
+
},
|
|
171
|
+
previous() {
|
|
172
|
+
const index = store.all.findIndex((x) => x.id === store.active)
|
|
173
|
+
if (index === -1) return
|
|
174
|
+
const prevIndex = index === 0 ? store.all.length - 1 : index - 1
|
|
175
|
+
setStore("active", store.all[prevIndex]?.id)
|
|
176
|
+
},
|
|
177
|
+
async close(id: string) {
|
|
178
|
+
batch(() => {
|
|
179
|
+
const filtered = store.all.filter((x) => x.id !== id)
|
|
180
|
+
if (store.active === id) {
|
|
181
|
+
const index = store.all.findIndex((f) => f.id === id)
|
|
182
|
+
const next = index > 0 ? index - 1 : 0
|
|
183
|
+
setStore("active", filtered[next]?.id)
|
|
184
|
+
}
|
|
185
|
+
setStore("all", filtered)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
await sdk.client.pty.remove({ ptyID: id }).catch((e) => {
|
|
189
|
+
console.error("Failed to close terminal", e)
|
|
190
|
+
})
|
|
191
|
+
},
|
|
192
|
+
move(id: string, to: number) {
|
|
193
|
+
const index = store.all.findIndex((f) => f.id === id)
|
|
194
|
+
if (index === -1) return
|
|
195
|
+
setStore(
|
|
196
|
+
"all",
|
|
197
|
+
produce((all) => {
|
|
198
|
+
all.splice(to, 0, all.splice(index, 1)[0])
|
|
199
|
+
}),
|
|
200
|
+
)
|
|
201
|
+
},
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export const { use: useTerminal, provider: TerminalProvider } = createSimpleContext({
|
|
206
|
+
name: "Terminal",
|
|
207
|
+
gate: false,
|
|
208
|
+
init: () => {
|
|
209
|
+
const sdk = useSDK()
|
|
210
|
+
const params = useParams()
|
|
211
|
+
const cache = new Map<string, TerminalCacheEntry>()
|
|
212
|
+
|
|
213
|
+
const disposeAll = () => {
|
|
214
|
+
for (const entry of cache.values()) {
|
|
215
|
+
entry.dispose()
|
|
216
|
+
}
|
|
217
|
+
cache.clear()
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
onCleanup(disposeAll)
|
|
221
|
+
|
|
222
|
+
const prune = () => {
|
|
223
|
+
while (cache.size > MAX_TERMINAL_SESSIONS) {
|
|
224
|
+
const first = cache.keys().next().value
|
|
225
|
+
if (!first) return
|
|
226
|
+
const entry = cache.get(first)
|
|
227
|
+
entry?.dispose()
|
|
228
|
+
cache.delete(first)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const load = (dir: string, session?: string) => {
|
|
233
|
+
const key = `${dir}:${WORKSPACE_KEY}`
|
|
234
|
+
const existing = cache.get(key)
|
|
235
|
+
if (existing) {
|
|
236
|
+
cache.delete(key)
|
|
237
|
+
cache.set(key, existing)
|
|
238
|
+
return existing.value
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const entry = createRoot((dispose) => ({
|
|
242
|
+
value: createTerminalSession(sdk, dir, session),
|
|
243
|
+
dispose,
|
|
244
|
+
}))
|
|
245
|
+
|
|
246
|
+
cache.set(key, entry)
|
|
247
|
+
prune()
|
|
248
|
+
return entry.value
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const workspace = createMemo(() => load(params.dir!, params.id))
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
ready: () => workspace().ready(),
|
|
255
|
+
all: () => workspace().all(),
|
|
256
|
+
active: () => workspace().active(),
|
|
257
|
+
new: () => workspace().new(),
|
|
258
|
+
update: (pty: Partial<LocalPTY> & { id: string }) => workspace().update(pty),
|
|
259
|
+
clone: (id: string) => workspace().clone(id),
|
|
260
|
+
open: (id: string) => workspace().open(id),
|
|
261
|
+
close: (id: string) => workspace().close(id),
|
|
262
|
+
move: (id: string, to: number) => workspace().move(id, to),
|
|
263
|
+
next: () => workspace().next(),
|
|
264
|
+
previous: () => workspace().previous(),
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DIFFS_TAG_NAME } from "@pierre/diffs"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TypeScript declaration for the <diffs-container> custom element.
|
|
5
|
+
* This tells TypeScript that <diffs-container> is a valid JSX element in SolidJS.
|
|
6
|
+
* Required for using the @pierre/diffs web component in .tsx files.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
declare module "solid-js" {
|
|
10
|
+
namespace JSX {
|
|
11
|
+
interface IntrinsicElements {
|
|
12
|
+
[DIFFS_TAG_NAME]: HTMLAttributes<HTMLElement>
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export {}
|
package/src/entry.tsx
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// @refresh reload
|
|
2
|
+
import { render } from "solid-js/web"
|
|
3
|
+
import { AppBaseProviders, AppInterface } from "@/app"
|
|
4
|
+
import { Platform, PlatformProvider } from "@/context/platform"
|
|
5
|
+
import { dict as en } from "@/i18n/en"
|
|
6
|
+
import { dict as zh } from "@/i18n/zh"
|
|
7
|
+
import pkg from "../package.json"
|
|
8
|
+
|
|
9
|
+
const root = document.getElementById("root")
|
|
10
|
+
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
|
11
|
+
const locale = (() => {
|
|
12
|
+
if (typeof navigator !== "object") return "en" as const
|
|
13
|
+
const languages = navigator.languages?.length ? navigator.languages : [navigator.language]
|
|
14
|
+
for (const language of languages) {
|
|
15
|
+
if (!language) continue
|
|
16
|
+
if (language.toLowerCase().startsWith("zh")) return "zh" as const
|
|
17
|
+
}
|
|
18
|
+
return "en" as const
|
|
19
|
+
})()
|
|
20
|
+
|
|
21
|
+
const key = "error.dev.rootNotFound" as const
|
|
22
|
+
const message = locale === "zh" ? (zh[key] ?? en[key]) : en[key]
|
|
23
|
+
throw new Error(message)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const platform: Platform = {
|
|
27
|
+
platform: "web",
|
|
28
|
+
version: pkg.version,
|
|
29
|
+
openLink(url: string) {
|
|
30
|
+
window.open(url, "_blank")
|
|
31
|
+
},
|
|
32
|
+
restart: async () => {
|
|
33
|
+
window.location.reload()
|
|
34
|
+
},
|
|
35
|
+
notify: async (title, description, href) => {
|
|
36
|
+
if (!("Notification" in window)) return
|
|
37
|
+
|
|
38
|
+
const permission =
|
|
39
|
+
Notification.permission === "default"
|
|
40
|
+
? await Notification.requestPermission().catch(() => "denied")
|
|
41
|
+
: Notification.permission
|
|
42
|
+
|
|
43
|
+
if (permission !== "granted") return
|
|
44
|
+
|
|
45
|
+
const inView = document.visibilityState === "visible" && document.hasFocus()
|
|
46
|
+
if (inView) return
|
|
47
|
+
|
|
48
|
+
await Promise.resolve()
|
|
49
|
+
.then(() => {
|
|
50
|
+
const notification = new Notification(title, {
|
|
51
|
+
body: description ?? "",
|
|
52
|
+
icon: "https://jonsoc.com/favicon-96x96-v3.png",
|
|
53
|
+
})
|
|
54
|
+
notification.onclick = () => {
|
|
55
|
+
window.focus()
|
|
56
|
+
if (href) {
|
|
57
|
+
window.history.pushState(null, "", href)
|
|
58
|
+
window.dispatchEvent(new PopStateEvent("popstate"))
|
|
59
|
+
}
|
|
60
|
+
notification.close()
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
.catch(() => undefined)
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
render(
|
|
68
|
+
() => (
|
|
69
|
+
<PlatformProvider value={platform}>
|
|
70
|
+
<AppBaseProviders>
|
|
71
|
+
<AppInterface />
|
|
72
|
+
</AppBaseProviders>
|
|
73
|
+
</PlatformProvider>
|
|
74
|
+
),
|
|
75
|
+
root!,
|
|
76
|
+
)
|
package/src/env.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useGlobalSync } from "@/context/global-sync"
|
|
2
|
+
import { base64Decode } from "@jonsoc/util/encode"
|
|
3
|
+
import { useParams } from "@solidjs/router"
|
|
4
|
+
import { createMemo } from "solid-js"
|
|
5
|
+
|
|
6
|
+
export const popularProviders = ["jonsoc", "anthropic", "github-copilot", "openai", "google", "openrouter", "vercel"]
|
|
7
|
+
|
|
8
|
+
export function useProviders() {
|
|
9
|
+
const globalSync = useGlobalSync()
|
|
10
|
+
const params = useParams()
|
|
11
|
+
const currentDirectory = createMemo(() => base64Decode(params.dir ?? ""))
|
|
12
|
+
const providers = createMemo(() => {
|
|
13
|
+
if (currentDirectory()) {
|
|
14
|
+
const [projectStore] = globalSync.child(currentDirectory())
|
|
15
|
+
return projectStore.provider
|
|
16
|
+
}
|
|
17
|
+
return globalSync.data.provider
|
|
18
|
+
})
|
|
19
|
+
const connected = createMemo(() => providers().all.filter((p) => providers().connected.includes(p.id)))
|
|
20
|
+
const paid = createMemo(() =>
|
|
21
|
+
connected().filter((p) => p.id !== "jonsoc" || Object.values(p.models).find((m) => m.cost?.input)),
|
|
22
|
+
)
|
|
23
|
+
const popular = createMemo(() => providers().all.filter((p) => popularProviders.includes(p.id)))
|
|
24
|
+
return {
|
|
25
|
+
all: createMemo(() => providers().all),
|
|
26
|
+
default: createMemo(() => providers().default),
|
|
27
|
+
popular,
|
|
28
|
+
connected,
|
|
29
|
+
paid,
|
|
30
|
+
}
|
|
31
|
+
}
|