@kmugalkhod/lightcode 0.13.1 → 0.15.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/README.md +5 -3
- package/assets/web/index-qeqpc3cb.css +1 -0
- package/assets/web/index-yavzmb1c.js +149 -0
- package/assets/web/index.html +2 -2
- package/cli.js +17 -4
- package/package.json +1 -1
- package/server.js +218 -68
- package/assets/web/index-21aq5q6q.css +0 -1
- package/assets/web/index-j5mzdh93.js +0 -325
package/assets/web/index.html
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
<meta name="theme-color" content="#0D1117" />
|
|
8
8
|
<title>Lightcode</title>
|
|
9
9
|
|
|
10
|
-
<link rel="stylesheet" crossorigin href="./index-
|
|
10
|
+
<link rel="stylesheet" crossorigin href="./index-qeqpc3cb.css"><script type="module" crossorigin src="./index-yavzmb1c.js"></script></head>
|
|
11
11
|
<body>
|
|
12
12
|
<!--
|
|
13
13
|
THESIS: One local coding workspace shared with the CLI; this surface rejects dashboard-card clutter and keeps the project conversation central.
|
|
14
14
|
OWN-WORLD: Charcoal #0D1117/#121820/#18212B, hairline #273241, amber #F2A65A, workhorse system sans, and ui-monospace only for code and data.
|
|
15
|
-
STORY: Choose a
|
|
15
|
+
STORY: Choose a project from a familiar local folder, create or resume a session, stream the work, and resolve approvals without leaving the conversation.
|
|
16
16
|
FIRST VIEWPORT: A 264px session rail, an amber workspace ribbon, and one centered conversation/composer; below 760px the rail becomes a drawer.
|
|
17
17
|
FORM: Workspace-ribbon wayfinding, grounded candidate five and the assigned Operate structure; seed key bf554f40.
|
|
18
18
|
FINISH: unreviewed and undocumented is unfinished; this build ends with the finish review, the verdict, and DESIGN.md
|
package/cli.js
CHANGED
|
@@ -124,7 +124,7 @@ var init_logger = __esm(() => {
|
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
// packages/shared/src/version.ts
|
|
127
|
-
var lightcodeVersion = "0.
|
|
127
|
+
var lightcodeVersion = "0.15.0";
|
|
128
128
|
|
|
129
129
|
// packages/shared/src/index.ts
|
|
130
130
|
var productName = "Lightcode";
|
|
@@ -10093,7 +10093,13 @@ var workspaceLocationIdSchema, workspaceLocationStateSchema, workspaceLocationSc
|
|
|
10093
10093
|
var init_workspace_browser = __esm(() => {
|
|
10094
10094
|
init_coding_agent_modes();
|
|
10095
10095
|
init_permissions();
|
|
10096
|
-
workspaceLocationIdSchema = z37.
|
|
10096
|
+
workspaceLocationIdSchema = z37.enum([
|
|
10097
|
+
"desktop",
|
|
10098
|
+
"home",
|
|
10099
|
+
"documents",
|
|
10100
|
+
"downloads",
|
|
10101
|
+
"projects"
|
|
10102
|
+
]);
|
|
10097
10103
|
workspaceLocationStateSchema = z37.enum([
|
|
10098
10104
|
"unprobed",
|
|
10099
10105
|
"available"
|
|
@@ -11352,6 +11358,9 @@ function scanRunCursorText(state, text) {
|
|
|
11352
11358
|
cursor
|
|
11353
11359
|
};
|
|
11354
11360
|
}
|
|
11361
|
+
function shouldMonitorChatResponseBody(response) {
|
|
11362
|
+
return Boolean(response.body && response.status !== 204 && response.status !== 205 && response.status !== 304);
|
|
11363
|
+
}
|
|
11355
11364
|
function buildSessionRunResumeUrl({
|
|
11356
11365
|
chatApi,
|
|
11357
11366
|
runId,
|
|
@@ -11756,7 +11765,11 @@ function useCodingSessionChat({
|
|
|
11756
11765
|
} catch {}
|
|
11757
11766
|
}
|
|
11758
11767
|
lastStreamActivityRef.current = Date.now();
|
|
11759
|
-
if (!response
|
|
11768
|
+
if (!shouldMonitorChatResponseBody(response)) {
|
|
11769
|
+
return response;
|
|
11770
|
+
}
|
|
11771
|
+
const responseBody = response.body;
|
|
11772
|
+
if (!responseBody) {
|
|
11760
11773
|
return response;
|
|
11761
11774
|
}
|
|
11762
11775
|
const cursorDecoder = new TextDecoder;
|
|
@@ -11764,7 +11777,7 @@ function useCodingSessionChat({
|
|
|
11764
11777
|
carry: "",
|
|
11765
11778
|
cursor: runCursorRef.current
|
|
11766
11779
|
};
|
|
11767
|
-
const monitoredBody =
|
|
11780
|
+
const monitoredBody = responseBody.pipeThrough(new TransformStream({
|
|
11768
11781
|
transform(chunk, controller) {
|
|
11769
11782
|
lastStreamActivityRef.current = Date.now();
|
|
11770
11783
|
cursorState = scanRunCursorText(cursorState, cursorDecoder.decode(chunk, { stream: true }));
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -9526,7 +9526,13 @@ var workspaceLocationIdSchema, workspaceLocationStateSchema, workspaceLocationSc
|
|
|
9526
9526
|
var init_workspace_browser = __esm(() => {
|
|
9527
9527
|
init_coding_agent_modes();
|
|
9528
9528
|
init_permissions();
|
|
9529
|
-
workspaceLocationIdSchema = z38.
|
|
9529
|
+
workspaceLocationIdSchema = z38.enum([
|
|
9530
|
+
"desktop",
|
|
9531
|
+
"home",
|
|
9532
|
+
"documents",
|
|
9533
|
+
"downloads",
|
|
9534
|
+
"projects"
|
|
9535
|
+
]);
|
|
9530
9536
|
workspaceLocationStateSchema = z38.enum([
|
|
9531
9537
|
"unprobed",
|
|
9532
9538
|
"available"
|
|
@@ -16808,7 +16814,7 @@ import { Hono as Hono7 } from "hono";
|
|
|
16808
16814
|
function pathImplementation(platform4) {
|
|
16809
16815
|
return platform4 === "win32" ? path24.win32 : path24.posix;
|
|
16810
16816
|
}
|
|
16811
|
-
function
|
|
16817
|
+
function normalizeKnownFolderCandidate(candidate, platform4) {
|
|
16812
16818
|
if (candidate.includes("\x00")) {
|
|
16813
16819
|
return null;
|
|
16814
16820
|
}
|
|
@@ -16852,22 +16858,23 @@ async function readSmallUserDirsConfiguration(filePath) {
|
|
|
16852
16858
|
});
|
|
16853
16859
|
}
|
|
16854
16860
|
}
|
|
16855
|
-
async function
|
|
16861
|
+
async function resolveWindowsKnownFolder(environment, folder) {
|
|
16856
16862
|
try {
|
|
16857
16863
|
const systemRootCandidate = environment.SystemRoot?.trim() || environment.WINDIR?.trim() || "C:\\Windows";
|
|
16858
|
-
const windowsRoot =
|
|
16864
|
+
const windowsRoot = normalizeKnownFolderCandidate(systemRootCandidate, "win32");
|
|
16859
16865
|
if (!windowsRoot) {
|
|
16860
16866
|
return null;
|
|
16861
16867
|
}
|
|
16862
16868
|
const systemDirectory = path24.win32.join(windowsRoot, "System32");
|
|
16863
16869
|
const executable = path24.win32.join(systemDirectory, "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
16870
|
+
const folderExpression = folder === "desktop" ? "[Environment]::GetFolderPath([Environment+SpecialFolder]::DesktopDirectory)" : folder === "documents" ? "[Environment]::GetFolderPath([Environment+SpecialFolder]::MyDocuments)" : "[Environment]::ExpandEnvironmentVariables([string](Get-ItemProperty -LiteralPath 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders' -Name '{374DE290-123F-4565-9164-39C4925E467B}' -ErrorAction Stop).'{374DE290-123F-4565-9164-39C4925E467B}')";
|
|
16864
16871
|
const child = Bun.spawn([
|
|
16865
16872
|
executable,
|
|
16866
16873
|
"-NoLogo",
|
|
16867
16874
|
"-NoProfile",
|
|
16868
16875
|
"-NonInteractive",
|
|
16869
16876
|
"-Command",
|
|
16870
|
-
|
|
16877
|
+
`[Console]::OutputEncoding=[Text.UTF8Encoding]::new($false);[Console]::Write(${folderExpression})`
|
|
16871
16878
|
], {
|
|
16872
16879
|
cwd: systemDirectory,
|
|
16873
16880
|
env: {
|
|
@@ -16904,13 +16911,13 @@ async function resolveWindowsDesktopKnownFolder(environment) {
|
|
|
16904
16911
|
return null;
|
|
16905
16912
|
}
|
|
16906
16913
|
}
|
|
16907
|
-
function
|
|
16914
|
+
function parseXdgUserDirectory(contents, homeDirectory, variableName) {
|
|
16908
16915
|
for (const line of contents.split(/\r?\n/)) {
|
|
16909
|
-
const match = line.match(/^\s*
|
|
16910
|
-
if (!match) {
|
|
16916
|
+
const match = line.match(/^\s*(XDG_(?:DESKTOP|DOCUMENTS|DOWNLOAD)_DIR)="([^"\r\n]*)"\s*$/);
|
|
16917
|
+
if (!match || match[1] !== variableName) {
|
|
16911
16918
|
continue;
|
|
16912
16919
|
}
|
|
16913
|
-
const configured = match[
|
|
16920
|
+
const configured = match[2];
|
|
16914
16921
|
if (!configured || configured.includes("\x00") || /[`]/.test(configured)) {
|
|
16915
16922
|
return null;
|
|
16916
16923
|
}
|
|
@@ -16930,6 +16937,28 @@ function parseXdgDesktopDirectory(contents, homeDirectory) {
|
|
|
16930
16937
|
}
|
|
16931
16938
|
return null;
|
|
16932
16939
|
}
|
|
16940
|
+
async function firstUsableKnownFolder(candidates, platform4, probe) {
|
|
16941
|
+
const seen = new Set;
|
|
16942
|
+
for (const unvalidated of candidates) {
|
|
16943
|
+
if (!unvalidated) {
|
|
16944
|
+
continue;
|
|
16945
|
+
}
|
|
16946
|
+
const candidate = normalizeKnownFolderCandidate(unvalidated, platform4);
|
|
16947
|
+
if (!candidate) {
|
|
16948
|
+
continue;
|
|
16949
|
+
}
|
|
16950
|
+
const key = platform4 === "win32" ? candidate.toLowerCase() : candidate;
|
|
16951
|
+
if (seen.has(key)) {
|
|
16952
|
+
continue;
|
|
16953
|
+
}
|
|
16954
|
+
seen.add(key);
|
|
16955
|
+
const state = await probe(candidate);
|
|
16956
|
+
if (state === "available" || state === "permission-denied") {
|
|
16957
|
+
return candidate;
|
|
16958
|
+
}
|
|
16959
|
+
}
|
|
16960
|
+
return null;
|
|
16961
|
+
}
|
|
16933
16962
|
async function resolveDesktopDirectoryPath(options = {}) {
|
|
16934
16963
|
const platform4 = options.platform ?? process.platform;
|
|
16935
16964
|
const pathApi = pathImplementation(platform4);
|
|
@@ -16937,35 +16966,16 @@ async function resolveDesktopDirectoryPath(options = {}) {
|
|
|
16937
16966
|
const homeDirectory = pathApi.normalize(options.homeDirectory ?? homedir4());
|
|
16938
16967
|
const explicit = environment.LIGHTCODE_DESKTOP_PATH?.trim();
|
|
16939
16968
|
if (explicit) {
|
|
16940
|
-
const normalized =
|
|
16969
|
+
const normalized = normalizeKnownFolderCandidate(explicit, platform4);
|
|
16941
16970
|
if (!normalized) {
|
|
16942
16971
|
throw new WorkspaceRouteError("workspace_unavailable", "LIGHTCODE_DESKTOP_PATH must be an absolute folder path and cannot be a filesystem root.", 500, false);
|
|
16943
16972
|
}
|
|
16944
16973
|
return normalized;
|
|
16945
16974
|
}
|
|
16946
16975
|
const probe = options.probeDirectory ?? probeDirectory;
|
|
16947
|
-
const firstAvailable = async (candidates) => {
|
|
16948
|
-
const seen = new Set;
|
|
16949
|
-
for (const unvalidated of candidates) {
|
|
16950
|
-
const candidate = normalizeDesktopCandidate(unvalidated, platform4);
|
|
16951
|
-
if (!candidate) {
|
|
16952
|
-
continue;
|
|
16953
|
-
}
|
|
16954
|
-
const key = platform4 === "win32" ? candidate.toLowerCase() : candidate;
|
|
16955
|
-
if (seen.has(key)) {
|
|
16956
|
-
continue;
|
|
16957
|
-
}
|
|
16958
|
-
seen.add(key);
|
|
16959
|
-
const state = await probe(candidate);
|
|
16960
|
-
if (state === "available" || state === "permission-denied") {
|
|
16961
|
-
return candidate;
|
|
16962
|
-
}
|
|
16963
|
-
}
|
|
16964
|
-
return null;
|
|
16965
|
-
};
|
|
16966
16976
|
if (platform4 === "win32") {
|
|
16967
16977
|
const userProfile = environment.USERPROFILE?.trim() || homeDirectory;
|
|
16968
|
-
const knownFolder = await (options.resolveWindowsKnownFolder ?? (() =>
|
|
16978
|
+
const knownFolder = await (options.resolveWindowsKnownFolder ?? (() => resolveWindowsKnownFolder(environment, "desktop")))();
|
|
16969
16979
|
const candidates = [
|
|
16970
16980
|
knownFolder,
|
|
16971
16981
|
environment.OneDrive,
|
|
@@ -16981,16 +16991,16 @@ async function resolveDesktopDirectoryPath(options = {}) {
|
|
|
16981
16991
|
];
|
|
16982
16992
|
});
|
|
16983
16993
|
candidates.push(path24.win32.join(userProfile, "Desktop"));
|
|
16984
|
-
return await
|
|
16994
|
+
return await firstUsableKnownFolder(candidates, platform4, probe) ?? path24.win32.join(homeDirectory, "Desktop");
|
|
16985
16995
|
}
|
|
16986
16996
|
if (platform4 === "linux") {
|
|
16987
16997
|
const configHome = environment.XDG_CONFIG_HOME?.trim();
|
|
16988
16998
|
const safeConfigHome = configHome && path24.posix.isAbsolute(configHome) ? path24.posix.normalize(configHome) : path24.posix.join(homeDirectory, ".config");
|
|
16989
16999
|
const readConfiguration = options.readUserDirsConfiguration ?? readSmallUserDirsConfiguration;
|
|
16990
17000
|
const contents = await readConfiguration(path24.posix.join(safeConfigHome, "user-dirs.dirs"));
|
|
16991
|
-
const configured = contents ?
|
|
17001
|
+
const configured = contents ? parseXdgUserDirectory(contents, homeDirectory, "XDG_DESKTOP_DIR") : null;
|
|
16992
17002
|
if (configured) {
|
|
16993
|
-
const selected = await
|
|
17003
|
+
const selected = await firstUsableKnownFolder([configured], platform4, probe);
|
|
16994
17004
|
if (selected) {
|
|
16995
17005
|
return selected;
|
|
16996
17006
|
}
|
|
@@ -16998,6 +17008,89 @@ async function resolveDesktopDirectoryPath(options = {}) {
|
|
|
16998
17008
|
}
|
|
16999
17009
|
return pathApi.join(homeDirectory, "Desktop");
|
|
17000
17010
|
}
|
|
17011
|
+
async function resolveUserDirectoryPath(folder, options) {
|
|
17012
|
+
const platform4 = options.platform ?? process.platform;
|
|
17013
|
+
const pathApi = pathImplementation(platform4);
|
|
17014
|
+
const environment = options.environment ?? Bun.env;
|
|
17015
|
+
const homeDirectory = pathApi.normalize(options.homeDirectory ?? homedir4());
|
|
17016
|
+
const displayName = folder === "documents" ? "Documents" : "Downloads";
|
|
17017
|
+
const overrideName = folder === "documents" ? "LIGHTCODE_DOCUMENTS_PATH" : "LIGHTCODE_DOWNLOADS_PATH";
|
|
17018
|
+
const explicit = environment[overrideName]?.trim();
|
|
17019
|
+
if (explicit) {
|
|
17020
|
+
const normalized = normalizeKnownFolderCandidate(explicit, platform4);
|
|
17021
|
+
if (!normalized) {
|
|
17022
|
+
throw new WorkspaceRouteError("workspace_unavailable", `${overrideName} must be an absolute folder path and cannot be a filesystem root.`, 500, false);
|
|
17023
|
+
}
|
|
17024
|
+
return normalized;
|
|
17025
|
+
}
|
|
17026
|
+
const probe = options.probeDirectory ?? probeDirectory;
|
|
17027
|
+
if (platform4 === "win32") {
|
|
17028
|
+
const knownFolder = await (options.resolveWindowsKnownFolder ?? (() => resolveWindowsKnownFolder(environment, folder)))();
|
|
17029
|
+
const userProfile = environment.USERPROFILE?.trim() || homeDirectory;
|
|
17030
|
+
const cloudRoots = [
|
|
17031
|
+
environment.OneDrive,
|
|
17032
|
+
environment.OneDriveConsumer,
|
|
17033
|
+
environment.OneDriveCommercial
|
|
17034
|
+
];
|
|
17035
|
+
const candidates = [
|
|
17036
|
+
knownFolder,
|
|
17037
|
+
...cloudRoots.map((root) => root?.trim() ? path24.win32.join(root.trim(), displayName) : null),
|
|
17038
|
+
path24.win32.join(userProfile, displayName),
|
|
17039
|
+
path24.win32.join(homeDirectory, displayName)
|
|
17040
|
+
];
|
|
17041
|
+
return await firstUsableKnownFolder(candidates, platform4, probe) ?? path24.win32.join(homeDirectory, displayName);
|
|
17042
|
+
}
|
|
17043
|
+
if (platform4 === "linux") {
|
|
17044
|
+
const configHome = environment.XDG_CONFIG_HOME?.trim();
|
|
17045
|
+
const safeConfigHome = configHome && path24.posix.isAbsolute(configHome) ? path24.posix.normalize(configHome) : path24.posix.join(homeDirectory, ".config");
|
|
17046
|
+
const readConfiguration = options.readUserDirsConfiguration ?? readSmallUserDirsConfiguration;
|
|
17047
|
+
const contents = await readConfiguration(path24.posix.join(safeConfigHome, "user-dirs.dirs"));
|
|
17048
|
+
const configured = contents ? parseXdgUserDirectory(contents, homeDirectory, folder === "documents" ? "XDG_DOCUMENTS_DIR" : "XDG_DOWNLOAD_DIR") : null;
|
|
17049
|
+
const selected = await firstUsableKnownFolder([configured], platform4, probe);
|
|
17050
|
+
if (selected) {
|
|
17051
|
+
return selected;
|
|
17052
|
+
}
|
|
17053
|
+
}
|
|
17054
|
+
return pathApi.join(homeDirectory, displayName);
|
|
17055
|
+
}
|
|
17056
|
+
function resolveDocumentsDirectoryPath(options = {}) {
|
|
17057
|
+
return resolveUserDirectoryPath("documents", options);
|
|
17058
|
+
}
|
|
17059
|
+
function resolveDownloadsDirectoryPath(options = {}) {
|
|
17060
|
+
return resolveUserDirectoryPath("downloads", options);
|
|
17061
|
+
}
|
|
17062
|
+
async function resolveProjectsDirectoryPath(options = {}) {
|
|
17063
|
+
const platform4 = options.platform ?? process.platform;
|
|
17064
|
+
const pathApi = pathImplementation(platform4);
|
|
17065
|
+
const environment = options.environment ?? Bun.env;
|
|
17066
|
+
const homeDirectory = pathApi.normalize(options.homeDirectory ?? homedir4());
|
|
17067
|
+
const explicit = environment.LIGHTCODE_PROJECTS_PATH?.trim();
|
|
17068
|
+
if (explicit) {
|
|
17069
|
+
const normalized = normalizeKnownFolderCandidate(explicit, platform4);
|
|
17070
|
+
if (!normalized) {
|
|
17071
|
+
throw new WorkspaceRouteError("workspace_unavailable", "LIGHTCODE_PROJECTS_PATH must be an absolute folder path and cannot be a filesystem root.", 500, false);
|
|
17072
|
+
}
|
|
17073
|
+
return normalized;
|
|
17074
|
+
}
|
|
17075
|
+
const probe = options.probeDirectory ?? probeDirectory;
|
|
17076
|
+
const candidates = [
|
|
17077
|
+
pathApi.join(homeDirectory, "Projects"),
|
|
17078
|
+
pathApi.join(homeDirectory, "projects")
|
|
17079
|
+
];
|
|
17080
|
+
const seen = new Set;
|
|
17081
|
+
for (const candidate of candidates) {
|
|
17082
|
+
const key = platform4 === "win32" ? candidate.toLowerCase() : candidate;
|
|
17083
|
+
if (seen.has(key)) {
|
|
17084
|
+
continue;
|
|
17085
|
+
}
|
|
17086
|
+
seen.add(key);
|
|
17087
|
+
const state = await probe(candidate);
|
|
17088
|
+
if (state === "available" || state === "permission-denied") {
|
|
17089
|
+
return candidate;
|
|
17090
|
+
}
|
|
17091
|
+
}
|
|
17092
|
+
return candidates[0];
|
|
17093
|
+
}
|
|
17001
17094
|
function filesystemErrorCode(error) {
|
|
17002
17095
|
if (typeof error !== "object" || error === null) {
|
|
17003
17096
|
return;
|
|
@@ -17057,6 +17150,9 @@ function isContainedPath2(root, candidate) {
|
|
|
17057
17150
|
async function resolveKnownDirectory(directory) {
|
|
17058
17151
|
try {
|
|
17059
17152
|
const root = await fs8.realpath(path24.resolve(directory));
|
|
17153
|
+
if (root === path24.parse(root).root) {
|
|
17154
|
+
throw new WorkspaceRouteError("workspace_unavailable", "A filesystem root cannot be opened as a workspace location.", 500, false);
|
|
17155
|
+
}
|
|
17060
17156
|
const stats = await fs8.stat(root, { bigint: true });
|
|
17061
17157
|
if (!stats.isDirectory()) {
|
|
17062
17158
|
throw new WorkspaceRouteError("workspace_not_directory", "The selected path is not a directory.", 422, false);
|
|
@@ -17086,11 +17182,11 @@ async function assertRootIdentity({
|
|
|
17086
17182
|
throw mapFilesystemError(error);
|
|
17087
17183
|
}
|
|
17088
17184
|
}
|
|
17089
|
-
async function symlinkFailure(root, candidate) {
|
|
17185
|
+
async function symlinkFailure(root, candidate, locationName) {
|
|
17090
17186
|
try {
|
|
17091
17187
|
const target = await fs8.realpath(candidate);
|
|
17092
17188
|
if (!isContainedPath2(root, target)) {
|
|
17093
|
-
return new WorkspaceRouteError("workspace_symlink_escape",
|
|
17189
|
+
return new WorkspaceRouteError("workspace_symlink_escape", `This symbolic link points outside the allowed ${locationName} location.`, 403, false);
|
|
17094
17190
|
}
|
|
17095
17191
|
return new WorkspaceRouteError("workspace_symlink_not_traversable", "Symbolic links cannot be used to navigate the workspace browser.", 422, false);
|
|
17096
17192
|
} catch (error) {
|
|
@@ -17100,12 +17196,12 @@ async function symlinkFailure(root, candidate) {
|
|
|
17100
17196
|
return new WorkspaceRouteError("workspace_symlink_broken", "This symbolic link is broken and cannot be opened.", 422, false);
|
|
17101
17197
|
}
|
|
17102
17198
|
}
|
|
17103
|
-
async function resolveDirectoryFromSegments(root, segments) {
|
|
17199
|
+
async function resolveDirectoryFromSegments(root, segments, locationName) {
|
|
17104
17200
|
let current = root;
|
|
17105
17201
|
for (const segment of segments) {
|
|
17106
17202
|
const candidate = path24.join(current, segment);
|
|
17107
17203
|
if (!isContainedPath2(root, candidate)) {
|
|
17108
|
-
throw new WorkspaceRouteError("workspace_symlink_escape",
|
|
17204
|
+
throw new WorkspaceRouteError("workspace_symlink_escape", `The requested path is outside the allowed ${locationName} location.`, 403, false);
|
|
17109
17205
|
}
|
|
17110
17206
|
let stats;
|
|
17111
17207
|
try {
|
|
@@ -17114,7 +17210,7 @@ async function resolveDirectoryFromSegments(root, segments) {
|
|
|
17114
17210
|
throw mapFilesystemError(error);
|
|
17115
17211
|
}
|
|
17116
17212
|
if (stats.isSymbolicLink()) {
|
|
17117
|
-
throw await symlinkFailure(root, candidate);
|
|
17213
|
+
throw await symlinkFailure(root, candidate, locationName);
|
|
17118
17214
|
}
|
|
17119
17215
|
if (!stats.isDirectory()) {
|
|
17120
17216
|
throw new WorkspaceRouteError("workspace_not_directory", "The selected path is not a directory.", 422, false);
|
|
@@ -17125,7 +17221,7 @@ async function resolveDirectoryFromSegments(root, segments) {
|
|
|
17125
17221
|
throw mapFilesystemError(error);
|
|
17126
17222
|
});
|
|
17127
17223
|
if (!isContainedPath2(root, canonical)) {
|
|
17128
|
-
throw new WorkspaceRouteError("workspace_symlink_escape",
|
|
17224
|
+
throw new WorkspaceRouteError("workspace_symlink_escape", `The requested path is outside the allowed ${locationName} location.`, 403, false);
|
|
17129
17225
|
}
|
|
17130
17226
|
const metadata = await fs8.lstat(canonical, { bigint: true });
|
|
17131
17227
|
if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
|
|
@@ -17255,17 +17351,78 @@ function trimOldestEntries(map, maximum) {
|
|
|
17255
17351
|
map.delete(oldest);
|
|
17256
17352
|
}
|
|
17257
17353
|
}
|
|
17354
|
+
function configuredKnownLocationResolver(options, id, fallback) {
|
|
17355
|
+
const configuredResolver = options.resolveLocationPaths?.[id];
|
|
17356
|
+
if (configuredResolver) {
|
|
17357
|
+
return configuredResolver;
|
|
17358
|
+
}
|
|
17359
|
+
const configuredPath = options.locationPaths?.[id];
|
|
17360
|
+
if (configuredPath !== undefined) {
|
|
17361
|
+
return async () => configuredPath;
|
|
17362
|
+
}
|
|
17363
|
+
return fallback;
|
|
17364
|
+
}
|
|
17365
|
+
function knownLocation(definition, state) {
|
|
17366
|
+
return {
|
|
17367
|
+
id: definition.id,
|
|
17368
|
+
name: definition.name,
|
|
17369
|
+
kind: "known-folder",
|
|
17370
|
+
state,
|
|
17371
|
+
pathLabel: definition.pathLabel
|
|
17372
|
+
};
|
|
17373
|
+
}
|
|
17258
17374
|
function createWorkspaceRoutes(options = {}) {
|
|
17259
17375
|
const now = options.now ?? Date.now;
|
|
17260
17376
|
const capabilityTtlMs = options.browserCapabilityTtlMs ?? defaultBrowserCapabilityTtlMs;
|
|
17261
|
-
const
|
|
17262
|
-
const normalized =
|
|
17377
|
+
const getLegacyDesktopPath = options.resolveDesktopPath ?? (options.desktopPath ? async () => {
|
|
17378
|
+
const normalized = normalizeKnownFolderCandidate(options.desktopPath, process.platform);
|
|
17263
17379
|
if (!normalized) {
|
|
17264
17380
|
throw new WorkspaceRouteError("workspace_unavailable", "The configured Desktop path must be absolute and cannot be a filesystem root.", 500, false);
|
|
17265
17381
|
}
|
|
17266
17382
|
return normalized;
|
|
17267
17383
|
} : () => resolveDesktopDirectoryPath());
|
|
17268
17384
|
const desktopPathLabel = options.desktopPathLabel ?? "Desktop";
|
|
17385
|
+
const defaultHomePath = async () => homedir4();
|
|
17386
|
+
const defaultDocumentsPath = () => resolveDocumentsDirectoryPath();
|
|
17387
|
+
const defaultDownloadsPath = () => resolveDownloadsDirectoryPath();
|
|
17388
|
+
const definitions = [
|
|
17389
|
+
{
|
|
17390
|
+
id: "desktop",
|
|
17391
|
+
name: "Desktop",
|
|
17392
|
+
rootName: "Desktop",
|
|
17393
|
+
pathLabel: options.locationPathLabels?.desktop ?? desktopPathLabel,
|
|
17394
|
+
resolvePath: configuredKnownLocationResolver(options, "desktop", getLegacyDesktopPath)
|
|
17395
|
+
},
|
|
17396
|
+
{
|
|
17397
|
+
id: "home",
|
|
17398
|
+
name: "Home (broad access)",
|
|
17399
|
+
rootName: "Home",
|
|
17400
|
+
pathLabel: options.locationPathLabels?.home ?? "Home",
|
|
17401
|
+
resolvePath: configuredKnownLocationResolver(options, "home", defaultHomePath)
|
|
17402
|
+
},
|
|
17403
|
+
{
|
|
17404
|
+
id: "documents",
|
|
17405
|
+
name: "Documents",
|
|
17406
|
+
rootName: "Documents",
|
|
17407
|
+
pathLabel: options.locationPathLabels?.documents ?? "Documents",
|
|
17408
|
+
resolvePath: configuredKnownLocationResolver(options, "documents", defaultDocumentsPath)
|
|
17409
|
+
},
|
|
17410
|
+
{
|
|
17411
|
+
id: "downloads",
|
|
17412
|
+
name: "Downloads",
|
|
17413
|
+
rootName: "Downloads",
|
|
17414
|
+
pathLabel: options.locationPathLabels?.downloads ?? "Downloads",
|
|
17415
|
+
resolvePath: configuredKnownLocationResolver(options, "downloads", defaultDownloadsPath)
|
|
17416
|
+
},
|
|
17417
|
+
{
|
|
17418
|
+
id: "projects",
|
|
17419
|
+
name: "Projects",
|
|
17420
|
+
rootName: "Projects",
|
|
17421
|
+
pathLabel: options.locationPathLabels?.projects ?? "Projects",
|
|
17422
|
+
resolvePath: configuredKnownLocationResolver(options, "projects", () => resolveProjectsDirectoryPath())
|
|
17423
|
+
}
|
|
17424
|
+
];
|
|
17425
|
+
const definitionsById = new Map(definitions.map((definition) => [definition.id, definition]));
|
|
17269
17426
|
const createSession = options.createSession ?? createChatSession;
|
|
17270
17427
|
const cursorSecret = randomBytes(32);
|
|
17271
17428
|
const browserCapabilities = new Map;
|
|
@@ -17274,7 +17431,7 @@ function createWorkspaceRoutes(options = {}) {
|
|
|
17274
17431
|
const capability2 = browserCapabilities.get(browserId);
|
|
17275
17432
|
if (!capability2 || capability2.expiresAtMs <= now()) {
|
|
17276
17433
|
browserCapabilities.delete(browserId);
|
|
17277
|
-
throw new WorkspaceRouteError("browser_capability_expired", "This folder browser expired. Open
|
|
17434
|
+
throw new WorkspaceRouteError("browser_capability_expired", capability2 ? `This folder browser expired. Open ${capability2.rootName} again.` : "This folder browser expired. Open a location again.", 410, true);
|
|
17278
17435
|
}
|
|
17279
17436
|
return capability2;
|
|
17280
17437
|
};
|
|
@@ -17286,15 +17443,7 @@ function createWorkspaceRoutes(options = {}) {
|
|
|
17286
17443
|
return grant;
|
|
17287
17444
|
};
|
|
17288
17445
|
return new Hono7().get("/locations", (c) => c.json(workspaceLocationsResponseSchema.parse({
|
|
17289
|
-
locations:
|
|
17290
|
-
{
|
|
17291
|
-
id: "desktop",
|
|
17292
|
-
name: "Desktop",
|
|
17293
|
-
kind: "known-folder",
|
|
17294
|
-
state: "unprobed",
|
|
17295
|
-
pathLabel: desktopPathLabel
|
|
17296
|
-
}
|
|
17297
|
-
]
|
|
17446
|
+
locations: definitions.map((definition) => knownLocation(definition, "unprobed"))
|
|
17298
17447
|
}))).post("/browser/open", zValidator3("json", workspaceBrowserOpenRequestSchema, (result, c) => {
|
|
17299
17448
|
if (!result.success) {
|
|
17300
17449
|
return invalidRequestResponse(c);
|
|
@@ -17302,32 +17451,30 @@ function createWorkspaceRoutes(options = {}) {
|
|
|
17302
17451
|
}), async (c) => {
|
|
17303
17452
|
try {
|
|
17304
17453
|
const { locationId } = c.req.valid("json");
|
|
17305
|
-
|
|
17454
|
+
const definition = definitionsById.get(locationId);
|
|
17455
|
+
if (!definition) {
|
|
17306
17456
|
throw new WorkspaceRouteError("location_not_found", "Workspace location not found.", 404, false);
|
|
17307
17457
|
}
|
|
17308
|
-
const
|
|
17309
|
-
const
|
|
17310
|
-
if (!
|
|
17311
|
-
throw new WorkspaceRouteError("workspace_unavailable",
|
|
17458
|
+
const discoveredPath = await definition.resolvePath();
|
|
17459
|
+
const safePath = normalizeKnownFolderCandidate(discoveredPath, process.platform);
|
|
17460
|
+
if (!safePath) {
|
|
17461
|
+
throw new WorkspaceRouteError("workspace_unavailable", `The resolved ${definition.rootName} location is not a safe absolute folder path.`, 500, false);
|
|
17312
17462
|
}
|
|
17313
|
-
const resolved = await resolveKnownDirectory(
|
|
17463
|
+
const resolved = await resolveKnownDirectory(safePath);
|
|
17314
17464
|
const browserId = crypto.randomUUID();
|
|
17315
17465
|
const expiresAtMs = now() + capabilityTtlMs;
|
|
17466
|
+
const location = knownLocation(definition, "available");
|
|
17316
17467
|
browserCapabilities.set(browserId, {
|
|
17317
17468
|
id: browserId,
|
|
17318
17469
|
...resolved,
|
|
17470
|
+
location,
|
|
17471
|
+
rootName: definition.rootName,
|
|
17319
17472
|
expiresAtMs
|
|
17320
17473
|
});
|
|
17321
17474
|
trimOldestEntries(browserCapabilities, maximumBrowserCapabilities);
|
|
17322
17475
|
return c.json(workspaceBrowserOpenResponseSchema.parse({
|
|
17323
17476
|
browserId,
|
|
17324
|
-
location
|
|
17325
|
-
id: "desktop",
|
|
17326
|
-
name: "Desktop",
|
|
17327
|
-
kind: "known-folder",
|
|
17328
|
-
state: "available",
|
|
17329
|
-
pathLabel: desktopPathLabel
|
|
17330
|
-
},
|
|
17477
|
+
location,
|
|
17331
17478
|
expiresAt: new Date(expiresAtMs).toISOString()
|
|
17332
17479
|
}), 201);
|
|
17333
17480
|
} catch (error) {
|
|
@@ -17347,7 +17494,7 @@ function createWorkspaceRoutes(options = {}) {
|
|
|
17347
17494
|
const input = c.req.valid("json");
|
|
17348
17495
|
const capability2 = getBrowserCapability(browserId);
|
|
17349
17496
|
await assertRootIdentity(capability2);
|
|
17350
|
-
const directory = await resolveDirectoryFromSegments(capability2.root, input.segments);
|
|
17497
|
+
const directory = await resolveDirectoryFromSegments(capability2.root, input.segments, capability2.rootName);
|
|
17351
17498
|
const beforeRead = await assertDirectoryIdentity(capability2.root, directory);
|
|
17352
17499
|
const cursorBinding = {
|
|
17353
17500
|
browserId,
|
|
@@ -17425,7 +17572,7 @@ function createWorkspaceRoutes(options = {}) {
|
|
|
17425
17572
|
const { segments } = c.req.valid("json");
|
|
17426
17573
|
const capability2 = getBrowserCapability(browserId);
|
|
17427
17574
|
await assertRootIdentity(capability2);
|
|
17428
|
-
const directory = await resolveDirectoryFromSegments(capability2.root, segments);
|
|
17575
|
+
const directory = await resolveDirectoryFromSegments(capability2.root, segments, capability2.rootName);
|
|
17429
17576
|
await assertDirectoryIdentity(capability2.root, directory);
|
|
17430
17577
|
await options.beforeDirectoryOperation?.({
|
|
17431
17578
|
operation: "select",
|
|
@@ -17434,8 +17581,11 @@ function createWorkspaceRoutes(options = {}) {
|
|
|
17434
17581
|
await fs8.access(directory.path, fsConstants2.R_OK | fsConstants2.X_OK);
|
|
17435
17582
|
const createdAt = new Date(now()).toISOString();
|
|
17436
17583
|
const id = crypto.randomUUID();
|
|
17437
|
-
const name = segments.at(-1) ??
|
|
17438
|
-
const pathLabel = [
|
|
17584
|
+
const name = segments.at(-1) ?? capability2.rootName;
|
|
17585
|
+
const pathLabel = [
|
|
17586
|
+
capability2.location.pathLabel,
|
|
17587
|
+
...segments
|
|
17588
|
+
].join("/");
|
|
17439
17589
|
const grant = {
|
|
17440
17590
|
id,
|
|
17441
17591
|
name,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--buncss-light: ;--buncss-dark:initial;color-scheme:dark;font-synthesis:none;--base:#0d1117;--panel:#121820;--elevated:#18212b;--inset:#10151c;--border:#273241;--border-soft:#202a36;--accent:#f2a65a;--accent-soft:#33260f;--accent-text:#f6c99a;--text:#eef3f8;--text-secondary:#b6c2cf;--text-muted:#7f8d9d;--success:#8bd49c;--warning:#e5c07b;--error:#f28b82;--info:#9cbdfd;--mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}*{box-sizing:border-box}html,body,#root{width:100%;height:100%;margin:0}body{overflow:hidden;background:var(--base);color:var(--text);text-rendering:optimizeLegibility;min-width:320px;font-size:14px;line-height:1.5}button,textarea,input,select{color:inherit;font:inherit}button,select,label,summary{-webkit-tap-highlight-color:transparent}button{border:0}button:not(:disabled),select:not(:disabled),summary{cursor:pointer}label:has(input:not(:disabled)){cursor:pointer}button:disabled,select:disabled,textarea:disabled,input:disabled{cursor:not-allowed;opacity:.5}:focus-visible{outline:2px solid var(--accent);outline-offset:2px}::selection{color:var(--text);background:#67421e}.sr-only{position:absolute;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0}.app-shell{display:grid;grid-template-columns:264px minmax(0,1fr);background:var(--panel);width:100%;height:100dvh}.session-rail{position:relative;z-index:30;display:flex;overflow:hidden;background:var(--base);border-right:1px solid var(--border);flex-direction:column;min-width:0;height:100%}.rail-header{display:flex;border-bottom:1px solid var(--border-soft);justify-content:space-between;align-items: center;min-height:58px;padding:0 16px}.brand-lockup{display:flex;color:var(--text);letter-spacing:-.01em;align-items: center;gap:9px;font-size:15px;font-weight:670}.brand-mark,.new-session-mark,.empty-mark,.authorization-mark{display:inline-flex;color:var(--accent);justify-content:center;align-items: center}.brand-mark{background:var(--accent-soft);border:1px solid #57402a;border-radius:8px;width:27px;height:27px}.rail-actions{display:grid;gap:7px;padding:14px 12px 12px}.new-session-button,.project-button{display:flex;border:1px solid var(--border);background:var(--panel);color:var(--text-secondary);text-align:left;border-radius:9px;align-items: center;gap:9px;width:100%;min-height:36px;padding:7px 10px;transition:background-color .16s ease-out,border-color .16s ease-out,color .16s ease-out}.new-session-button:hover:not(:disabled),.project-button:hover:not(:disabled){background:var(--elevated);color:var(--text);border-color:#3b495b}.new-session-button kbd{color:var(--text-muted);font-family:var(--mono);margin-left:auto;font-size:11px;font-weight:500}.project-button{color:var(--text-muted);background:0 0;border-color:#0000}.project-button span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}.rail-section-heading{display:flex;color:var(--text-muted);letter-spacing:.055em;text-transform:uppercase;justify-content:space-between;align-items: center;padding:7px 15px;font-size:11px;font-weight:650}.session-list{overflow-y:auto;flex:1;min-height:80px;padding:2px 8px 18px}.session-item{position:relative;display:flex;color:var(--text-secondary);text-align:left;background:0 0;border-radius:8px;align-items: center;gap:9px;width:100%;min-height:58px;padding:8px 9px;transition:background-color .16s ease-out,color .16s ease-out}.session-item:hover{background:var(--panel);color:var(--text)}.session-item.active{background:var(--elevated);color:var(--text)}.session-marker{background:var(--border);border-radius:50%;flex:none;width:5px;height:5px}.session-item.active .session-marker{background:var(--accent)}.session-copy{display:grid;gap:2px;min-width:0}.session-copy strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:590}.session-copy small{color:var(--text-muted);font-size:11px}.rail-state{display:flex;border:1px solid var(--border-soft);color:var(--text-muted);border-radius:9px;align-items: flex-start;gap:10px;margin:7px;padding:12px}.rail-state p{margin:0;font-size:12px}.rail-footer{border-top:1px solid var(--border-soft);padding:8px 8px 14px}.later-item{display:flex;color:var(--text-muted);text-align:left;background:0 0;border-radius:7px;align-items: center;gap:9px;width:100%;min-height:32px;padding:5px 8px}.later-item span{flex:1}.later-item small{font-family:var(--mono);font-size:10px}.session-skeleton,.folder-skeleton,.conversation-skeleton{display:grid;gap:12px;padding:12px}.session-skeleton span,.folder-skeleton span,.conversation-skeleton span{display:block;background:var(--elevated);animation:skeleton-pulse 1.4s ease-in-out infinite alternate;border-radius:4px;height:10px}.session-skeleton span:first-child{width:82%}.session-skeleton span:nth-child(2){width:74%}.session-skeleton span:nth-child(3){width:68%}.session-skeleton span:nth-child(4){width:61%}.folder-skeleton span:first-child{width:58%}.folder-skeleton span:nth-child(2){width:71%}.folder-skeleton span:nth-child(3){width:84%}.folder-skeleton span:nth-child(4){width:67%}.folder-skeleton span:nth-child(5){width:79%}.folder-skeleton span:nth-child(6){width:62%}.folder-skeleton span:nth-child(7){width:75%}@keyframes skeleton-pulse{0%{opacity:.45}to{opacity:.95}}.workspace-main{display:flex;overflow:hidden;background:var(--panel);flex-direction:column;min-width:0;min-height:0}.workspace-ribbon{position:relative;z-index:10;display:flex;border-top:2px solid var(--accent);color:var(--accent-text);background:#211a13;border-bottom:1px solid #493522;align-items: center;gap:9px;min-height:56px;padding:6px 14px}.workspace-path{display:flex;color:inherit;text-align:left;background:0 0;border-radius:8px;align-items: center;gap:8px;min-width:180px;max-width:min(42vw,560px);padding:5px 7px}.workspace-path:hover{background:#2c2117}.workspace-icon{display:inline-flex;color:var(--accent);align-items: center}.workspace-path-copy{display:grid;gap:0;min-width:0}.workspace-path-copy strong,.workspace-path-copy small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.workspace-path-copy strong{color:var(--text);font-size:13px;font-weight:650}.workspace-path-copy small{color:#be9e7e;font-family:var(--mono);font-size:10px}.ribbon-status,.ribbon-model{display:flex;color:#d8b38d;white-space:nowrap;align-items: center;gap:7px;font-size:12px}.live-dot{display:inline-block;background:#6f665d;border-radius:50%;flex:none;width:7px;height:7px}.live-dot.active,.agent-working .live-dot{background:var(--accent);animation:live-pulse 1.1s ease-out infinite alternate}@keyframes live-pulse{0%{opacity:.55}to{opacity:1}}.ribbon-spacer{flex:1}.ribbon-model{overflow:hidden;color:#b69778;text-overflow:ellipsis;max-width:190px}.ribbon-select{position:relative;display:flex;color:var(--accent-text);background:#2b2016;border:1px solid #59412b;border-radius:7px;align-items: center;gap:6px;height:32px;padding:0 7px}.ribbon-select select{appearance:none;outline:0;color:inherit;background:0 0;border:0;font-size:12px}.ribbon-select select:focus-visible{outline:0}.ribbon-select:focus-within{outline:2px solid var(--accent);outline-offset:2px}.chat-surface{display:flex;overflow:hidden;flex-direction:column;flex:1;min-height:0}.conversation-scroll{overflow-y:auto;overscroll-behavior:contain;flex:1;min-height:0}.conversation-column{width:min(100%,900px);margin:0 auto;padding-inline-start:28px;padding-inline-end:28px}.conversation-scroll>.conversation-column{display:flex;flex-direction:column;min-height:100%;padding-top:32px;padding-bottom:28px}.conversation-empty{display:grid;justify-items:start;width:min(100%,610px);margin:auto;padding:48px 0 92px}.empty-mark{background:var(--accent-soft);border:1px solid #62472d;border-radius:12px;width:43px;height:43px;margin-bottom:20px}.conversation-empty h1,.new-session-title h1,.authorization-gate h1{color:var(--text);letter-spacing:-.025em;margin:0;font-size:25px;font-weight:650;line-height:1.2}.conversation-empty p{color:var(--text-secondary);max-width:58ch;margin:12px 0 0;font-size:15px}.chat-message{display:grid;grid-template-columns:108px minmax(0,1fr);border-bottom:1px solid var(--border-soft);gap:20px;padding:24px 0 27px}.chat-message>header{display:flex;color:var(--text-muted);align-items: center;gap:7px;height:26px;font-size:12px;font-weight:640}.role-mark{display:inline-flex;border:1px solid var(--border);background:var(--inset);border-radius:7px;justify-content:center;align-items: center;width:24px;height:24px}.role-assistant .role-mark{color:var(--success);border-color:#2d4a35}.role-user .role-mark{color:var(--info);border-color:#354561}.message-parts{min-width:0}.message-text{color:var(--text);font-size:14px;line-height:1.72}.message-text p{white-space:pre-wrap;overflow-wrap:anywhere;max-width:72ch;margin:0 0 15px}.message-text p:last-child{margin-bottom:0}.message-text .markdown-heading{color:var(--text);letter-spacing:-.018em;max-width:46ch;margin:20px 0 8px;font-size:18px;font-weight:680;line-height:1.3}.message-text .markdown-heading.level-2{font-size:16px}.message-text .markdown-heading.level-3,.message-text .markdown-heading.level-4,.message-text .markdown-heading.level-5,.message-text .markdown-heading.level-6{font-size:14px}.message-text ul,.message-text ol{max-width:72ch;margin:0 0 15px;padding-left:22px}.message-text li+li{margin-top:4px}.message-text blockquote{border-left:2px solid var(--accent);color:var(--text-secondary);white-space:pre-wrap;max-width:72ch;margin:12px 0 15px;padding-left:13px}.message-text a{color:var(--info);text-underline-offset:2px}.inline-code{border:1px solid var(--border-soft);background:var(--inset);color:#dce6ef;font-family:var(--mono);border-radius:5px;padding:1px 5px;font-size:.9em}.code-block{overflow:hidden;border:1px solid var(--border);background:var(--inset);border-radius:11px;margin:14px 0 18px}.code-header{display:flex;border-bottom:1px solid var(--border-soft);color:var(--text-muted);font-family:var(--mono);justify-content:space-between;align-items: center;min-height:34px;padding:0 12px;font-size:11px}.code-header button{color:var(--text-secondary);font-family:inherit;font-size:inherit;background:0 0}.code-block pre,.tool-details pre,.authorization-gate pre{overflow:auto;font-family:var(--mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;font-size:12px;line-height:1.65}.code-block pre{color:#dce6ef;white-space:pre;overflow-wrap:normal;padding:14px 16px 17px}.reasoning-part{color:var(--text-muted);margin:8px 0 14px;font-size:12px}.reasoning-part summary{color:var(--text-secondary);width:max-content}.reasoning-part p{white-space:pre-wrap;margin:8px 0 0}.tool-event{overflow:hidden;border:1px solid var(--border);background:var(--inset);border-radius:11px;margin:10px 0}.tool-event[open]{background:#111820}.tool-event summary{display:flex;list-style:none;align-items: center;gap:10px;min-height:48px;padding:8px 12px}.tool-event summary::-webkit-details-marker{display:none}.tool-event summary>svg:last-child{margin-left:auto;transition:transform .16s ease-out}.tool-event[open] summary>svg:last-child{transform:rotate(180deg)}.tool-state-mark{display:inline-flex;background:var(--elevated);color:var(--text-muted);border-radius:7px;justify-content:center;align-items: center;width:26px;height:26px}.state-output-available .tool-state-mark{color:var(--success)}.state-output-error .tool-state-mark{color:var(--error)}.state-approval-requested .tool-state-mark{color:var(--warning)}.tool-summary-copy{display:grid;min-width:0}.tool-summary-copy strong{color:var(--text-secondary);font-size:12px;font-weight:610}.tool-summary-copy small{overflow:hidden;color:var(--text-muted);font-family:var(--mono);text-overflow:ellipsis;white-space:nowrap;font-size:10px}.tool-details{display:grid;gap:12px;padding:0 12px 13px 48px}.tool-details>div>span{display:block;color:var(--text-muted);letter-spacing:.04em;text-transform:uppercase;margin-bottom:4px;font-size:10px;font-weight:650}.tool-details pre{border:1px solid var(--border-soft);background:var(--base);color:var(--text-secondary);border-radius:7px;max-height:280px;padding:10px}.source-link,.attachment-line{display:flex;color:var(--info);text-decoration:none;align-items: center;gap:7px;width:max-content;max-width:100%;margin:8px 0;font-size:12px}.source-link span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.source-link:not(.static):hover{text-decoration:underline}.source-link.static,.attachment-line{color:var(--text-muted)}.agent-working{display:flex;color:var(--text-muted);align-items: center;gap:9px;padding:17px 128px 0;font-size:12px}.conversation-controls{position:relative;z-index:4;background:var(--panel);flex:none;padding:0 0 10px}.inline-error,.new-session-error{display:flex;color:#f2aaa3;background:#261719;border:1px solid #603638;border-radius:8px;align-items: center;gap:9px;margin-bottom:8px;padding:9px 11px;font-size:12px}.inline-error span{flex:1}.inline-error button{color:var(--text);background:0 0;font-weight:620}.approval-dock,.prompt-dock{overflow:hidden;background:#211a13;border:1px solid #62472d;border-radius:11px;margin-bottom:8px}.approval-row{display:grid;grid-template-columns:auto minmax(0,1fr)auto;align-items: center;gap:11px;padding:12px}.approval-row+.approval-row{border-top:1px solid #493522}.approval-icon{display:inline-flex;color:var(--warning)}.approval-row strong,.prompt-dock strong{color:var(--text);font-size:12px}.approval-row p,.prompt-dock p{color:var(--text-secondary);margin:1px 0;font-size:12px}.approval-row small{color:#b39272;font-size:10px}.approval-actions,.project-browser-actions,.prompt-options,.prompt-custom{display:flex;align-items: center;gap:7px}.primary-button,.secondary-button{display:inline-flex;border-radius:8px;justify-content:center;align-items: center;gap:7px;min-height:36px;padding:7px 12px;font-size:12px;font-weight:650}.primary-button{background:var(--accent);color:#23170c}.primary-button:hover:not(:disabled){background:#ffc077}.secondary-button{border:1px solid var(--border);background:var(--elevated);color:var(--text-secondary)}.secondary-button:hover:not(:disabled){color:var(--text);border-color:#3e4b5d}.compact-primary{min-height:32px;padding:5px 10px}.prompt-dock{display:grid;gap:10px;padding:12px}.prompt-custom input{border:1px solid var(--border);outline:0;background:var(--inset);border-radius:7px;flex:1;min-width:0;min-height:34px;padding:6px 9px}.prompt-custom input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.composer,.starter-composer{overflow:hidden;background:var(--inset);border:1px solid #3c495a;border-radius:13px;transition:border-color .16s ease-out,background-color .16s ease-out}.composer:focus-within,.starter-composer:focus-within{border-color:var(--accent);background:#11171e}.composer.disabled{opacity:.72}.composer textarea,.starter-composer textarea{display:block;resize:none;outline:0;color:var(--text);field-sizing:content;background:0 0;border:0;width:100%;min-height:62px;max-height:220px;padding:14px 16px 6px;line-height:1.55}.composer textarea::placeholder,.starter-composer textarea::placeholder,.prompt-custom input::placeholder{color:var(--text-muted);opacity:1}.composer-toolbar,.starter-toolbar{display:flex;align-items: center;min-height:40px;padding:4px 7px 7px 13px}.composer-toolbar>span,.starter-toolbar>span{display:flex;color:var(--text-muted);align-items: center;gap:7px;font-size:11px}.send-button,.abort-button{display:inline-flex;border-radius:8px;justify-content:center;align-items: center;gap:6px;min-height:31px;margin-left:auto}.send-button{background:var(--accent);color:#20150b;width:31px}.send-button:hover:not(:disabled){background:#ffc077}.abort-button{color:#f4aaa3;background:#29191b;border:1px solid #674244;padding:5px 9px;font-size:11px}.composer-hint{display:flex;color:var(--text-muted);justify-content:space-between;padding:6px 2px 0;font-size:10px}.new-session-surface{display:grid;overflow-y:auto;flex:1;place-items: center;min-height:0;padding:32px}.new-session-inner{width:min(100%,800px);transform:translateY(-5vh)}.new-session-title{display:flex;align-items: center;gap:14px;margin-bottom:24px}.new-session-mark{background:var(--accent-soft);border:1px solid #62472d;border-radius:12px;width:44px;height:44px}.new-session-title>div{min-width:0}.new-session-title h1{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.new-session-title button{display:flex;color:var(--text-muted);font-family:var(--mono);background:0 0;align-items: center;gap:6px;max-width:100%;padding:3px 0;font-size:11px}.new-session-title button:hover{color:var(--accent-text)}.starter-composer textarea{min-height:116px;padding-top:17px;font-size:15px}.button-loading{animation:spin .8s linear infinite;border:2px solid #6b4622;border-top-color:#20150b;border-radius:50%;width:13px;height:13px}@keyframes spin{to{transform:rotate(360deg)}}.new-session-error{margin-top:9px;margin-bottom:0}.starter-suggestions{display:flex;flex-wrap:wrap;gap:7px;margin-top:12px}.starter-suggestions button{border:1px solid var(--border-soft);color:var(--text-muted);background:0 0;border-radius:8px;min-height:34px;padding:6px 10px;font-size:11px}.starter-suggestions button:hover:not(:disabled){border-color:var(--border);background:var(--inset);color:var(--text-secondary)}.authorization-gate{display:grid;background:var(--base);align-content: center;justify-items:start;width:min(100% - 40px,560px);height:100%;margin:0 auto}.authorization-mark{background:var(--accent-soft);border:1px solid #62472d;border-radius:13px;width:48px;height:48px;margin-bottom:22px}.authorization-gate p{color:var(--text-secondary);margin:13px 0 17px;font-size:15px}.authorization-gate pre{border:1px solid var(--border);background:var(--inset);color:var(--accent-text);border-radius:9px;min-width:190px;padding:11px 14px}.authorization-gate small{color:var(--text-muted);max-width:58ch;margin-top:13px}.project-browser-backdrop{position:fixed;z-index:80;display:grid;background:#06090dc7;place-items: center;padding:24px;inset:0}.project-browser{display:grid;grid-template-rows:auto auto auto minmax(220px,1fr)auto;overflow:hidden;background:var(--panel);border:1px solid #3c495a;border-radius:14px;width:min(100%,780px);height:min(720px,100dvh - 48px);box-shadow:0 18px 54px #0000006b}.project-browser-header{display:flex;justify-content:space-between;align-items: flex-start;padding:19px 20px 16px}.project-browser-header h1{letter-spacing:-.015em;margin:0;font-size:18px;font-weight:660}.project-browser-header p{color:var(--text-muted);margin:3px 0 0;font-size:12px}.icon-button{display:inline-flex;color:var(--text-muted);background:0 0;border-radius:8px;justify-content:center;align-items: center;width:34px;height:34px}.icon-button:hover:not(:disabled){background:var(--elevated);color:var(--text)}.icon-button.compact{width:28px;height:28px}.location-strip{display:flex;border-bottom:1px solid var(--border-soft);gap:6px;padding:0 20px 12px}.location-button{display:inline-flex;border:1px solid var(--border);background:var(--inset);color:var(--text-muted);border-radius:8px;align-items: center;gap:7px;min-height:34px;padding:6px 10px;font-size:12px}.location-button.active{background:var(--accent-soft);color:var(--accent-text);border-color:#62472d}.folder-breadcrumbs{display:flex;overflow-x:auto;border-bottom:1px solid var(--border-soft);color:var(--text-muted);font-family:var(--mono);white-space:nowrap;align-items: center;gap:2px;min-height:46px;padding:7px 16px;font-size:11px}.folder-breadcrumbs>span{display:inline-flex;align-items: center}.folder-breadcrumbs button:not(.icon-button){color:var(--text-secondary);background:0 0;border-radius:5px;padding:4px 5px}.folder-breadcrumbs button:not(.icon-button):hover{background:var(--elevated);color:var(--text)}.project-browser-body{overflow-y:auto;min-height:0;padding:8px}.folder-list{display:grid;align-content: start}.folder-list.loading{opacity:.55;pointer-events:none}.folder-row{display:grid;grid-template-columns:22px minmax(0,1fr)auto 20px;color:var(--text-secondary);text-align:left;background:0 0;border-radius:7px;align-items: center;gap:8px;width:100%;min-height:42px;padding:7px 10px}button.folder-row:hover:not(:disabled){background:var(--elevated);color:var(--text)}.folder-row>svg:first-child{color:var(--accent-text)}.folder-row>svg:last-child,.file-row,.file-row>svg:first-child{color:var(--text-muted)}.entry-note{color:var(--text-muted);font-family:var(--mono);font-size:10px}.browser-state{display:grid;color:var(--text-muted);text-align:center;align-content: center;justify-items:center;min-height:220px;padding:28px}.browser-state h2{color:var(--text);margin:13px 0 3px;font-size:15px;font-weight:640}.browser-state p{max-width:50ch;margin:0 0 14px;font-size:12px}.error-state>svg{color:var(--error)}.load-more-button{border:1px solid var(--border);background:var(--inset);color:var(--text-secondary);border-radius:8px;min-height:38px;margin:9px}.project-browser-footer{display:flex;border-top:1px solid var(--border-soft);background:var(--base);justify-content:space-between;align-items: center;gap:16px;min-height:67px;padding:11px 16px}.checkbox-control{display:flex;color:var(--text-muted);white-space:nowrap;align-items: center;gap:8px;font-size:11px}.checkbox-control input{accent-color:var(--accent);width:15px;height:15px}.broad-root-warning{display:grid;grid-template-columns:auto minmax(180px,1fr);color:var(--warning);align-items: start;gap:2px 8px;max-width:520px;font-size:11px}.broad-root-warning>svg{margin-top:1px}.broad-root-warning button{grid-column:2;color:var(--text-secondary);text-decoration:underline;background:0 0;width:max-content;padding:1px 0;font-size:11px}.project-browser-footer.has-root-confirmation{min-height:78px}.project-browser-actions{justify-content:flex-end;min-width:0}.selected-path{overflow:hidden;color:var(--text-muted);font-family:var(--mono);text-overflow:ellipsis;white-space:nowrap;max-width:310px;font-size:10px}.rail-scrim,.mobile-only{display:none}@media (max-width:980px){.ribbon-model{display:none}.workspace-path{max-width:34vw}.permission-select select{max-width:112px}}@media (max-width:760px){.app-shell{display:block}.mobile-only{display:inline-flex}.session-rail{position:fixed;z-index:70;visibility:hidden;width:min(86vw,310px);transition:transform .19s cubic-bezier(.16,1,.3,1),visibility 0s linear .19s;inset:0 auto 0 0;transform:translate(-102%)}.session-rail.open{visibility:visible;transition-delay:0s;transform:translate(0)}.rail-scrim{position:fixed;z-index:60;display:block;background:#05080cb3;width:100%;height:100%;inset:0}.workspace-main{width:100%;height:100dvh}.workspace-ribbon{gap:5px;padding-inline-start:8px;padding-inline-end:8px}.ribbon-menu{color:var(--accent-text);flex:none}.ribbon-menu,.rail-header .icon-button,.send-button{width:44px;min-width:44px;height:44px;min-height:44px}.ribbon-select{min-height:44px}.workspace-path{flex:1;min-width:0;max-width:none}.workspace-path-copy small,.ribbon-status,.ribbon-spacer{display:none}.conversation-column{padding-inline-start:16px;padding-inline-end:16px}.conversation-scroll>.conversation-column{padding-top:14px;padding-bottom:18px}.chat-message{grid-template-columns:1fr;gap:10px;padding-block-start:19px;padding-block-end:22px}.chat-message>header{height:auto}.agent-working{padding-left:0}.composer-hint span:last-child{display:none}.approval-row{grid-template-columns:auto minmax(0,1fr)}.approval-actions{grid-column:2;justify-content:flex-start}.new-session-surface{align-items: start;padding:52px 16px 24px}.new-session-inner{transform:none}.new-session-title h1{font-size:20px}.starter-suggestions{display:grid}.project-browser-backdrop{padding:0}.project-browser{border:0;border-radius:0;width:100%;height:100dvh}.project-browser-footer{flex-direction:column;align-items:stretch;gap:8px}.project-browser-actions{justify-content:space-between}.selected-path{max-width:50vw}}@media (max-width:460px){.workspace-ribbon{min-height:52px}.ribbon-select{height:44px;padding-inline-start:5px;padding-inline-end:5px}.ribbon-select>svg:first-of-type{display:none}.conversation-column{padding-inline-start:11px;padding-inline-end:11px}.composer-hint{display:none}.conversation-controls{padding-bottom:7px}.approval-actions,.prompt-options{flex-direction:column;align-items:stretch}.primary-button,.secondary-button{width:100%}.project-browser-header,.location-strip{padding-inline-start:14px;padding-inline-end:14px}}@media (prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}
|