@slycode/slycode 0.2.21 → 0.2.22
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/bridge/provider-utils.d.ts +10 -0
- package/dist/bridge/provider-utils.js +5 -1
- package/dist/bridge/provider-utils.js.map +1 -1
- package/dist/bridge/session-manager.js +6 -0
- package/dist/bridge/session-manager.js.map +1 -1
- package/dist/bridge/types.d.ts +4 -0
- package/dist/messaging/bridge-client.d.ts +3 -3
- package/dist/messaging/bridge-client.js +6 -4
- package/dist/messaging/bridge-client.js.map +1 -1
- package/dist/messaging/index.js +111 -20
- package/dist/messaging/index.js.map +1 -1
- package/dist/messaging/state.d.ts +3 -0
- package/dist/messaging/state.js +13 -0
- package/dist/messaging/state.js.map +1 -1
- package/dist/messaging/types.d.ts +3 -0
- package/dist/web/.next/BUILD_ID +1 -1
- package/dist/web/.next/build-manifest.json +2 -2
- package/dist/web/.next/server/app/_global-error.html +2 -2
- package/dist/web/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/.next/server/app/_not-found.html +1 -1
- package/dist/web/.next/server/app/_not-found.rsc +2 -2
- package/dist/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/dist/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/dist/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/dist/web/.next/server/app/api/areas/route.js +1 -1
- package/dist/web/.next/server/app/api/areas/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/bridge/[...path]/route.js +1 -1
- package/dist/web/.next/server/app/api/bridge/[...path]/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/cli-assets/assistant/route.js +1 -1
- package/dist/web/.next/server/app/api/cli-assets/assistant/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/cli-assets/fix/route.js +1 -1
- package/dist/web/.next/server/app/api/cli-assets/fix/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/cli-assets/import/route.js +1 -1
- package/dist/web/.next/server/app/api/cli-assets/import/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/cli-assets/route.js +2 -2
- package/dist/web/.next/server/app/api/cli-assets/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/cli-assets/store/preview/route.js +1 -1
- package/dist/web/.next/server/app/api/cli-assets/store/preview/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/cli-assets/store/route.js +2 -2
- package/dist/web/.next/server/app/api/cli-assets/store/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/cli-assets/sync/route.js +1 -1
- package/dist/web/.next/server/app/api/cli-assets/sync/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/cli-assets/updates/route.js +2 -2
- package/dist/web/.next/server/app/api/cli-assets/updates/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/dashboard/route.js +2 -2
- package/dist/web/.next/server/app/api/dashboard/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/events/route.js +1 -1
- package/dist/web/.next/server/app/api/events/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/file/route.js +1 -1
- package/dist/web/.next/server/app/api/file/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/git-status/route.js +1 -1
- package/dist/web/.next/server/app/api/git-status/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/kanban/route.js +1 -1
- package/dist/web/.next/server/app/api/kanban/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/kanban/stream/route.js +1 -1
- package/dist/web/.next/server/app/api/kanban/stream/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/projects/[id]/route.js +2 -2
- package/dist/web/.next/server/app/api/projects/[id]/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/projects/analyze/route.js +1 -1
- package/dist/web/.next/server/app/api/projects/analyze/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/projects/reorder/route.js +2 -2
- package/dist/web/.next/server/app/api/projects/reorder/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/projects/route.js +2 -2
- package/dist/web/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/providers/route.js +1 -1
- package/dist/web/.next/server/app/api/providers/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/scheduler/route.js +1 -1
- package/dist/web/.next/server/app/api/scheduler/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/search/route.js +1 -1
- package/dist/web/.next/server/app/api/search/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/settings/route.js +1 -1
- package/dist/web/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/sly-actions/invalidate/route.js +1 -1
- package/dist/web/.next/server/app/api/sly-actions/invalidate/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/sly-actions/route.js +1 -1
- package/dist/web/.next/server/app/api/sly-actions/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/sly-actions/stream/route.js +1 -1
- package/dist/web/.next/server/app/api/sly-actions/stream/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/system-stats/route.js +1 -1
- package/dist/web/.next/server/app/api/system-stats/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/terminal-classes/route.js +1 -1
- package/dist/web/.next/server/app/api/terminal-classes/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/transcribe/route.js +5 -5
- package/dist/web/.next/server/app/api/transcribe/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/api/version-check/route.js +1 -1
- package/dist/web/.next/server/app/api/version-check/route.js.nft.json +1 -1
- package/dist/web/.next/server/app/page.js +1 -1
- package/dist/web/.next/server/app/page.js.nft.json +1 -1
- package/dist/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/.next/server/app/project/[id]/page.js +2 -2
- package/dist/web/.next/server/app/project/[id]/page.js.nft.json +1 -1
- package/dist/web/.next/server/app/project/[id]/page_client-reference-manifest.js +1 -1
- package/dist/web/.next/server/chunks/{[externals]__c6831f39._.js → [externals]__78e522ea._.js} +2 -2
- package/dist/web/.next/server/chunks/{[root-of-the-server]__1ec21ccc._.js → [root-of-the-server]__029203cd._.js} +3 -3
- package/dist/web/.next/server/chunks/{[root-of-the-server]__4297cb97._.js → [root-of-the-server]__0d6d4443._.js} +1 -1
- package/dist/web/.next/server/chunks/[root-of-the-server]__172ad0b1._.js +18 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__1c5f4ef9._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__1cab11f0._.js +3 -0
- package/dist/web/.next/server/chunks/{[root-of-the-server]__0f69c28a._.js → [root-of-the-server]__1eb3f172._.js} +2 -2
- package/dist/web/.next/server/chunks/[root-of-the-server]__22cba275._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__2543e413._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__2c42a835._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__2ed0ff47._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__35454eea._.js +27 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__35768b56._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__3880228a._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__42322d88._.js +3 -0
- package/dist/web/.next/server/chunks/{[root-of-the-server]__d0f4efec._.js → [root-of-the-server]__5152eeff._.js} +3 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__527c7f57._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__5c5dac4b._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__5cb130f2._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__68927e75._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__719517c7._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__73cf49c2._.js +3 -0
- package/dist/web/.next/server/chunks/{[root-of-the-server]__f5dae2ad._.js → [root-of-the-server]__7af4ab09._.js} +1 -1
- package/dist/web/.next/server/chunks/{[root-of-the-server]__4244617a._.js → [root-of-the-server]__7e6860e0._.js} +3 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__88bf5e22._.js +3 -0
- package/dist/web/.next/server/chunks/{[root-of-the-server]__f97e93fa._.js → [root-of-the-server]__8b4259cb._.js} +3 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__92f81907._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__967603e9._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__9e4bd28f._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__ba1d2e56._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__c942d872._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__d7893622._.js +3 -0
- package/dist/web/.next/server/chunks/{[root-of-the-server]__3b9d3e43._.js → [root-of-the-server]__d843611b._.js} +6 -6
- package/dist/web/.next/server/chunks/[root-of-the-server]__f4d2627f._.js +3 -0
- package/dist/web/.next/server/chunks/[root-of-the-server]__f597835d._.js +3 -0
- package/dist/web/.next/server/chunks/{[root-of-the-server]__cf14e306._.js → [root-of-the-server]__fe8b9abd._.js} +1 -1
- package/dist/web/.next/server/chunks/src_677020aa._.js +1 -1
- package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__1f5fc489._.js +1 -1
- package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__43d93717._.js +3 -0
- package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__90f82e6d._.js +3 -0
- package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__bcbe4bf2._.js +1 -1
- package/dist/web/.next/server/chunks/ssr/src_lib_registry_ts_2fc87c9c._.js +1 -1
- package/dist/web/.next/server/pages/404.html +1 -1
- package/dist/web/.next/server/pages/500.html +2 -2
- package/dist/web/.next/static/chunks/18cfbdd7e977bb01.css +1 -0
- package/dist/web/.next/static/chunks/8415039c5941cf5c.js +4 -0
- package/dist/web/.next/static/chunks/{3d5195b57fc05540.js → a0f5f9cdee8a22c1.js} +2 -2
- package/dist/web/src/app/api/projects/analyze/route.ts +12 -2
- package/dist/web/src/app/api/projects/route.ts +1 -11
- package/dist/web/src/app/api/providers/route.ts +4 -0
- package/dist/web/src/components/ClaudeTerminalPanel.tsx +124 -70
- package/dist/web/src/lib/paths.ts +14 -0
- package/dist/web/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/templates/kanban-seed.json +1 -1
- package/dist/web/.next/server/chunks/[root-of-the-server]__09aec55a._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__12f6cd6f._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__15fc9266._.js +0 -18
- package/dist/web/.next/server/chunks/[root-of-the-server]__198f01e0._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__279e9bf3._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__2b639eab._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__2d1f0ed9._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__3f239285._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__47dd878e._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__5b8c9374._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__5e08b942._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__6ffce934._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__71bb3374._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__7603305e._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__7c476ad6._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__846ca56f._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__98d88050._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__b273cc05._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__b90bbd70._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__d5272169._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__d56e68cb._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__d6362272._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__de1277ee._.js +0 -27
- package/dist/web/.next/server/chunks/[root-of-the-server]__e88a19d2._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__f3e501b6._.js +0 -3
- package/dist/web/.next/server/chunks/[root-of-the-server]__f59af2bc._.js +0 -3
- package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__9ac6ea25._.js +0 -3
- package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__dfe2728c._.js +0 -3
- package/dist/web/.next/static/chunks/59fb302a5bfd2dc0.js +0 -4
- package/dist/web/.next/static/chunks/747f5e5f9dcf2621.css +0 -1
- /package/dist/web/.next/static/{0sPAbk-Qw-InZ0rdHjHnC → b2V8jC3HBMi4vgm7Kie3H}/_buildManifest.js +0 -0
- /package/dist/web/.next/static/{0sPAbk-Qw-InZ0rdHjHnC → b2V8jC3HBMi4vgm7Kie3H}/_clientMiddlewareManifest.json +0 -0
- /package/dist/web/.next/static/{0sPAbk-Qw-InZ0rdHjHnC → b2V8jC3HBMi4vgm7Kie3H}/_ssgManifest.js +0 -0
|
@@ -2,7 +2,7 @@ import { NextResponse } from 'next/server';
|
|
|
2
2
|
import { execFile } from 'child_process';
|
|
3
3
|
import { promisify } from 'util';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import { getPackageDir } from '@/lib/paths';
|
|
5
|
+
import { getPackageDir, expandTilde } from '@/lib/paths';
|
|
6
6
|
|
|
7
7
|
const execFileAsync = promisify(execFile);
|
|
8
8
|
|
|
@@ -23,11 +23,21 @@ export async function POST(request: Request) {
|
|
|
23
23
|
);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
// Expand tilde and validate absolute path
|
|
27
|
+
const expanded = expandTilde(targetPath);
|
|
28
|
+
if (!path.isAbsolute(expanded)) {
|
|
29
|
+
return NextResponse.json(
|
|
30
|
+
{ error: 'Please enter an absolute path (e.g. ~/Dev/myproject or /home/user/Dev/myproject)' },
|
|
31
|
+
{ status: 400 }
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const resolvedPath = path.resolve(expanded);
|
|
35
|
+
|
|
26
36
|
const scaffoldScript = path.join(getPackageDir(), 'scripts', 'scaffold.js');
|
|
27
37
|
const args = [
|
|
28
38
|
scaffoldScript,
|
|
29
39
|
'analyze',
|
|
30
|
-
'--path',
|
|
40
|
+
'--path', resolvedPath,
|
|
31
41
|
'--json',
|
|
32
42
|
];
|
|
33
43
|
if (providers && Array.isArray(providers) && providers.length > 0) {
|
|
@@ -3,22 +3,12 @@ import { loadRegistry, saveRegistry } from '@/lib/registry';
|
|
|
3
3
|
import { execFile } from 'child_process';
|
|
4
4
|
import { promisify } from 'util';
|
|
5
5
|
import path from 'path';
|
|
6
|
-
import
|
|
7
|
-
import { getSlycodeRoot, getPackageDir } from '@/lib/paths';
|
|
6
|
+
import { getSlycodeRoot, getPackageDir, expandTilde } from '@/lib/paths';
|
|
8
7
|
|
|
9
8
|
const execFileAsync = promisify(execFile);
|
|
10
9
|
|
|
11
10
|
export const dynamic = 'force-dynamic';
|
|
12
11
|
|
|
13
|
-
function expandTilde(p: string): string {
|
|
14
|
-
// Normalize Unicode tildes (U+02DC small tilde, U+FF5E fullwidth tilde) to ASCII
|
|
15
|
-
p = p.replace(/^[\u02dc\uff5e]/, '~');
|
|
16
|
-
if (p.startsWith('~/') || p === '~') {
|
|
17
|
-
return p.replace(/^~/, os.homedir());
|
|
18
|
-
}
|
|
19
|
-
return p;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
12
|
function toKebabCase(str: string): string {
|
|
23
13
|
return str
|
|
24
14
|
.toLowerCase()
|
|
@@ -31,6 +31,10 @@ function validateProviderDefault(
|
|
|
31
31
|
if (typeof d.skipPermissions !== 'boolean') {
|
|
32
32
|
return `${label}.skipPermissions must be a boolean`;
|
|
33
33
|
}
|
|
34
|
+
// Optional model field — pass through to CLI (no validation against available list)
|
|
35
|
+
if ('model' in d && d.model !== undefined && typeof d.model !== 'string') {
|
|
36
|
+
return `${label}.model must be a string if provided`;
|
|
37
|
+
}
|
|
34
38
|
return null;
|
|
35
39
|
}
|
|
36
40
|
|
|
@@ -13,11 +13,16 @@ interface ProviderConfig {
|
|
|
13
13
|
command: string;
|
|
14
14
|
permissions: { flag: string; label: string; default: boolean };
|
|
15
15
|
resume: { supported: boolean };
|
|
16
|
+
model?: {
|
|
17
|
+
flag: string;
|
|
18
|
+
available: Array<{ id: string; label: string; description?: string }>;
|
|
19
|
+
};
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
interface ProviderDefault {
|
|
19
23
|
provider: string;
|
|
20
24
|
skipPermissions: boolean;
|
|
25
|
+
model?: string;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
interface ProvidersData {
|
|
@@ -45,6 +50,7 @@ interface SessionInfo {
|
|
|
45
50
|
claudeSessionId?: string | null;
|
|
46
51
|
provider?: string;
|
|
47
52
|
skipPermissions?: boolean;
|
|
53
|
+
model?: string;
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
export interface TerminalContext {
|
|
@@ -145,6 +151,13 @@ export function ClaudeTerminalPanel({
|
|
|
145
151
|
// Spawn error toast — shown when session creation fails (e.g. posix_spawnp failed)
|
|
146
152
|
const [spawnError, setSpawnError] = useState<string | null>(null);
|
|
147
153
|
|
|
154
|
+
// Model selection state
|
|
155
|
+
const [selectedModel, setSelectedModel] = useState(''); // '' = Default (no flag)
|
|
156
|
+
const prevProviderRef = useRef(selectedProvider);
|
|
157
|
+
const userSelectedModelRef = useRef(false);
|
|
158
|
+
const sessionInfoRef = useRef(sessionInfo);
|
|
159
|
+
sessionInfoRef.current = sessionInfo;
|
|
160
|
+
|
|
148
161
|
// Instruction file check state
|
|
149
162
|
const [instructionFileCheck, setInstructionFileCheck] = useState<{ needed: boolean; targetFile?: string; copySource?: string } | null>(null);
|
|
150
163
|
const [createInstructionFile, setCreateInstructionFile] = useState(true);
|
|
@@ -177,15 +190,53 @@ export function ClaudeTerminalPanel({
|
|
|
177
190
|
setSelectedProvider(def.provider);
|
|
178
191
|
}
|
|
179
192
|
setSkipPermissions(def.skipPermissions);
|
|
193
|
+
if (def.model) {
|
|
194
|
+
setSelectedModel(def.model);
|
|
195
|
+
}
|
|
180
196
|
}
|
|
181
197
|
}
|
|
182
198
|
})
|
|
183
199
|
.catch(() => { /* providers.json not available, use defaults */ });
|
|
184
200
|
}, [stage]);
|
|
185
201
|
|
|
202
|
+
// Pre-fill model when provider changes
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
if (!providersData) return;
|
|
205
|
+
const providerChanged = prevProviderRef.current !== selectedProvider;
|
|
206
|
+
prevProviderRef.current = selectedProvider;
|
|
207
|
+
if (!providerChanged && userSelectedModelRef.current) return;
|
|
208
|
+
userSelectedModelRef.current = false;
|
|
209
|
+
// Priority: last-used from session > stage default > Default
|
|
210
|
+
const si = sessionInfoRef.current;
|
|
211
|
+
if (si?.model && si.provider === selectedProvider) {
|
|
212
|
+
const available = providersData.providers[selectedProvider]?.model?.available;
|
|
213
|
+
if (available?.some(m => m.id === si.model)) {
|
|
214
|
+
setSelectedModel(si.model);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const stageDefault = stage ? providersData.defaults.stages[stage] : null;
|
|
219
|
+
const def = stageDefault || providersData.defaults.global;
|
|
220
|
+
if (def?.model && def.provider === selectedProvider) {
|
|
221
|
+
const available = providersData.providers[selectedProvider]?.model?.available;
|
|
222
|
+
if (available?.some(m => m.id === def.model)) {
|
|
223
|
+
setSelectedModel(def.model);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
setSelectedModel('');
|
|
228
|
+
}, [selectedProvider, providersData, stage]);
|
|
229
|
+
|
|
186
230
|
// Persist provider default to /api/providers (fire-and-forget)
|
|
187
231
|
const saveProviderDefault = useCallback((provider: string, skip: boolean) => {
|
|
188
|
-
|
|
232
|
+
// Preserve any existing model field in the stage default (stage model defaults are intentional config)
|
|
233
|
+
const existingDefault = stage
|
|
234
|
+
? providersData?.defaults.stages[stage]
|
|
235
|
+
: providersData?.defaults.global;
|
|
236
|
+
const defaultVal: Record<string, unknown> = { provider, skipPermissions: skip };
|
|
237
|
+
if (existingDefault?.model && existingDefault.provider === provider) {
|
|
238
|
+
defaultVal.model = existingDefault.model;
|
|
239
|
+
}
|
|
189
240
|
const defaults = stage
|
|
190
241
|
? { stages: { [stage]: defaultVal } }
|
|
191
242
|
: { global: defaultVal };
|
|
@@ -194,7 +245,7 @@ export function ClaudeTerminalPanel({
|
|
|
194
245
|
headers: { 'Content-Type': 'application/json' },
|
|
195
246
|
body: JSON.stringify({ defaults }),
|
|
196
247
|
}).catch(() => { /* preference save — ignore errors */ });
|
|
197
|
-
}, [stage]);
|
|
248
|
+
}, [stage, providersData]);
|
|
198
249
|
|
|
199
250
|
const isRunning = sessionInfo?.status === 'running' || sessionInfo?.status === 'detached';
|
|
200
251
|
const hasHistory = sessionInfo?.hasHistory;
|
|
@@ -288,6 +339,74 @@ export function ClaudeTerminalPanel({
|
|
|
288
339
|
</div>
|
|
289
340
|
) : null;
|
|
290
341
|
|
|
342
|
+
// Helper to render model label from provider config
|
|
343
|
+
const getModelLabel = (providerId: string, modelId?: string) => {
|
|
344
|
+
if (!modelId || !providersData) return null;
|
|
345
|
+
return providersData.providers[providerId]?.model?.available?.find(m => m.id === modelId)?.label || modelId;
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// Shared provider selector (eliminates duplication between resume and fresh-start screens)
|
|
349
|
+
const renderProviderSelector = (options?: { showModel?: boolean }) => {
|
|
350
|
+
if (!providersData || Object.keys(providersData.providers).length <= 1) return null;
|
|
351
|
+
const currentProvider = providersData.providers[selectedProvider];
|
|
352
|
+
const models = currentProvider?.model?.available;
|
|
353
|
+
const showModel = options?.showModel !== false;
|
|
354
|
+
return (
|
|
355
|
+
<div className="flex flex-col items-center gap-2 mt-3 pt-3 border-t border-void-700/50">
|
|
356
|
+
<div className="flex gap-1">
|
|
357
|
+
{Object.values(providersData.providers).map(p => (
|
|
358
|
+
<button
|
|
359
|
+
key={p.id}
|
|
360
|
+
onClick={() => {
|
|
361
|
+
setSelectedProvider(p.id);
|
|
362
|
+
saveProviderDefault(p.id, skipPermissions);
|
|
363
|
+
onProviderChange?.(p.id);
|
|
364
|
+
}}
|
|
365
|
+
className={`rounded-md px-3 py-1 text-xs font-medium transition-all ${
|
|
366
|
+
selectedProvider === p.id
|
|
367
|
+
? 'border border-neon-blue-400/60 bg-neon-blue-400/15 text-neon-blue-400 shadow-[0_0_8px_rgba(0,191,255,0.2)]'
|
|
368
|
+
: 'border border-void-600 bg-void-800 text-void-400 hover:border-void-500 hover:text-void-300'
|
|
369
|
+
}`}
|
|
370
|
+
>
|
|
371
|
+
{p.displayName}
|
|
372
|
+
</button>
|
|
373
|
+
))}
|
|
374
|
+
</div>
|
|
375
|
+
<div className="flex items-center gap-3">
|
|
376
|
+
{showModel && models && models.length > 0 && (
|
|
377
|
+
<div className="flex items-center gap-1.5">
|
|
378
|
+
<span className="text-xs text-void-500">Model</span>
|
|
379
|
+
<select
|
|
380
|
+
value={selectedModel}
|
|
381
|
+
onChange={(e) => { setSelectedModel(e.target.value); userSelectedModelRef.current = true; }}
|
|
382
|
+
className={`max-w-[140px] truncate rounded border px-2 py-1 text-xs font-medium transition-all ${
|
|
383
|
+
selectedModel
|
|
384
|
+
? 'border-neon-blue-400/40 bg-neon-blue-400/10 text-neon-blue-400'
|
|
385
|
+
: 'border-void-600 bg-void-800 text-void-400'
|
|
386
|
+
}`}
|
|
387
|
+
>
|
|
388
|
+
<option value="">Default</option>
|
|
389
|
+
{models.map(m => (
|
|
390
|
+
<option key={m.id} value={m.id}>{m.label}</option>
|
|
391
|
+
))}
|
|
392
|
+
</select>
|
|
393
|
+
</div>
|
|
394
|
+
)}
|
|
395
|
+
<label className="flex items-center gap-1.5 text-xs text-void-500 cursor-pointer">
|
|
396
|
+
<input
|
|
397
|
+
type="checkbox"
|
|
398
|
+
checked={skipPermissions}
|
|
399
|
+
onChange={(e) => { setSkipPermissions(e.target.checked); saveProviderDefault(selectedProvider, e.target.checked); }}
|
|
400
|
+
className="rounded border-void-600"
|
|
401
|
+
/>
|
|
402
|
+
{providersData.providers[selectedProvider]?.permissions.label || 'Skip permissions'}
|
|
403
|
+
</label>
|
|
404
|
+
</div>
|
|
405
|
+
{instructionFileWarning}
|
|
406
|
+
</div>
|
|
407
|
+
);
|
|
408
|
+
};
|
|
409
|
+
|
|
291
410
|
// Derive startup and toolbar lists from placement
|
|
292
411
|
const startupActions = actions.filter(a => a.placement === 'startup' || a.placement === 'both');
|
|
293
412
|
const toolbarActions = actions.filter(a => a.placement === 'toolbar' || a.placement === 'both');
|
|
@@ -317,6 +436,7 @@ export function ClaudeTerminalPanel({
|
|
|
317
436
|
cwd,
|
|
318
437
|
fresh: !hasHistory,
|
|
319
438
|
prompt,
|
|
439
|
+
model: selectedModel || undefined,
|
|
320
440
|
createInstructionFile: instructionFileCheck?.needed ? createInstructionFile : undefined,
|
|
321
441
|
}),
|
|
322
442
|
});
|
|
@@ -619,40 +739,7 @@ export function ClaudeTerminalPanel({
|
|
|
619
739
|
Custom...
|
|
620
740
|
</button>
|
|
621
741
|
</div>
|
|
622
|
-
{
|
|
623
|
-
{providersData && Object.keys(providersData.providers).length > 1 && (
|
|
624
|
-
<div className="flex flex-col items-center gap-2 mt-3 pt-3 border-t border-void-700/50">
|
|
625
|
-
<div className="flex gap-1">
|
|
626
|
-
{Object.values(providersData.providers).map(p => (
|
|
627
|
-
<button
|
|
628
|
-
key={p.id}
|
|
629
|
-
onClick={() => {
|
|
630
|
-
setSelectedProvider(p.id);
|
|
631
|
-
saveProviderDefault(p.id, skipPermissions);
|
|
632
|
-
onProviderChange?.(p.id);
|
|
633
|
-
}}
|
|
634
|
-
className={`rounded-md px-3 py-1 text-xs font-medium transition-all ${
|
|
635
|
-
selectedProvider === p.id
|
|
636
|
-
? 'border border-neon-blue-400/60 bg-neon-blue-400/15 text-neon-blue-400 shadow-[0_0_8px_rgba(0,191,255,0.2)]'
|
|
637
|
-
: 'border border-void-600 bg-void-800 text-void-400 hover:border-void-500 hover:text-void-300'
|
|
638
|
-
}`}
|
|
639
|
-
>
|
|
640
|
-
{p.displayName}
|
|
641
|
-
</button>
|
|
642
|
-
))}
|
|
643
|
-
</div>
|
|
644
|
-
<label className="flex items-center gap-1.5 text-xs text-void-500 cursor-pointer">
|
|
645
|
-
<input
|
|
646
|
-
type="checkbox"
|
|
647
|
-
checked={skipPermissions}
|
|
648
|
-
onChange={(e) => { setSkipPermissions(e.target.checked); saveProviderDefault(selectedProvider, e.target.checked); }}
|
|
649
|
-
className="rounded border-void-600"
|
|
650
|
-
/>
|
|
651
|
-
{providersData.providers[selectedProvider]?.permissions.label || 'Skip permissions'}
|
|
652
|
-
</label>
|
|
653
|
-
{instructionFileWarning}
|
|
654
|
-
</div>
|
|
655
|
-
)}
|
|
742
|
+
{renderProviderSelector({ showModel: false })}
|
|
656
743
|
</>
|
|
657
744
|
) : (
|
|
658
745
|
<>
|
|
@@ -684,40 +771,7 @@ export function ClaudeTerminalPanel({
|
|
|
684
771
|
>
|
|
685
772
|
Start without prompt
|
|
686
773
|
</button>
|
|
687
|
-
{
|
|
688
|
-
{providersData && Object.keys(providersData.providers).length > 1 && (
|
|
689
|
-
<div className="flex flex-col items-center gap-2 mt-3 pt-3 border-t border-void-700/50">
|
|
690
|
-
<div className="flex gap-1">
|
|
691
|
-
{Object.values(providersData.providers).map(p => (
|
|
692
|
-
<button
|
|
693
|
-
key={p.id}
|
|
694
|
-
onClick={() => {
|
|
695
|
-
setSelectedProvider(p.id);
|
|
696
|
-
saveProviderDefault(p.id, skipPermissions);
|
|
697
|
-
onProviderChange?.(p.id);
|
|
698
|
-
}}
|
|
699
|
-
className={`rounded-md px-3 py-1 text-xs font-medium transition-all ${
|
|
700
|
-
selectedProvider === p.id
|
|
701
|
-
? 'border border-neon-blue-400/60 bg-neon-blue-400/15 text-neon-blue-400 shadow-[0_0_8px_rgba(0,191,255,0.2)]'
|
|
702
|
-
: 'border border-void-600 bg-void-800 text-void-400 hover:border-void-500 hover:text-void-300'
|
|
703
|
-
}`}
|
|
704
|
-
>
|
|
705
|
-
{p.displayName}
|
|
706
|
-
</button>
|
|
707
|
-
))}
|
|
708
|
-
</div>
|
|
709
|
-
<label className="flex items-center gap-1.5 text-xs text-void-500 cursor-pointer">
|
|
710
|
-
<input
|
|
711
|
-
type="checkbox"
|
|
712
|
-
checked={skipPermissions}
|
|
713
|
-
onChange={(e) => { setSkipPermissions(e.target.checked); saveProviderDefault(selectedProvider, e.target.checked); }}
|
|
714
|
-
className="rounded border-void-600"
|
|
715
|
-
/>
|
|
716
|
-
{providersData.providers[selectedProvider]?.permissions.label || 'Skip permissions'}
|
|
717
|
-
</label>
|
|
718
|
-
{instructionFileWarning}
|
|
719
|
-
</div>
|
|
720
|
-
)}
|
|
774
|
+
{renderProviderSelector()}
|
|
721
775
|
</>
|
|
722
776
|
)}
|
|
723
777
|
</div>
|
|
@@ -7,6 +7,20 @@
|
|
|
7
7
|
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import fs from 'fs';
|
|
10
|
+
import os from 'os';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Expand tilde (~) in a path to the user's home directory.
|
|
14
|
+
* Also normalizes Unicode tildes (U+02DC, U+FF5E) that Mac browsers can produce.
|
|
15
|
+
*/
|
|
16
|
+
export function expandTilde(p: string): string {
|
|
17
|
+
// Normalize Unicode tildes (U+02DC small tilde, U+FF5E fullwidth tilde) to ASCII
|
|
18
|
+
p = p.replace(/^[\u02dc\uff5e]/, '~');
|
|
19
|
+
if (p.startsWith('~/') || p === '~') {
|
|
20
|
+
return p.replace(/^~/, os.homedir());
|
|
21
|
+
}
|
|
22
|
+
return p;
|
|
23
|
+
}
|
|
10
24
|
|
|
11
25
|
/**
|
|
12
26
|
* Resolve the SlyCode root directory (workspace).
|