History

Add workspace file browser and preview panel (#3087)

Julius Marminge committed 5 months agov0.0.28-nightly.20260615.565

Commit de8bdc11 parent 477795662 files · +2850 −9551Browse files

Unified · Split

62 files
  1. apps/server/src/server.test.ts +36 −0
  2. apps/server/src/workspace/Layers/WorkspaceEntries.test.ts +29 −0
  3. apps/server/src/workspace/Layers/WorkspaceEntries.ts +15 −0
  4. apps/server/src/workspace/Layers/WorkspaceFileSystem.test.ts +58 −0
  5. apps/server/src/workspace/Layers/WorkspaceFileSystem.ts +66 −1
  6. apps/server/src/workspace/Services/WorkspaceEntries.ts +9 −0
  7. apps/server/src/workspace/Services/WorkspaceFileSystem.ts +21 −2
  8. apps/server/src/ws.ts +31 −0
  9. apps/web/THIRD_PARTY_NOTICES.md +11 −0
  10. apps/web/package.json +1 −0
  11. apps/web/src/components/ChatMarkdown.browser.tsx +90 −2
  12. apps/web/src/components/ChatMarkdown.tsx +34 −26
  13. apps/web/src/components/ChatView.browser.tsx +368 −13
  14. apps/web/src/components/ChatView.tsx +174 −34
  15. apps/web/src/components/ComposerPromptEditor.tsx +1 −1
  16. apps/web/src/components/DiffWorkerPoolProvider.tsx +1 −0
  17. apps/web/src/components/NoActiveThreadState.tsx +2 −4
  18. apps/web/src/components/RightPanelTabs.tsx +150 −71
  19. apps/web/src/components/chat/ChangedFilesTree.tsx +2 −2
  20. apps/web/src/components/chat/ChatComposer.tsx +1 −1
  21. apps/web/src/components/chat/ChatHeader.tsx +11 −64
  22. apps/web/src/components/chat/ComposerCommandMenu.tsx +2 −2
  23. apps/web/src/components/chat/FileTagChip.tsx +3 −3
  24. apps/web/src/components/chat/MessagesTimeline.browser.tsx +2 −6
  25. apps/web/src/components/chat/PanelLayoutControls.tsx +114 −0
  26. apps/web/src/components/chat/PierreEntryIcon.tsx +95 −0
  27. apps/web/src/components/chat/VscodeEntryIcon.tsx +0 −37
  28. apps/web/src/components/files/FileBrowserPanel.tsx +128 −0
  29. apps/web/src/components/files/FilePreviewPanel.tsx +298 −0
  30. apps/web/src/components/files/fileContentRevision.test.ts +15 −0
  31. apps/web/src/components/files/fileContentRevision.ts +12 −0
  32. apps/web/src/components/files/filePath.test.ts +23 −0
  33. apps/web/src/components/files/filePath.ts +17 −0
  34. apps/web/src/components/files/fileSaveCoordinator.test.ts +87 −0
  35. apps/web/src/components/files/fileSaveCoordinator.ts +75 −0
  36. apps/web/src/components/files/projectFilesQueryState.test.ts +129 −0
  37. apps/web/src/components/files/projectFilesQueryState.ts +206 −0
  38. apps/web/src/components/preview/PreviewPanelShell.tsx +13 −4
  39. apps/web/src/environmentApi.ts +2 −0
  40. apps/web/src/environments/runtime/service.threadSubscriptions.test.ts +2 −0
  41. apps/web/src/index.css +43 −0
  42. apps/web/src/localApi.test.ts +2 −0
  43. apps/web/src/markdown-links.test.ts +9 −0
  44. apps/web/src/markdown-links.ts +15 −0
  45. apps/web/src/pierre-icons.test.ts +57 −0
  46. apps/web/src/pierre-icons.ts +117 −0
  47. apps/web/src/rightPanelStore.test.ts +48 −0
  48. apps/web/src/rightPanelStore.ts +52 −7
  49. apps/web/src/vscode-icons-language-associations.json +0 −481
  50. apps/web/src/vscode-icons-manifest.json +0 −8351
  51. apps/web/src/vscode-icons.test.ts +0 −52
  52. apps/web/src/vscode-icons.ts +0 −211
  53. apps/web/vite.config.ts +1 −0
  54. package.json +0 −1
  55. packages/client-runtime/src/wsRpcClient.ts +6 −0
  56. packages/contracts/src/ipc.ts +6 −0
  57. packages/contracts/src/project.ts +43 −1
  58. packages/contracts/src/rpc.ts +22 −0
  59. patches/@pierre%2Fdiffs@1.3.0-beta.4.patch +2 −2
  60. pnpm-lock.yaml +89 −17
  61. pnpm-workspace.yaml +4 −2
  62. scripts/sync-vscode-icons.mjs +0 −153
apps/server/src/server.test.ts+36−0
@@ -4357,6 +4357,42 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 4356 unmodified lines
4357 }).pipe(Effect.provide(NodeHttpServer.layerTest)),4357 }).pipe(Effect.provide(NodeHttpServer.layerTest)),
4358 );4358 );
4359 4359
4360 it.effect("routes websocket rpc projects.listEntries and projects.readFile", () =>
4361 Effect.gen(function* () {
4362 const fs = yield* FileSystem.FileSystem;
4363 const path = yield* Path.Path;
4364 const workspaceDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-ws-project-files-" });
4365 yield* fs.makeDirectory(path.join(workspaceDir, "src"), { recursive: true });
4366 yield* fs.writeFileString(
4367 path.join(workspaceDir, "src", "index.ts"),
4368 "export const answer = 42;\n",
4369 );
4370
4371 yield* buildAppUnderTest();
4372
4373 const wsUrl = yield* getWsServerUrl("/ws");
4374 const response = yield* Effect.scoped(
4375 withWsRpcClient(wsUrl, (client) =>
4376 Effect.all({
4377 listing: client[WS_METHODS.projectsListEntries]({ cwd: workspaceDir }),
4378 file: client[WS_METHODS.projectsReadFile]({
4379 cwd: workspaceDir,
4380 relativePath: "src/index.ts",
4381 }),
4382 }),
4383 ),
4384 );
4385
4386 assert.isTrue(response.listing.entries.some((entry) => entry.path === "src/index.ts"));
4387 assert.deepEqual(response.file, {
4388 relativePath: "src/index.ts",
4389 contents: "export const answer = 42;\n",
4390 byteLength: 26,
4391 truncated: false,
4392 });
4393 }).pipe(Effect.provide(NodeHttpServer.layerTest)),
4394 );
4395
4360 it.effect("routes websocket rpc projects.searchEntries excludes gitignored files", () =>4396 it.effect("routes websocket rpc projects.searchEntries excludes gitignored files", () =>
4361 Effect.gen(function* () {4397 Effect.gen(function* () {
4362 const fs = yield* FileSystem.FileSystem;4398 const fs = yield* FileSystem.FileSystem;
apps/server/src/workspace/Layers/WorkspaceEntries.test.ts+29−0
@@ -87,6 +87,35 @@ it.layer(TestLayer)("WorkspaceEntriesLive", (it) => { · 86 unmodified lines
87 vi.restoreAllMocks();87 vi.restoreAllMocks();
88 });88 });
89 89
90 describe("list", () => {
91 it.effect("returns the complete cached workspace index", () =>
92 Effect.gen(function* () {
93 const cwd = yield* makeTempDir();
94 yield* writeTextFile(cwd, "src/components/Composer.tsx");
95 yield* writeTextFile(cwd, "README.md");
96 yield* writeTextFile(cwd, "node_modules/pkg/index.js");
97
98 const workspaceEntries = yield* WorkspaceEntries;
99 const result = yield* workspaceEntries.list({ cwd });
100
101 expect(result.entries).toEqual(
102 expect.arrayContaining([
103 { path: "src", kind: "directory" },
104 { path: "src/components", kind: "directory", parentPath: "src" },
105 {
106 path: "src/components/Composer.tsx",
107 kind: "file",
108 parentPath: "src/components",
109 },
110 { path: "README.md", kind: "file" },
111 ]),
112 );
113 expect(result.entries.some((entry) => entry.path.startsWith("node_modules"))).toBe(false);
114 expect(result.truncated).toBe(false);
115 }),
116 );
117 });
118
90 describe("search", () => {119 describe("search", () => {
91 it.effect("returns files and directories relative to cwd", () =>120 it.effect("returns files and directories relative to cwd", () =>
92 Effect.gen(function* () {121 Effect.gen(function* () {
apps/server/src/workspace/Layers/WorkspaceEntries.ts+15−0
@@ -530,9 +530,24 @@ export const makeWorkspaceEntries = Effect.gen(function* () { · 529 unmodified lines
530 },530 },
531 );531 );
532 532
533 const list: WorkspaceEntriesShape["list"] = Effect.fn("WorkspaceEntries.list")(function* (input) {
534 const normalizedCwd = yield* normalizeWorkspaceRoot(input.cwd);
535 return yield* Cache.get(workspaceIndexCache, normalizedCwd).pipe(
536 Effect.map((index) => ({
537 entries: index.entries.map(({ path: entryPath, kind, parentPath }) => ({
538 path: entryPath,
539 kind,
540 ...(parentPath ? { parentPath } : {}),
541 })),
542 truncated: index.truncated,
543 })),
544 );
545 });
546
533 return {547 return {
534 browse,548 browse,
535 invalidate,549 invalidate,
550 list,
536 search,551 search,
537 } satisfies WorkspaceEntriesShape;552 } satisfies WorkspaceEntriesShape;
538});553});
apps/server/src/workspace/Layers/WorkspaceFileSystem.test.ts+58−0
@@ -54,6 +54,64 @@ const writeTextFile = Effect.fn("writeTextFile")(function* ( · 53 unmodified lines
54});54});
55 55
56it.layer(TestLayer)("WorkspaceFileSystemLive", (it) => {56it.layer(TestLayer)("WorkspaceFileSystemLive", (it) => {
57 describe("readFile", () => {
58 it.effect("reads UTF-8 files relative to the workspace root", () =>
59 Effect.gen(function* () {
60 const workspaceFileSystem = yield* WorkspaceFileSystem;
61 const cwd = yield* makeTempDir;
62 yield* writeTextFile(cwd, "src/index.ts", "export const answer = 42;\n");
63
64 const result = yield* workspaceFileSystem.readFile({
65 cwd,
66 relativePath: "src/index.ts",
67 });
68
69 expect(result).toEqual({
70 relativePath: "src/index.ts",
71 contents: "export const answer = 42;\n",
72 byteLength: 26,
73 truncated: false,
74 });
75 }),
76 );
77
78 it.effect("rejects reads outside the workspace root", () =>
79 Effect.gen(function* () {
80 const workspaceFileSystem = yield* WorkspaceFileSystem;
81 const cwd = yield* makeTempDir;
82
83 const error = yield* workspaceFileSystem
84 .readFile({ cwd, relativePath: "../escape.md" })
85 .pipe(Effect.flip);
86
87 expect(error.message).toContain(
88 "Workspace file path must be relative to the project root: ../escape.md",
89 );
90 }),
91 );
92
93 it.effect("rejects symlinks that resolve outside the workspace root", () =>
94 Effect.gen(function* () {
95 const workspaceFileSystem = yield* WorkspaceFileSystem;
96 const fileSystem = yield* FileSystem.FileSystem;
97 const path = yield* Path.Path;
98 const cwd = yield* makeTempDir;
99 const outsideDir = yield* makeTempDir;
100 yield* writeTextFile(outsideDir, "secret.txt", "outside\n");
101 yield* fileSystem.symlink(
102 path.join(outsideDir, "secret.txt"),
103 path.join(cwd, "linked-secret.txt"),
104 );
105
106 const error = yield* workspaceFileSystem
107 .readFile({ cwd, relativePath: "linked-secret.txt" })
108 .pipe(Effect.flip);
109
110 expect(error.message).toContain("resolves outside the project root");
111 }),
112 );
113 });
114
57 describe("writeFile", () => {115 describe("writeFile", () => {
58 it.effect("writes files relative to the workspace root", () =>116 it.effect("writes files relative to the workspace root", () =>
59 Effect.gen(function* () {117 Effect.gen(function* () {
apps/server/src/workspace/Layers/WorkspaceFileSystem.ts+66−1
@@ -1,3 +1,6 @@
1// @effect-diagnostics nodeBuiltinImport:off
2import fsPromises from "node:fs/promises";
3
1import * as Effect from "effect/Effect";4import * as Effect from "effect/Effect";
2import * as FileSystem from "effect/FileSystem";5import * as FileSystem from "effect/FileSystem";
3import * as Layer from "effect/Layer";6import * as Layer from "effect/Layer";
@@ -11,12 +14,74 @@ import { · 7 unmodified lines
11import { WorkspaceEntries } from "../Services/WorkspaceEntries.ts";14import { WorkspaceEntries } from "../Services/WorkspaceEntries.ts";
12import { WorkspacePaths } from "../Services/WorkspacePaths.ts";15import { WorkspacePaths } from "../Services/WorkspacePaths.ts";
13 16
17const PROJECT_READ_FILE_MAX_BYTES = 1024 * 1024;
18
14export const makeWorkspaceFileSystem = Effect.gen(function* () {19export const makeWorkspaceFileSystem = Effect.gen(function* () {
15 const fileSystem = yield* FileSystem.FileSystem;20 const fileSystem = yield* FileSystem.FileSystem;
16 const path = yield* Path.Path;21 const path = yield* Path.Path;
17 const workspacePaths = yield* WorkspacePaths;22 const workspacePaths = yield* WorkspacePaths;
18 const workspaceEntries = yield* WorkspaceEntries;23 const workspaceEntries = yield* WorkspaceEntries;
19 24
25 const readFile: WorkspaceFileSystemShape["readFile"] = Effect.fn("WorkspaceFileSystem.readFile")(
26 function* (input) {
27 const target = yield* workspacePaths.resolveRelativePathWithinRoot({
28 workspaceRoot: input.cwd,
29 relativePath: input.relativePath,
30 });
31
32 const result = yield* Effect.tryPromise({
33 try: async () => {
34 const [realWorkspaceRoot, realTargetPath] = await Promise.all([
35 fsPromises.realpath(input.cwd),
36 fsPromises.realpath(target.absolutePath),
37 ]);
38 const relativeRealPath = path.relative(realWorkspaceRoot, realTargetPath);
39 if (
40 relativeRealPath.startsWith(`..${path.sep}`) ||
41 relativeRealPath === ".." ||
42 path.isAbsolute(relativeRealPath)
43 ) {
44 throw new Error("Workspace file path resolves outside the project root.");
45 }
46
47 const handle = await fsPromises.open(realTargetPath, "r");
48 try {
49 const stat = await handle.stat();
50 if (!stat.isFile()) {
51 throw new Error("Workspace path is not a file.");
52 }
53 const bytesToRead = Math.min(stat.size, PROJECT_READ_FILE_MAX_BYTES);
54 const buffer = Buffer.alloc(bytesToRead);
55 const { bytesRead } = await handle.read(buffer, 0, bytesToRead, 0);
56 const fileBytes = buffer.subarray(0, bytesRead);
57 if (fileBytes.includes(0)) {
58 throw new Error("Binary files cannot be previewed as text.");
59 }
60 const contents = new TextDecoder("utf-8").decode(fileBytes);
61 return {
62 relativePath: target.relativePath,
63 contents,
64 byteLength: stat.size,
65 truncated: stat.size > PROJECT_READ_FILE_MAX_BYTES,
66 };
67 } finally {
68 await handle.close();
69 }
70 },
71 catch: (cause) =>
72 new WorkspaceFileSystemError({
73 cwd: input.cwd,
74 relativePath: input.relativePath,
75 operation: "workspaceFileSystem.readFile",
76 detail: cause instanceof Error ? cause.message : String(cause),
77 cause,
78 }),
79 });
80
81 return result;
82 },
83 );
84
20 const writeFile: WorkspaceFileSystemShape["writeFile"] = Effect.fn(85 const writeFile: WorkspaceFileSystemShape["writeFile"] = Effect.fn(
21 "WorkspaceFileSystem.writeFile",86 "WorkspaceFileSystem.writeFile",
22 )(function* (input) {87 )(function* (input) {
@@ -52,7 +117,7 @@ export const makeWorkspaceFileSystem = Effect.gen(function* () { · 29 unmodified lines
52 yield* workspaceEntries.invalidate(input.cwd);117 yield* workspaceEntries.invalidate(input.cwd);
53 return { relativePath: target.relativePath };118 return { relativePath: target.relativePath };
54 });119 });
55 return { writeFile } satisfies WorkspaceFileSystemShape;120 return { readFile, writeFile } satisfies WorkspaceFileSystemShape;
56});121});
57 122
58export const WorkspaceFileSystemLive = Layer.effect(WorkspaceFileSystem, makeWorkspaceFileSystem);123export const WorkspaceFileSystemLive = Layer.effect(WorkspaceFileSystem, makeWorkspaceFileSystem);
apps/server/src/workspace/Services/WorkspaceEntries.ts+9−0
@@ -13,6 +13,8 @@ import type * as Effect from "effect/Effect"; · 12 unmodified lines
13import type {13import type {
14 FilesystemBrowseInput,14 FilesystemBrowseInput,
15 FilesystemBrowseResult,15 FilesystemBrowseResult,
16 ProjectListEntriesInput,
17 ProjectListEntriesResult,
16 ProjectSearchEntriesInput,18 ProjectSearchEntriesInput,
17 ProjectSearchEntriesResult,19 ProjectSearchEntriesResult,
18} from "@t3tools/contracts";20} from "@t3tools/contracts";
@@ -50,6 +52,13 @@ export interface WorkspaceEntriesShape { · 31 unmodified lines
50 input: FilesystemBrowseInput,52 input: FilesystemBrowseInput,
51 ) => Effect.Effect<FilesystemBrowseResult, WorkspaceEntriesBrowseError>;53 ) => Effect.Effect<FilesystemBrowseResult, WorkspaceEntriesBrowseError>;
52 54
55 /**
56 * List the cached workspace index for a project root.
57 */
58 readonly list: (
59 input: ProjectListEntriesInput,
60 ) => Effect.Effect<ProjectListEntriesResult, WorkspaceEntriesError>;
61
53 /**62 /**
54 * Search indexed workspace entries for files and directories matching the63 * Search indexed workspace entries for files and directories matching the
55 * provided query.64 * provided query.
apps/server/src/workspace/Services/WorkspaceFileSystem.ts+21−2
@@ -10,7 +10,12 @@ import * as Schema from "effect/Schema"; · 9 unmodified lines
10import * as Context from "effect/Context";10import * as Context from "effect/Context";
11import type * as Effect from "effect/Effect";11import type * as Effect from "effect/Effect";
12 12
13import type { ProjectWriteFileInput, ProjectWriteFileResult } from "@t3tools/contracts";13import type {
14 ProjectReadFileInput,
15 ProjectReadFileResult,
16 ProjectWriteFileInput,
17 ProjectWriteFileResult,
18} from "@t3tools/contracts";
14import { WorkspacePathOutsideRootError } from "./WorkspacePaths.ts";19import { WorkspacePathOutsideRootError } from "./WorkspacePaths.ts";
15 20
16export class WorkspaceFileSystemError extends Schema.TaggedErrorClass<WorkspaceFileSystemError>()(21export class WorkspaceFileSystemError extends Schema.TaggedErrorClass<WorkspaceFileSystemError>()(
@@ -22,12 +27,26 @@ export class WorkspaceFileSystemError extends Schema.TaggedErrorClass<WorkspaceF · 5 unmodified lines
22 detail: Schema.String,27 detail: Schema.String,
23 cause: Schema.optional(Schema.Defect()),28 cause: Schema.optional(Schema.Defect()),
24 },29 },
25) {}30) {
31 override get message(): string {
32 return this.detail;
33 }
34}
26 35
27/**36/**
28 * WorkspaceFileSystemShape - Service API for workspace-relative file operations.37 * WorkspaceFileSystemShape - Service API for workspace-relative file operations.
29 */38 */
30export interface WorkspaceFileSystemShape {39export interface WorkspaceFileSystemShape {
40 /**
41 * Read a UTF-8 text file relative to the workspace root.
42 */
43 readonly readFile: (
44 input: ProjectReadFileInput,
45 ) => Effect.Effect<
46 ProjectReadFileResult,
47 WorkspaceFileSystemError | WorkspacePathOutsideRootError
48 >;
49
31 /**50 /**
32 * Write a file relative to the workspace root.51 * Write a file relative to the workspace root.
33 *52 *
apps/server/src/ws.ts+31−0
@@ -34,6 +34,8 @@ import { · 33 unmodified lines
34 OrchestrationGetSnapshotError,34 OrchestrationGetSnapshotError,
35 OrchestrationGetTurnDiffError,35 OrchestrationGetTurnDiffError,
36 ORCHESTRATION_WS_METHODS,36 ORCHESTRATION_WS_METHODS,
37 ProjectListEntriesError,
38 ProjectReadFileError,
37 ProjectSearchEntriesError,39 ProjectSearchEntriesError,
38 ProjectWriteFileError,40 ProjectWriteFileError,
39 RelayClientInstallFailedError,41 RelayClientInstallFailedError,
@@ -160,6 +162,8 @@ const RPC_REQUIRED_SCOPE = new Map<string, AuthEnvironmentScope>([ · 120 unmodified lines
160 [WS_METHODS.sourceControlLookupRepository, AuthOrchestrationReadScope],162 [WS_METHODS.sourceControlLookupRepository, AuthOrchestrationReadScope],
161 [WS_METHODS.sourceControlCloneRepository, AuthOrchestrationOperateScope],163 [WS_METHODS.sourceControlCloneRepository, AuthOrchestrationOperateScope],
162 [WS_METHODS.sourceControlPublishRepository, AuthOrchestrationOperateScope],164 [WS_METHODS.sourceControlPublishRepository, AuthOrchestrationOperateScope],
165 [WS_METHODS.projectsListEntries, AuthOrchestrationReadScope],
166 [WS_METHODS.projectsReadFile, AuthOrchestrationReadScope],
163 [WS_METHODS.projectsSearchEntries, AuthOrchestrationReadScope],167 [WS_METHODS.projectsSearchEntries, AuthOrchestrationReadScope],
164 [WS_METHODS.projectsWriteFile, AuthOrchestrationOperateScope],168 [WS_METHODS.projectsWriteFile, AuthOrchestrationOperateScope],
165 [WS_METHODS.shellOpenInEditor, AuthOrchestrationOperateScope],169 [WS_METHODS.shellOpenInEditor, AuthOrchestrationOperateScope],
@@ -1172,6 +1176,33 @@ const makeWsRpcLayer = (currentSession: AuthenticatedSession) => · 1006 unmodified lines
1172 ),1176 ),
1173 { "rpc.aggregate": "workspace" },1177 { "rpc.aggregate": "workspace" },
1174 ),1178 ),
1179 [WS_METHODS.projectsListEntries]: (input) =>
1180 observeRpcEffect(
1181 WS_METHODS.projectsListEntries,
1182 workspaceEntries.list(input).pipe(
1183 Effect.mapError(
1184 (cause) =>
1185 new ProjectListEntriesError({
1186 message: `Failed to list workspace entries: ${cause.detail}`,
1187 cause,
1188 }),
1189 ),
1190 ),
1191 { "rpc.aggregate": "workspace" },
1192 ),
1193 [WS_METHODS.projectsReadFile]: (input) =>
1194 observeRpcEffect(
1195 WS_METHODS.projectsReadFile,
1196 workspaceFileSystem.readFile(input).pipe(
1197 Effect.mapError((cause) => {
1198 const message = isWorkspacePathOutsideRootError(cause)
1199 ? "Workspace file path must stay within the project root."
1200 : `Failed to read workspace file: ${cause.detail}`;
1201 return new ProjectReadFileError({ message, cause });
1202 }),
1203 ),
1204 { "rpc.aggregate": "workspace" },
1205 ),
1175 [WS_METHODS.projectsWriteFile]: (input) =>1206 [WS_METHODS.projectsWriteFile]: (input) =>
1176 observeRpcEffect(1207 observeRpcEffect(
1177 WS_METHODS.projectsWriteFile,1208 WS_METHODS.projectsWriteFile,
apps/web/THIRD_PARTY_NOTICES.md+11−0
@@ -0,0 +1,11 @@
1# Third-Party Notices
2
3## vscode-icons
4
5The custom file icon symbols in `src/pierre-icons.ts` are adapted from the
6[`vscode-icons`](https://github.com/vscode-icons/vscode-icons) project.
7
8Copyright (c) 2016 Roberto Huertas
9
10Licensed under the MIT License. The full license text is available in the
11upstream repository: <https://github.com/vscode-icons/vscode-icons/blob/master/LICENSE>.
apps/web/package.json+1−0
@@ -27,6 +27,7 @@ · 26 unmodified lines
27 "@legendapp/list": "3.0.0-beta.44",27 "@legendapp/list": "3.0.0-beta.44",
28 "@lexical/react": "^0.41.0",28 "@lexical/react": "^0.41.0",
29 "@pierre/diffs": "catalog:",29 "@pierre/diffs": "catalog:",
30 "@pierre/trees": "1.0.0-beta.4",
30 "@t3tools/client-runtime": "workspace:*",31 "@t3tools/client-runtime": "workspace:*",
31 "@t3tools/contracts": "workspace:*",32 "@t3tools/contracts": "workspace:*",
32 "@t3tools/shared": "workspace:*",33 "@t3tools/shared": "workspace:*",
apps/web/src/components/ChatMarkdown.browser.tsx+90−2
@@ -50,6 +50,7 @@ vi.mock("../browser/openFileInPreview", async (importOriginal) => ({ · 49 unmodified lines
50import ChatMarkdown from "./ChatMarkdown";50import ChatMarkdown from "./ChatMarkdown";
51import { serializeTableElementToCsv, serializeTableElementToMarkdown } from "../markdown-clipboard";51import { serializeTableElementToCsv, serializeTableElementToMarkdown } from "../markdown-clipboard";
52import { EnvironmentId, ThreadId } from "@t3tools/contracts";52import { EnvironmentId, ThreadId } from "@t3tools/contracts";
53import { selectThreadRightPanelState, useRightPanelStore } from "../rightPanelStore";
53 54
54const threadRef = {55const threadRef = {
55 environmentId: EnvironmentId.make("environment-test"),56 environmentId: EnvironmentId.make("environment-test"),
@@ -63,6 +64,7 @@ describe("ChatMarkdown", () => { · 7 unmodified lines
63 openUrlInPreviewMock.mockClear();64 openUrlInPreviewMock.mockClear();
64 contextMenuShowMock.mockReset();65 contextMenuShowMock.mockReset();
65 readLocalApiMock.mockClear();66 readLocalApiMock.mockClear();
67 useRightPanelStore.setState({ byThreadKey: {} });
66 localStorage.clear();68 localStorage.clear();
67 document.body.innerHTML = "";69 document.body.innerHTML = "";
68 });70 });
@@ -248,6 +250,92 @@ describe("ChatMarkdown", () => { · 179 unmodified lines
248 }250 }
249 });251 });
250 252
253 it("opens code file links in the right-panel file preview", async () => {
254 const screen = await render(
255 <ChatMarkdown
256 text="[ChatMarkdown.tsx](apps/web/src/components/ChatMarkdown.tsx#L978)"
257 cwd="/repo/project"
258 threadRef={threadRef}
259 />,
260 );
261
262 try {
263 await page.getByRole("link", { name: "ChatMarkdown.tsx · L978" }).click();
264
265 await vi.waitFor(() => {
266 expect(
267 selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, threadRef),
268 ).toMatchObject({
269 isOpen: true,
270 activeSurfaceId: "file:apps/web/src/components/ChatMarkdown.tsx",
271 });
272 expect(openInPreferredEditorMock).not.toHaveBeenCalled();
273 expect(openFileInPreviewMock).not.toHaveBeenCalled();
274 });
275 } finally {
276 await screen.unmount();
277 }
278 });
279
280 it("opens HTML and PDF file links in the integrated browser preview", async () => {
281 const screen = await render(
282 <ChatMarkdown
283 text="[report.html](report.html) [report.pdf](report.pdf)"
284 cwd="/repo/project"
285 threadRef={threadRef}
286 />,
287 );
288
289 try {
290 await page.getByRole("link", { name: "report.html" }).click();
291 await page.getByRole("link", { name: "report.pdf" }).click();
292
293 await vi.waitFor(() => {
294 expect(openFileInPreviewMock).toHaveBeenNthCalledWith(
295 1,
296 threadRef,
297 "/repo/project/report.html",
298 );
299 expect(openFileInPreviewMock).toHaveBeenNthCalledWith(
300 2,
301 threadRef,
302 "/repo/project/report.pdf",
303 );
304 expect(openInPreferredEditorMock).not.toHaveBeenCalled();
305 });
306 } finally {
307 await screen.unmount();
308 }
309 });
310
311 it("keeps opening file links in the editor from the context menu", async () => {
312 contextMenuShowMock.mockResolvedValue("open");
313 const filePath = "/repo/project/src/index.ts";
314 const screen = await render(
315 <ChatMarkdown text="[index.ts](src/index.ts)" cwd="/repo/project" threadRef={threadRef} />,
316 );
317
318 try {
319 page
320 .getByRole("link", { name: "index.ts" })
321 .element()
322 .dispatchEvent(
323 new MouseEvent("contextmenu", {
324 bubbles: true,
325 cancelable: true,
326 clientX: 4,
327 clientY: 8,
328 }),
329 );
330
331 await vi.waitFor(() => {
332 expect(openInPreferredEditorMock).toHaveBeenCalledWith(expect.anything(), filePath);
333 });
334 } finally {
335 await screen.unmount();
336 }
337 });
338
251 it("keeps a favicon with the leading segment of a wrapping URL", async () => {339 it("keeps a favicon with the leading segment of a wrapping URL", async () => {
252 const url = "https://github.com/pingdotgg/t3code/pull/3017/changes";340 const url = "https://github.com/pingdotgg/t3code/pull/3017/changes";
253 const screen = await render(341 const screen = await render(
@@ -429,7 +517,7 @@ describe("ChatMarkdown", () => { · 175 unmodified lines
429 517
430 // Language with a known icon: icon XOR text — never the redundant pair.518 // Language with a known icon: icon XOR text — never the redundant pair.
431 const languageOnly = titles[0]!;519 const languageOnly = titles[0]!;
432 const hasIcon = languageOnly.querySelector("img") != null;520 const hasIcon = languageOnly.querySelector("svg[data-pierre-icon]") != null;
433 const hasText = (languageOnly.textContent ?? "").includes("ts");521 const hasText = (languageOnly.textContent ?? "").includes("ts");
434 expect(hasIcon || hasText).toBe(true);522 expect(hasIcon || hasText).toBe(true);
435 expect(hasIcon && hasText).toBe(false);523 expect(hasIcon && hasText).toBe(false);
@@ -446,7 +534,7 @@ describe("ChatMarkdown", () => { · 10 unmodified lines
446 expect(titles[1]!.textContent).toBe("src/main.ts");534 expect(titles[1]!.textContent).toBe("src/main.ts");
447 535
448 // Unknown language: no icon attempt, text label.536 // Unknown language: no icon attempt, text label.
449 expect(titles[2]!.querySelector("img")).toBeNull();537 expect(titles[2]!.querySelector("svg[data-pierre-icon]")).toBeNull();
450 expect(titles[2]!.textContent).toBe("text");538 expect(titles[2]!.textContent).toBe("text");
451 } finally {539 } finally {
452 await screen.unmount();540 await screen.unmount();
apps/web/src/components/ChatMarkdown.tsx+34−26
@@ -33,12 +33,8 @@ import remarkBreaks from "remark-breaks"; · 32 unmodified lines
33import remarkGfm from "remark-gfm";33import remarkGfm from "remark-gfm";
34import { renderSkillInlineMarkdownChildren } from "./chat/SkillInlineText";34import { renderSkillInlineMarkdownChildren } from "./chat/SkillInlineText";
35import { CHAT_FILE_TAG_CHIP_CLASS_NAME, FileTagChipContent } from "./chat/FileTagChip";35import { CHAT_FILE_TAG_CHIP_CLASS_NAME, FileTagChipContent } from "./chat/FileTagChip";
36import { VscodeEntryIcon } from "./chat/VscodeEntryIcon";36import { PierreEntryIcon } from "./chat/PierreEntryIcon";
37import {37import { hasSpecificPierreIconForFileName, syntheticFileNameForLanguageId } from "../pierre-icons";
38 getVscodeIconUrlForEntry,
39 hasSpecificVscodeIconForFileName,
40 syntheticFileNameForLanguageId,
41} from "../vscode-icons";
42import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";38import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
43import { Button } from "./ui/button";39import { Button } from "./ui/button";
44import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "./ui/collapsible";40import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "./ui/collapsible";
@@ -62,6 +58,7 @@ import { · 17 unmodified lines
62} from "../markdown-links";58} from "../markdown-links";
63import { readLocalApi } from "../localApi";59import { readLocalApi } from "../localApi";
64import { cn } from "../lib/utils";60import { cn } from "../lib/utils";
61import { useRightPanelStore } from "../rightPanelStore";
65import { isPreviewSupportedInRuntime } from "../previewStateStore";62import { isPreviewSupportedInRuntime } from "../previewStateStore";
66import {63import {
67 isBrowserPreviewFile,64 isBrowserPreviewFile,
@@ -443,22 +440,17 @@ function MarkdownCodeBlockTitleContent({ · 375 unmodified lines
443 language: string;440 language: string;
444 theme: "light" | "dark";441 theme: "light" | "dark";
445}) {442}) {
446 const [failedIconUrl, setFailedIconUrl] = useState<string | null>(null);
447
448 if (fenceTitle) {443 if (fenceTitle) {
449 return (444 return (
450 <>445 <>
451 <VscodeEntryIcon pathValue={fenceTitle} kind="file" theme={theme} className="size-3.5" />446 <PierreEntryIcon pathValue={fenceTitle} kind="file" theme={theme} className="size-3.5" />
452 <span className="truncate">{fenceTitle}</span>447 <span className="truncate">{fenceTitle}</span>
453 </>448 </>
454 );449 );
455 }450 }
456 451
457 const fileName = syntheticFileNameForLanguageId(language);452 const fileName = syntheticFileNameForLanguageId(language);
458 const iconUrl = hasSpecificVscodeIconForFileName(fileName, theme)453 if (!hasSpecificPierreIconForFileName(fileName)) {
459 ? getVscodeIconUrlForEntry(fileName, "file", theme)
460 : null;
461 if (!iconUrl || failedIconUrl === iconUrl) {
462 return <span className="truncate">{language}</span>;454 return <span className="truncate">{language}</span>;
463 }455 }
464 return (456 return (
@@ -468,15 +460,7 @@ function MarkdownCodeBlockTitleContent({ · 3 unmodified lines
468 <span className="inline-flex shrink-0 rounded-sm" aria-label={`Language: ${language}`} />460 <span className="inline-flex shrink-0 rounded-sm" aria-label={`Language: ${language}`} />
469 }461 }
470 >462 >
471 <img463 <PierreEntryIcon pathValue={fileName} kind="file" theme={theme} className="size-3.5" />
472 src={iconUrl}
473 alt=""
474 aria-hidden
475 className="size-3.5 shrink-0"
476 loading="lazy"
477 draggable={false}
478 onError={() => setFailedIconUrl(iconUrl)}
479 />
480 </TooltipTrigger>464 </TooltipTrigger>
481 <TooltipPopup side="top">{language}</TooltipPopup>465 <TooltipPopup side="top">{language}</TooltipPopup>
482 </Tooltip>466 </Tooltip>
@@ -674,6 +658,7 @@ interface MarkdownFileLinkProps { · 191 unmodified lines
674 targetPath: string;658 targetPath: string;
675 iconPath: string;659 iconPath: string;
676 displayPath: string;660 displayPath: string;
661 workspaceRelativePath: string | null;
677 label: string;662 label: string;
678 copyMarkdown: string;663 copyMarkdown: string;
679 theme: "light" | "dark";664 theme: "light" | "dark";
@@ -997,13 +982,14 @@ const MarkdownFileLink = memo(function MarkdownFileLink({ · 317 unmodified lines
997 targetPath,982 targetPath,
998 iconPath,983 iconPath,
999 displayPath,984 displayPath,
985 workspaceRelativePath,
1000 label,986 label,
1001 copyMarkdown,987 copyMarkdown,
1002 theme,988 theme,
1003 threadRef,989 threadRef,
1004 className,990 className,
1005}: MarkdownFileLinkProps) {991}: MarkdownFileLinkProps) {
1006 const handleOpen = useCallback(() => {992 const handleOpenInEditor = useCallback(() => {
1007 const api = readLocalApi();993 const api = readLocalApi();
1008 if (!api) {994 if (!api) {
1009 toastManager.add({995 toastManager.add({
@@ -1024,6 +1010,14 @@ const MarkdownFileLink = memo(function MarkdownFileLink({ · 14 unmodified lines
1024 });1010 });
1025 }, [targetPath]);1011 }, [targetPath]);
1026 1012
1013 const handleOpenInFilePreview = useCallback(() => {
1014 if (!threadRef || !workspaceRelativePath) {
1015 handleOpenInEditor();
1016 return;
1017 }
1018 useRightPanelStore.getState().openFile(threadRef, workspaceRelativePath);
1019 }, [handleOpenInEditor, threadRef, workspaceRelativePath]);
1020
1027 const handleOpenInBrowser = useCallback(() => {1021 const handleOpenInBrowser = useCallback(() => {
1028 if (!threadRef) return;1022 if (!threadRef) return;
1029 void openFileInPreview(threadRef, iconPath).catch((error) => {1023 void openFileInPreview(threadRef, iconPath).catch((error) => {
@@ -1092,7 +1086,7 @@ const MarkdownFileLink = memo(function MarkdownFileLink({ · 62 unmodified lines
1092 );1086 );
1093 1087
1094 if (clicked === "open") {1088 if (clicked === "open") {
1095 handleOpen();1089 handleOpenInEditor();
1096 return;1090 return;
1097 }1091 }
1098 if (clicked === "open-in-browser") {1092 if (clicked === "open-in-browser") {
@@ -1107,7 +1101,15 @@ const MarkdownFileLink = memo(function MarkdownFileLink({ · 8 unmodified lines
1107 handleCopy(targetPath, "Full path");1101 handleCopy(targetPath, "Full path");
1108 }1102 }
1109 },1103 },
1110 [displayPath, handleCopy, handleOpen, handleOpenInBrowser, iconPath, targetPath, threadRef],1104 [
1105 displayPath,
1106 handleCopy,
1107 handleOpenInBrowser,
1108 handleOpenInEditor,
1109 iconPath,
1110 targetPath,
1111 threadRef,
1112 ],
1111 );1113 );
1112 1114
1113 return (1115 return (
@@ -1121,7 +1123,11 @@ const MarkdownFileLink = memo(function MarkdownFileLink({ · 7 unmodified lines
1121 onClick={(event) => {1123 onClick={(event) => {
1122 event.preventDefault();1124 event.preventDefault();
1123 event.stopPropagation();1125 event.stopPropagation();
1124 handleOpen();1126 if (threadRef && isPreviewSupportedInRuntime() && isBrowserPreviewFile(iconPath)) {
1127 handleOpenInBrowser();
1128 return;
1129 }
1130 handleOpenInFilePreview();
1125 }}1131 }}
1126 onContextMenu={handleContextMenu}1132 onContextMenu={handleContextMenu}
1127 >1133 >
@@ -1150,6 +1156,7 @@ function areMarkdownFileLinkPropsEqual( · 22 unmodified lines
1150 previous.targetPath === next.targetPath &&1156 previous.targetPath === next.targetPath &&
1151 previous.iconPath === next.iconPath &&1157 previous.iconPath === next.iconPath &&
1152 previous.displayPath === next.displayPath &&1158 previous.displayPath === next.displayPath &&
1159 previous.workspaceRelativePath === next.workspaceRelativePath &&
1153 previous.label === next.label &&1160 previous.label === next.label &&
1154 previous.copyMarkdown === next.copyMarkdown &&1161 previous.copyMarkdown === next.copyMarkdown &&
1155 previous.theme === next.theme &&1162 previous.theme === next.theme &&
@@ -1274,6 +1281,7 @@ function ChatMarkdown({ · 118 unmodified lines
1274 targetPath={fileLinkMeta.targetPath}1281 targetPath={fileLinkMeta.targetPath}
1275 iconPath={fileLinkMeta.filePath}1282 iconPath={fileLinkMeta.filePath}
1276 displayPath={fileLinkMeta.displayPath}1283 displayPath={fileLinkMeta.displayPath}
1284 workspaceRelativePath={fileLinkMeta.workspaceRelativePath}
1277 label={labelParts.join(" · ")}1285 label={labelParts.join(" · ")}
1278 copyMarkdown={`[${fileLinkMeta.basename}](${normalizedHref})`}1286 copyMarkdown={`[${fileLinkMeta.basename}](${normalizedHref})`}
1279 theme={resolvedTheme}1287 theme={resolvedTheme}
apps/web/src/components/ChatView.browser.tsx+368−13
@@ -53,7 +53,7 @@ import { · 52 unmodified lines
53 resetSavedEnvironmentRuntimeStoreForTests,53 resetSavedEnvironmentRuntimeStoreForTests,
54 useSavedEnvironmentRegistryStore,54 useSavedEnvironmentRegistryStore,
55 useSavedEnvironmentRuntimeStore,55 useSavedEnvironmentRuntimeStore,
56} from "../environments/runtime";56} from "../environments/runtime/catalog";
57import {57import {
58 INLINE_TERMINAL_CONTEXT_PLACEHOLDER,58 INLINE_TERMINAL_CONTEXT_PLACEHOLDER,
59 removeInlineTerminalContextPlaceholder,59 removeInlineTerminalContextPlaceholder,
@@ -2110,6 +2110,360 @@ describe("ChatView timeline estimator parity (full app)", () => { · 2050 unmodified lines
2110 }2110 }
2111 });2111 });
2112 2112
2113 it("keeps panel toggles fixed and can maximize the right panel", async () => {
2114 const mounted = await mountChatView({
2115 viewport: WIDE_FOOTER_VIEWPORT,
2116 snapshot: createSnapshotForTargetUser({
2117 targetMessageId: "msg-user-maximize-right-panel" as MessageId,
2118 targetText: "maximize right panel",
2119 }),
2120 });
2121
2122 try {
2123 const terminalToggle = await waitForElement(
2124 () =>
2125 document.querySelector<HTMLButtonElement>('button[aria-label="Toggle terminal drawer"]'),
2126 "Unable to find terminal drawer toggle.",
2127 );
2128 const rightPanelToggle = await waitForElement(
2129 () => document.querySelector<HTMLButtonElement>('button[aria-label="Toggle right panel"]'),
2130 "Unable to find right panel toggle.",
2131 );
2132 const headerActions = await waitForElement(
2133 () => document.querySelector<HTMLElement>("[data-chat-header-actions]"),
2134 "Unable to find chat header actions.",
2135 );
2136 const chatHeader = await waitForElement(
2137 () => document.querySelector<HTMLElement>("[data-chat-header]"),
2138 "Unable to find chat header.",
2139 );
2140 const panelLayoutControls = await waitForElement(
2141 () => document.querySelector<HTMLElement>("[data-panel-layout-controls]"),
2142 "Unable to find panel layout controls.",
2143 );
2144 expect(chatHeader.getBoundingClientRect().height).toBe(52);
2145 expect(panelLayoutControls.getBoundingClientRect().height).toBe(52);
2146 expect(panelLayoutControls.getBoundingClientRect().top).toBe(
2147 chatHeader.getBoundingClientRect().top,
2148 );
2149 const initialTerminalRect = terminalToggle.getBoundingClientRect();
2150 const initialRightPanelRect = rightPanelToggle.getBoundingClientRect();
2151 const initialControlRects = [initialTerminalRect, initialRightPanelRect];
2152 expect(document.querySelector('button[aria-label="Maximize panel"]')).toBeNull();
2153 expect(initialControlRects.every((rect) => rect.width === 28 && rect.height === 28)).toBe(
2154 true,
2155 );
2156 expect(initialControlRects.every((rect) => rect.top === initialControlRects[0]?.top)).toBe(
2157 true,
2158 );
2159 expect(initialRightPanelRect.left - initialTerminalRect.right).toBe(4);
2160 expect(
2161 initialTerminalRect.left -
2162 (headerActions.lastElementChild?.getBoundingClientRect().right ?? Number.NaN),
2163 ).toBe(4);
2164
2165 document.documentElement.classList.add("wco");
2166 expect(panelLayoutControls.getBoundingClientRect().height).toBe(52);
2167 expect(panelLayoutControls.getBoundingClientRect().top).toBe(
2168 chatHeader.getBoundingClientRect().top,
2169 );
2170 document.documentElement.classList.remove("wco");
2171
2172 rightPanelToggle.click();
2173
2174 const maximizeButton = await waitForElement(
2175 () => document.querySelector<HTMLButtonElement>('button[aria-label="Maximize panel"]'),
2176 "Unable to find maximize panel button.",
2177 );
2178 const rightPanelTabbar = await waitForElement(
2179 () => document.querySelector<HTMLElement>("[data-right-panel-tabbar]"),
2180 "Unable to find right panel tab bar.",
2181 );
2182 const maximizeRect = maximizeButton.getBoundingClientRect();
2183 const rightPanelTabbarRect = rightPanelTabbar.getBoundingClientRect();
2184 expect(document.querySelector('button[aria-label="Add panel surface"]')).toBeNull();
2185 expect(rightPanelTabbarRect.height).toBe(52);
2186 expect(rightPanelTabbarRect.top).toBe(chatHeader.getBoundingClientRect().top);
2187 expect(maximizeRect.width).toBe(28);
2188 expect(maximizeRect.height).toBe(28);
2189 expect(maximizeRect.top).toBe(initialTerminalRect.top);
2190 expect(initialTerminalRect.left - maximizeRect.right).toBe(4);
2191 expect(terminalToggle.getBoundingClientRect().left).toBeCloseTo(initialTerminalRect.left, 1);
2192 expect(rightPanelToggle.getBoundingClientRect().left).toBeCloseTo(
2193 initialRightPanelRect.left,
2194 1,
2195 );
2196
2197 useRightPanelStore.getState().openFile(THREAD_REF, "components.json");
2198 const fileTabIcon = await waitForElement(
2199 () =>
2200 document.querySelector<SVGElement>(
2201 '[data-right-panel-tabbar] [data-pierre-icon][data-icon-token="json"]',
2202 ),
2203 "Unable to find the Pierre file icon in the file tab.",
2204 );
2205 expect(fileTabIcon.closest("button")?.textContent).toContain("components.json");
2206
2207 document.documentElement.classList.add("wco");
2208 expect(rightPanelTabbar.getBoundingClientRect().height).toBe(
2209 panelLayoutControls.getBoundingClientRect().height,
2210 );
2211 expect(rightPanelTabbar.getBoundingClientRect().top).toBe(
2212 panelLayoutControls.getBoundingClientRect().top,
2213 );
2214 document.documentElement.classList.remove("wco");
2215
2216 maximizeButton.click();
2217
2218 await vi.waitFor(() => {
2219 const chatColumn = document.querySelector<HTMLElement>(
2220 '[data-chat-column-maximized-away="true"]',
2221 );
2222 const panel = document.querySelector<HTMLElement>(
2223 '[data-preview-panel-mode="inline"][data-preview-panel-maximized="true"]',
2224 );
2225 expect(chatColumn?.getBoundingClientRect().width).toBe(0);
2226 expect(panel?.getBoundingClientRect().width).toBeGreaterThan(1_000);
2227 expect(
2228 document.querySelector<HTMLButtonElement>('button[aria-label="Restore panel size"]'),
2229 ).not.toBeNull();
2230 expect(terminalToggle.getBoundingClientRect().left).toBeCloseTo(
2231 initialTerminalRect.left,
2232 1,
2233 );
2234 expect(rightPanelToggle.getBoundingClientRect().left).toBeCloseTo(
2235 initialRightPanelRect.left,
2236 1,
2237 );
2238 });
2239 } finally {
2240 await mounted.cleanup();
2241 }
2242 });
2243
2244 it("scrolls file tabs and preserves the workspace explorer across file previews", async () => {
2245 const workspaceEntries = [
2246 { path: "src", kind: "directory" as const },
2247 { path: "src/index.ts", kind: "file" as const },
2248 { path: "src/router.ts", kind: "file" as const },
2249 { path: "src/store.ts", kind: "file" as const },
2250 { path: "src/styles.css", kind: "file" as const },
2251 { path: "src/large.ts", kind: "file" as const },
2252 { path: "e2e", kind: "directory" as const },
2253 { path: "e2e/test-results", kind: "directory" as const },
2254 {
2255 path: "e2e/test-results/playwright-integration-results",
2256 kind: "directory" as const,
2257 },
2258 {
2259 path: "e2e/test-results/playwright-integration-results/chromium-desktop-project",
2260 kind: "directory" as const,
2261 },
2262 {
2263 path: "e2e/test-results/playwright-integration-results/chromium-desktop-project/.last-run.json",
2264 kind: "file" as const,
2265 },
2266 { path: "README.md", kind: "file" as const },
2267 { path: "AGENTS.md", kind: "file" as const },
2268 { path: "package.json", kind: "file" as const },
2269 { path: "tsconfig.json", kind: "file" as const },
2270 ];
2271 const mounted = await mountChatView({
2272 viewport: WIDE_FOOTER_VIEWPORT,
2273 snapshot: createSnapshotForTargetUser({
2274 targetMessageId: "msg-user-file-tabs-and-tree-state" as MessageId,
2275 targetText: "keep file tabs readable and preserve tree state",
2276 }),
2277 resolveRpc: (body) => {
2278 if (body._tag === WS_METHODS.projectsListEntries) {
2279 return { entries: workspaceEntries, truncated: false };
2280 }
2281 if (body._tag === WS_METHODS.projectsReadFile) {
2282 const relativePath =
2283 typeof body.relativePath === "string" ? body.relativePath : "file.ts";
2284 const contents =
2285 relativePath === "src/large.ts"
2286 ? Array.from(
2287 { length: 5_000 },
2288 (_, index) => `export const line${index + 1} = ${index + 1};`,
2289 ).join("\n")
2290 : `// ${relativePath}\n`;
2291 return {
2292 relativePath,
2293 contents,
2294 byteLength: new TextEncoder().encode(contents).byteLength,
2295 truncated: false,
2296 };
2297 }
2298 return undefined;
2299 },
2300 });
2301
2302 try {
2303 useRightPanelStore.getState().open(THREAD_REF, "files");
2304
2305 const explorer = await waitForElement(
2306 () => document.querySelector<HTMLElement>("[data-file-browser-panel]"),
2307 "Unable to find the workspace file explorer.",
2308 );
2309
2310 for (const entry of workspaceEntries) {
2311 if (entry.kind === "file") {
2312 useRightPanelStore.getState().openFile(THREAD_REF, entry.path);
2313 }
2314 }
2315
2316 const tabList = await waitForElement(
2317 () => document.querySelector<HTMLElement>("[data-right-panel-tab-list]"),
2318 "Unable to find the right panel tab list.",
2319 );
2320 const tabViewport = await waitForElement(
2321 () => tabList.querySelector<HTMLElement>('[data-slot="scroll-area-viewport"]'),
2322 "Unable to find the right panel tab viewport.",
2323 );
2324
2325 await vi.waitFor(() => {
2326 const fileTabs = Array.from(tabList.querySelectorAll<HTMLElement>("[data-active-tab]"));
2327 expect(fileTabs.length).toBe(
2328 workspaceEntries.filter((entry) => entry.kind === "file").length,
2329 );
2330 expect(tabViewport.scrollWidth).toBeGreaterThan(tabViewport.clientWidth);
2331 expect(tabViewport.scrollLeft).toBeGreaterThan(0);
2332 expect(tabList.querySelector('[data-slot="scroll-area-scrollbar"]')).toBeNull();
2333 expect(
2334 fileTabs.every((tab) => {
2335 const width = tab.getBoundingClientRect().width;
2336 return width >= 100 && width <= 176;
2337 }),
2338 ).toBe(true);
2339 expect(document.querySelector<HTMLElement>("[data-file-browser-panel]")).toBe(explorer);
2340 });
2341
2342 useRightPanelStore.getState().openFile(THREAD_REF, "src/index.ts");
2343 await vi.waitFor(() => {
2344 expect(document.querySelector<HTMLElement>("[data-file-browser-panel]")).toBe(explorer);
2345 });
2346
2347 useRightPanelStore
2348 .getState()
2349 .openFile(
2350 THREAD_REF,
2351 "e2e/test-results/playwright-integration-results/chromium-desktop-project/.last-run.json",
2352 );
2353 await mounted.setContainerSize({ width: 800, height: WIDE_FOOTER_VIEWPORT.height });
2354 const breadcrumbs = await waitForElement(
2355 () => document.querySelector<HTMLElement>("[data-file-breadcrumbs]"),
2356 "Unable to find the responsive file breadcrumbs.",
2357 );
2358 const breadcrumbViewport = await waitForElement(
2359 () => breadcrumbs.querySelector<HTMLElement>('[data-slot="scroll-area-viewport"]'),
2360 "Unable to find the file breadcrumb viewport.",
2361 );
2362 const currentCrumb = await waitForElement(
2363 () =>
2364 Array.from(
2365 breadcrumbs.querySelectorAll<HTMLElement>("[data-current-file-crumb='true']"),
2366 ).find((crumb) => crumb.textContent === ".last-run.json") ?? null,
2367 "Unable to find the current file breadcrumb.",
2368 );
2369 const explorerToggle = await waitForElement(
2370 () => document.querySelector<HTMLButtonElement>('button[aria-label="Hide file explorer"]'),
2371 "Unable to find the file explorer toggle.",
2372 );
2373
2374 await vi.waitFor(() => {
2375 const viewportRect = breadcrumbViewport.getBoundingClientRect();
2376 const currentCrumbRect = currentCrumb.getBoundingClientRect();
2377 expect(breadcrumbViewport.scrollWidth).toBeGreaterThan(breadcrumbViewport.clientWidth);
2378 expect(breadcrumbViewport.scrollLeft).toBeGreaterThan(0);
2379 expect(breadcrumbs.querySelector('[data-slot="scroll-area-scrollbar"]')).toBeNull();
2380 expect(currentCrumbRect.right).toBeLessThanOrEqual(viewportRect.right + 1);
2381 expect(viewportRect.right).toBeLessThan(explorerToggle.getBoundingClientRect().left);
2382 expect(explorerToggle.getAttribute("aria-pressed")).toBe("true");
2383 expect(explorerToggle.getBoundingClientRect().width).toBe(28);
2384 expect(explorerToggle.getBoundingClientRect().height).toBe(28);
2385 });
2386
2387 const fileSearchButton = await waitForElement(
2388 () =>
2389 document.querySelector<HTMLButtonElement>('button[aria-label="Search workspace files"]'),
2390 "Unable to find the workspace file search button.",
2391 );
2392 fileSearchButton.click();
2393 const fileTree = await waitForElement(
2394 () => document.querySelector<HTMLElement>("file-tree-container"),
2395 "Unable to find the file tree host.",
2396 );
2397 const fileSearchInput = await waitForElement(
2398 () =>
2399 fileTree.shadowRoot?.querySelector<HTMLInputElement>("[data-file-tree-search-input]") ??
2400 null,
2401 "Unable to find the file tree search input.",
2402 );
2403 fileSearchInput.focus();
2404 const searchKeyEvent = new KeyboardEvent("keydown", {
2405 key: "r",
2406 bubbles: true,
2407 cancelable: true,
2408 composed: true,
2409 });
2410 fileSearchInput.dispatchEvent(searchKeyEvent);
2411 await waitForLayout();
2412 expect(searchKeyEvent.defaultPrevented).toBe(false);
2413 expect(fileTree.shadowRoot?.activeElement).toBe(fileSearchInput);
2414 expect(useComposerDraftStore.getState().draftsByThreadKey[THREAD_KEY]?.prompt ?? "").toBe("");
2415
2416 useRightPanelStore.getState().openFile(THREAD_REF, "src/large.ts");
2417 const codeVirtualizer = await waitForElement(
2418 () => document.querySelector<HTMLElement>(".file-preview-virtualizer"),
2419 "Unable to find the virtualized file preview.",
2420 );
2421 expect(codeVirtualizer.querySelector("diffs-container")).not.toBeNull();
2422 expect(codeVirtualizer.classList.contains("overflow-auto")).toBe(true);
2423 } finally {
2424 await mounted.cleanup();
2425 }
2426 });
2427
2428 it("removes persisted file tabs when a draft workspace no longer exists", async () => {
2429 const orphanedDraftId = DraftId.make("draft-orphaned-file-panel");
2430 const orphanedThreadId = "thread-orphaned-file-panel" as ThreadId;
2431 const orphanedThreadRef = scopeThreadRef(LOCAL_ENVIRONMENT_ID, orphanedThreadId);
2432 useComposerDraftStore.getState().setProjectDraftThreadId(
2433 {
2434 environmentId: LOCAL_ENVIRONMENT_ID,
2435 projectId: "project-deleted" as ProjectId,
2436 },
2437 orphanedDraftId,
2438 { threadId: orphanedThreadId },
2439 );
2440 useRightPanelStore.getState().openFile(orphanedThreadRef, "conductor.json");
2441
2442 const mounted = await mountChatView({
2443 viewport: WIDE_FOOTER_VIEWPORT,
2444 snapshot: createSnapshotForTargetUser({
2445 targetMessageId: "msg-user-orphaned-file-panel" as MessageId,
2446 targetText: "orphaned persisted file panel",
2447 }),
2448 initialPath: `/draft/${orphanedDraftId}`,
2449 });
2450
2451 try {
2452 await vi.waitFor(() => {
2453 expect(
2454 selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, orphanedThreadRef),
2455 ).toEqual({
2456 isOpen: false,
2457 activeSurfaceId: null,
2458 surfaces: [],
2459 });
2460 expect(document.querySelector("[data-right-panel-tabbar]")).toBeNull();
2461 });
2462 } finally {
2463 await mounted.cleanup();
2464 }
2465 });
2466
2113 it("keeps multiple terminal panel surfaces separate from the bottom drawer", async () => {2467 it("keeps multiple terminal panel surfaces separate from the bottom drawer", async () => {
2114 const mounted = await mountChatView({2468 const mounted = await mountChatView({
2115 viewport: WIDE_FOOTER_VIEWPORT,2469 viewport: WIDE_FOOTER_VIEWPORT,
@@ -2126,11 +2480,10 @@ describe("ChatView timeline estimator parity (full app)", () => { · 10 unmodified lines
2126 );2480 );
2127 rightPanelToggle.click();2481 rightPanelToggle.click();
2128 2482
2129 const addSurface = await waitForElement(2483 await vi.waitFor(() => {
2130 () => document.querySelector<HTMLButtonElement>('button[aria-label="Add panel surface"]'),2484 expect(document.body.textContent).toContain("Open a surface");
2131 "Unable to find add panel surface button.",2485 });
2132 );2486 expect(document.querySelector('button[aria-label="Add panel surface"]')).toBeNull();
2133 expect(document.body.textContent).toContain("Open a surface");
2134 expect(2487 expect(
2135 selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, THREAD_REF),2488 selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, THREAD_REF),
2136 ).toEqual({2489 ).toEqual({
@@ -2140,16 +2493,14 @@ describe("ChatView timeline estimator parity (full app)", () => { · 3 unmodified lines
2140 });2493 });
2141 expect(wsRequests.some((request) => request._tag === WS_METHODS.terminalOpen)).toBe(false);2494 expect(wsRequests.some((request) => request._tag === WS_METHODS.terminalOpen)).toBe(false);
2142 2495
2143 addSurface.click();2496 const emptyStateTerminalButton = await waitForElement(
2144
2145 const terminalItem = await waitForElement(
2146 () =>2497 () =>
2147 Array.from(document.querySelectorAll<HTMLElement>('[role="menuitem"]')).find(2498 Array.from(document.querySelectorAll<HTMLButtonElement>("button")).find((button) =>
2148 (item) => item.textContent?.trim() === "Terminal",2499 button.textContent?.includes("Start a shell in this workspace."),
2149 ) ?? null,2500 ) ?? null,
2150 "Unable to find Terminal panel menu item.",2501 "Unable to find the empty-state Terminal button.",
2151 );2502 );
2152 terminalItem.click();2503 emptyStateTerminalButton.click();
2153 2504
2154 await vi.waitFor(() => {2505 await vi.waitFor(() => {
2155 expect(2506 expect(
@@ -2159,6 +2510,10 @@ describe("ChatView timeline estimator parity (full app)", () => { · 3 unmodified lines
2159 ).toEqual(["term-1"]);2510 ).toEqual(["term-1"]);
2160 });2511 });
2161 2512
2513 const addSurface = await waitForElement(
2514 () => document.querySelector<HTMLButtonElement>('button[aria-label="Add panel surface"]'),
2515 "Unable to find add panel surface button beside the tabs.",
2516 );
2162 addSurface.click();2517 addSurface.click();
2163 const secondTerminalItem = await waitForElement(2518 const secondTerminalItem = await waitForElement(
2164 () =>2519 () =>
apps/web/src/components/ChatView.tsx+174−34
@@ -35,7 +35,7 @@ import { lazy, memo, Suspense, useCallback, useEffect, useMemo, useRef, useState · 34 unmodified lines
35import { useNavigate, useSearch } from "@tanstack/react-router";35import { useNavigate, useSearch } from "@tanstack/react-router";
36import { useShallow } from "zustand/react/shallow";36import { useShallow } from "zustand/react/shallow";
37import { useVcsStatus } from "~/lib/vcsStatusState";37import { useVcsStatus } from "~/lib/vcsStatusState";
38import { usePrimaryEnvironmentId } from "../environments/primary";38import { usePrimaryEnvironmentId } from "../environments/primary/context";
39import { readEnvironmentApi } from "../environmentApi";39import { readEnvironmentApi } from "../environmentApi";
40import { resolveAssetUrl } from "../assets/assetUrls";40import { resolveAssetUrl } from "../assets/assetUrls";
41import { isElectron } from "../env";41import { isElectron } from "../env";
@@ -67,7 +67,7 @@ import { · 25 unmodified lines
67 togglePendingUserInputOptionSelection,67 togglePendingUserInputOptionSelection,
68 type PendingUserInputDraftAnswer,68 type PendingUserInputDraftAnswer,
69} from "../pendingUserInput";69} from "../pendingUserInput";
70import { selectProjectsAcrossEnvironments, useStore } from "../store";70import { selectEnvironmentState, selectProjectsAcrossEnvironments, useStore } from "../store";
71import { createProjectSelectorByRef, createThreadSelectorByRef } from "../storeSelectors";71import { createProjectSelectorByRef, createThreadSelectorByRef } from "../storeSelectors";
72import { useUiStateStore } from "../uiStateStore";72import { useUiStateStore } from "../uiStateStore";
73import {73import {
@@ -111,6 +111,8 @@ const PreviewPanel = lazy(() => · 37 unmodified lines
111 import("./preview/PreviewPanel").then((mod) => ({ default: mod.PreviewPanel })),111 import("./preview/PreviewPanel").then((mod) => ({ default: mod.PreviewPanel })),
112);112);
113const DiffPanel = lazy(() => import("./DiffPanel"));113const DiffPanel = lazy(() => import("./DiffPanel"));
114const FilePreviewPanel = lazy(() => import("./files/FilePreviewPanel"));
115const EMPTY_PENDING_FILE_SURFACE_IDS: ReadonlySet<string> = new Set();
114import { BranchToolbar } from "./BranchToolbar";116import { BranchToolbar } from "./BranchToolbar";
115import { resolveShortcutCommand, shortcutLabelForCommand } from "../keybindings";117import { resolveShortcutCommand, shortcutLabelForCommand } from "../keybindings";
116import PlanSidebar from "./PlanSidebar";118import PlanSidebar from "./PlanSidebar";
@@ -137,10 +139,10 @@ import { · 20 unmodified lines
137 selectProjectGroupingSettings,139 selectProjectGroupingSettings,
138} from "../logicalProject";140} from "../logicalProject";
139import {141import {
140 reconnectSavedEnvironment,
141 useSavedEnvironmentRegistryStore,142 useSavedEnvironmentRegistryStore,
142 useSavedEnvironmentRuntimeStore,143 useSavedEnvironmentRuntimeStore,
143} from "../environments/runtime";144} from "../environments/runtime/catalog";
145import { reconnectSavedEnvironment } from "../environments/runtime/service";
144import { buildDraftThreadRouteParams } from "../threadRoutes";146import { buildDraftThreadRouteParams } from "../threadRoutes";
145import {147import {
146 type ComposerImageAttachment,148 type ComposerImageAttachment,
@@ -167,6 +169,7 @@ import { ExpandedImageDialog } from "./chat/ExpandedImageDialog"; · 20 unmodified lines
167import { PullRequestThreadDialog } from "./PullRequestThreadDialog";169import { PullRequestThreadDialog } from "./PullRequestThreadDialog";
168import { MessagesTimeline } from "./chat/MessagesTimeline";170import { MessagesTimeline } from "./chat/MessagesTimeline";
169import { ChatHeader } from "./chat/ChatHeader";171import { ChatHeader } from "./chat/ChatHeader";
172import { PanelLayoutControls } from "./chat/PanelLayoutControls";
170import { type ExpandedImagePreview } from "./chat/ExpandedImagePreview";173import { type ExpandedImagePreview } from "./chat/ExpandedImagePreview";
171import { NoActiveThreadState } from "./NoActiveThreadState";174import { NoActiveThreadState } from "./NoActiveThreadState";
172import { resolveEffectiveEnvMode, resolveEnvironmentOptionLabel } from "./BranchToolbar.logic";175import { resolveEffectiveEnvMode, resolveEnvironmentOptionLabel } from "./BranchToolbar.logic";
@@ -256,10 +259,12 @@ type EnvironmentUnavailableState = { · 83 unmodified lines
256 259
257type ThreadPlanCatalogEntry = Pick<Thread, "id" | "proposedPlans">;260type ThreadPlanCatalogEntry = Pick<Thread, "id" | "proposedPlans">;
258 261
259function eventTargetElement(target: EventTarget | null): Element | null {262function eventPathContainsSelector(event: Event, selector: string): boolean {
260 if (target instanceof Element) return target;263 const path = event.composedPath();
261 if (target instanceof Node) return target.parentElement;264 if (path.length === 0 && event.target) {
262 return null;265 path.push(event.target);
266 }
267 return path.some((target) => target instanceof Element && target.closest(selector));
263}268}
264 269
265function shouldTypeToFocusComposer(event: KeyboardEvent): boolean {270function shouldTypeToFocusComposer(event: KeyboardEvent): boolean {
@@ -267,9 +272,8 @@ function shouldTypeToFocusComposer(event: KeyboardEvent): boolean { · 1 unmodified line
267 if (event.metaKey || event.ctrlKey || event.altKey) return false;272 if (event.metaKey || event.ctrlKey || event.altKey) return false;
268 if (event.key.length !== 1) return false;273 if (event.key.length !== 1) return false;
269 274
270 const target = eventTargetElement(event.target);275 if (eventPathContainsSelector(event, TYPE_TO_FOCUS_EDITABLE_SELECTOR)) return false;
271 if (target?.closest(TYPE_TO_FOCUS_EDITABLE_SELECTOR)) return false;276 if (eventPathContainsSelector(event, TYPE_TO_FOCUS_INTERACTIVE_SELECTOR)) return false;
272 if (target?.closest(TYPE_TO_FOCUS_INTERACTIVE_SELECTOR)) return false;
273 if (document.querySelector(TYPE_TO_FOCUS_FLOATING_LAYER_SELECTOR)) return false;277 if (document.querySelector(TYPE_TO_FOCUS_FLOATING_LAYER_SELECTOR)) return false;
274 278
275 return true;279 return true;
@@ -1055,7 +1059,7 @@ const PersistentThreadTerminalPanel = memo(function PersistentThreadTerminalPane · 779 unmodified lines
1055 );1059 );
1056});1060});
1057 1061
1058export default function ChatView(props: ChatViewProps) {1062function ChatViewContent(props: ChatViewProps) {
1059 const {1063 const {
1060 environmentId,1064 environmentId,
1061 threadId,1065 threadId,
@@ -1152,6 +1156,9 @@ export default function ChatView(props: ChatViewProps) { · 90 unmodified lines
1152 >({});1156 >({});
1153 const [isConnecting, _setIsConnecting] = useState(false);1157 const [isConnecting, _setIsConnecting] = useState(false);
1154 const [isRevertingCheckpoint, setIsRevertingCheckpoint] = useState(false);1158 const [isRevertingCheckpoint, setIsRevertingCheckpoint] = useState(false);
1159 const [maximizedRightPanelThreadKey, setMaximizedRightPanelThreadKey] = useState<string | null>(
1160 null,
1161 );
1155 const [respondingRequestIds, setRespondingRequestIds] = useState<ApprovalRequestId[]>([]);1162 const [respondingRequestIds, setRespondingRequestIds] = useState<ApprovalRequestId[]>([]);
1156 const [respondingUserInputRequestIds, setRespondingUserInputRequestIds] = useState<1163 const [respondingUserInputRequestIds, setRespondingUserInputRequestIds] = useState<
1157 ApprovalRequestId[]1164 ApprovalRequestId[]
@@ -1324,6 +1331,9 @@ export default function ChatView(props: ChatViewProps) { · 166 unmodified lines
1324 const planSidebarOpen = activeRightPanelKind === "plan";1331 const planSidebarOpen = activeRightPanelKind === "plan";
1325 const previewPanelOpen = activeRightPanelKind === "preview" && isPreviewSupportedInRuntime();1332 const previewPanelOpen = activeRightPanelKind === "preview" && isPreviewSupportedInRuntime();
1326 const rightPanelOpen = rightPanelState.isOpen;1333 const rightPanelOpen = rightPanelState.isOpen;
1334 const canMaximizeRightPanel = rightPanelOpen && !shouldUsePlanSidebarSheet;
1335 const rightPanelMaximized =
1336 canMaximizeRightPanel && maximizedRightPanelThreadKey === routeThreadKey;
1327 const inlineRightPanelOwnsTitleBar = rightPanelOpen && !shouldUsePlanSidebarSheet;1337 const inlineRightPanelOwnsTitleBar = rightPanelOpen && !shouldUsePlanSidebarSheet;
1328 1338
1329 useEffect(() => {1339 useEffect(() => {
@@ -1358,11 +1368,58 @@ export default function ChatView(props: ChatViewProps) { · 28 unmodified lines
1358 const activeProject = useStore(1368 const activeProject = useStore(
1359 useMemo(() => createProjectSelectorByRef(activeProjectRef), [activeProjectRef]),1369 useMemo(() => createProjectSelectorByRef(activeProjectRef), [activeProjectRef]),
1360 );1370 );
1371 const activeProjectKey = activeProject
1372 ? `${activeProject.environmentId}:${activeProject.cwd}`
1373 : null;
1374 const [pendingFileSurfaceIdsByProject, setPendingFileSurfaceIdsByProject] = useState<
1375 ReadonlyMap<string, ReadonlySet<string>>
1376 >(() => new Map());
1377 const pendingFileSurfaceIds = activeProjectKey
1378 ? (pendingFileSurfaceIdsByProject.get(activeProjectKey) ?? EMPTY_PENDING_FILE_SURFACE_IDS)
1379 : EMPTY_PENDING_FILE_SURFACE_IDS;
1380 const handleFilePendingChange = useCallback(
1381 (relativePath: string, pending: boolean) => {
1382 if (!activeProjectKey) return;
1383 setPendingFileSurfaceIdsByProject((currentByProject) => {
1384 const current = currentByProject.get(activeProjectKey) ?? EMPTY_PENDING_FILE_SURFACE_IDS;
1385 const surfaceId = `file:${relativePath}`;
1386 if (current.has(surfaceId) === pending) return currentByProject;
1387
1388 const next = new Set(current);
1389 if (pending) {
1390 next.add(surfaceId);
1391 } else {
1392 next.delete(surfaceId);
1393 }
1394
1395 const nextByProject = new Map(currentByProject);
1396 if (next.size === 0) {
1397 nextByProject.delete(activeProjectKey);
1398 } else {
1399 nextByProject.set(activeProjectKey, next);
1400 }
1401 return nextByProject;
1402 });
1403 },
1404 [activeProjectKey],
1405 );
1406 const activeEnvironmentBootstrapComplete = useStore((state) =>
1407 activeThread
1408 ? selectEnvironmentState(state, activeThread.environmentId).bootstrapComplete
1409 : false,
1410 );
1361 const configuredPreviewUrls = useMemo(1411 const configuredPreviewUrls = useMemo(
1362 () => getConfiguredPreviewUrls(activeProject?.scripts),1412 () => getConfiguredPreviewUrls(activeProject?.scripts),
1363 [activeProject?.scripts],1413 [activeProject?.scripts],
1364 );1414 );
1365 1415
1416 useEffect(() => {
1417 if (!activeThreadRef || !activeEnvironmentBootstrapComplete) return;
1418 useRightPanelStore
1419 .getState()
1420 .reconcileFileSurfaces(activeThreadRef, activeProject !== undefined);
1421 }, [activeEnvironmentBootstrapComplete, activeProject, activeThreadRef]);
1422
1366 useEffect(() => {1423 useEffect(() => {
1367 if (routeKind !== "server") {1424 if (routeKind !== "server") {
1368 return;1425 return;
@@ -2209,6 +2266,8 @@ export default function ChatView(props: ChatViewProps) { · 840 unmodified lines
2209 }),2266 }),
2210 [terminalUiState.terminalOpen],2267 [terminalUiState.terminalOpen],
2211 );2268 );
2269 const terminalToggleShortcutLabel = shortcutLabelForCommand(keybindings, "terminal.toggle");
2270 const rightPanelToggleShortcutLabel = shortcutLabelForCommand(keybindings, "rightPanel.toggle");
2212 const splitTerminalShortcutLabel = useMemo(2271 const splitTerminalShortcutLabel = useMemo(
2213 () => shortcutLabelForCommand(keybindings, "terminal.split", terminalShortcutLabelOptions),2272 () => shortcutLabelForCommand(keybindings, "terminal.split", terminalShortcutLabelOptions),
2214 [keybindings, terminalShortcutLabelOptions],2273 [keybindings, terminalShortcutLabelOptions],
@@ -2257,6 +2316,14 @@ export default function ChatView(props: ChatViewProps) { · 42 unmodified lines
2257 onDiffPanelOpen,2316 onDiffPanelOpen,
2258 threadId,2317 threadId,
2259 ]);2318 ]);
2319 const addFilesSurface = () => {
2320 if (!activeThreadRef || !activeProject) return;
2321 useRightPanelStore.getState().open(activeThreadRef, "files");
2322 };
2323 const openFileSurface = (relativePath: string) => {
2324 if (!activeThreadRef || !activeProject) return;
2325 useRightPanelStore.getState().openFile(activeThreadRef, relativePath);
2326 };
2260 // Right-panel arbitration:2327 // Right-panel arbitration:
2261 // - The diff panel's openness is mirrored by the `?diff=1` URL search2328 // - The diff panel's openness is mirrored by the `?diff=1` URL search
2262 // param so it deep-links cleanly. The store still records preview/plan2329 // param so it deep-links cleanly. The store still records preview/plan
@@ -2979,12 +3046,14 @@ export default function ChatView(props: ChatViewProps) { · 716 unmodified lines
2979 }, [activePlan?.turnId, activeThreadRef, planSidebarOpen, sidebarProposedPlan?.turnId]);3046 }, [activePlan?.turnId, activeThreadRef, planSidebarOpen, sidebarProposedPlan?.turnId]);
2980 const closePlanSidebar = useCallback(() => {3047 const closePlanSidebar = useCallback(() => {
2981 if (!activeThreadRef) return;3048 if (!activeThreadRef) return;
3049 setMaximizedRightPanelThreadKey(null);
2982 useRightPanelStore.getState().close(activeThreadRef);3050 useRightPanelStore.getState().close(activeThreadRef);
2983 planSidebarDismissedForTurnRef.current =3051 planSidebarDismissedForTurnRef.current =
2984 activePlan?.turnId ?? sidebarProposedPlan?.turnId ?? "__dismissed__";3052 activePlan?.turnId ?? sidebarProposedPlan?.turnId ?? "__dismissed__";
2985 }, [activePlan?.turnId, activeThreadRef, sidebarProposedPlan?.turnId]);3053 }, [activePlan?.turnId, activeThreadRef, sidebarProposedPlan?.turnId]);
2986 const closePreviewPanel = useCallback(() => {3054 const closePreviewPanel = useCallback(() => {
2987 if (!activeThreadRef) return;3055 if (!activeThreadRef) return;
3056 setMaximizedRightPanelThreadKey(null);
2988 useRightPanelStore.getState().close(activeThreadRef);3057 useRightPanelStore.getState().close(activeThreadRef);
2989 }, [activeThreadRef]);3058 }, [activeThreadRef]);
2990 const activateRightPanelSurface = useCallback(3059 const activateRightPanelSurface = useCallback(
@@ -3018,8 +3087,17 @@ export default function ChatView(props: ChatViewProps) { · 27 unmodified lines
3018 );3087 );
3019 const toggleRightPanel = useCallback(() => {3088 const toggleRightPanel = useCallback(() => {
3020 if (!activeThreadRef) return;3089 if (!activeThreadRef) return;
3090 if (rightPanelOpen) {
3091 setMaximizedRightPanelThreadKey(null);
3092 }
3021 useRightPanelStore.getState().toggleVisibility(activeThreadRef);3093 useRightPanelStore.getState().toggleVisibility(activeThreadRef);
3022 }, [activeThreadRef]);3094 }, [activeThreadRef, rightPanelOpen]);
3095 const toggleRightPanelMaximized = () => {
3096 if (!canMaximizeRightPanel) return;
3097 setMaximizedRightPanelThreadKey((threadKey) =>
3098 threadKey === routeThreadKey ? null : routeThreadKey,
3099 );
3100 };
3023 const closeRightPanelSurface = useCallback(3101 const closeRightPanelSurface = useCallback(
3024 (surface: RightPanelSurface) => {3102 (surface: RightPanelSurface) => {
3025 if (!activeThreadRef) return;3103 if (!activeThreadRef) return;
@@ -4481,23 +4559,43 @@ export default function ChatView(props: ChatViewProps) { · 1455 unmodified lines
4481 }4559 }
4482 4560
4483 return (4561 return (
4484 <div className="flex min-h-0 min-w-0 flex-1 overflow-hidden bg-background">4562 <div className="relative flex min-h-0 min-w-0 flex-1 overflow-hidden bg-background">
4485 {isElectron && activeThreadRef ? (4563 {isElectron && activeThreadRef ? (
4486 <PreviewAutomationOwner threadRef={activeThreadRef} visible={previewPanelOpen} />4564 <PreviewAutomationOwner threadRef={activeThreadRef} visible={previewPanelOpen} />
4487 ) : null}4565 ) : null}
4488 <div className="flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden">4566 <PanelLayoutControls
4567 terminalAvailable={Boolean(activeProject)}
4568 terminalOpen={Boolean(terminalUiState.terminalOpen)}
4569 terminalShortcutLabel={terminalToggleShortcutLabel}
4570 rightPanelAvailable={Boolean(activeProject)}
4571 rightPanelOpen={rightPanelOpen}
4572 rightPanelShortcutLabel={rightPanelToggleShortcutLabel}
4573 rightPanelMaximized={rightPanelMaximized}
4574 canMaximizeRightPanel={canMaximizeRightPanel}
4575 onToggleTerminal={toggleTerminalVisibility}
4576 onToggleRightPanel={toggleRightPanel}
4577 onToggleRightPanelMaximized={toggleRightPanelMaximized}
4578 />
4579 <div
4580 className={cn(
4581 "flex min-h-0 min-w-0 flex-col overflow-hidden",
4582 rightPanelMaximized ? "w-0 flex-none" : "flex-1",
4583 )}
4584 data-chat-column-maximized-away={rightPanelMaximized ? "true" : "false"}
4585 >
4489 {/* Top bar */}4586 {/* Top bar */}
4490 <header4587 <header
4588 data-chat-header
4491 className={cn(4589 className={cn(
4492 "border-b border-border",4590 "border-b border-border",
4493 isElectron4591 isElectron
4494 ? cn(4592 ? cn(
4495 "drag-region flex h-[52px] items-center px-3 sm:px-5 wco:h-[env(titlebar-area-height)]",4593 "workspace-topbar drag-region px-3 sm:px-5",
4496 reserveTitleBarControlInset &&4594 reserveTitleBarControlInset &&
4497 !inlineRightPanelOwnsTitleBar &&4595 !inlineRightPanelOwnsTitleBar &&
4498 "wco:pr-[calc(100vw-env(titlebar-area-width)-env(titlebar-area-x)+1em)]",4596 "wco:pr-[var(--workspace-native-controls-inset)]",
4499 )4597 )
4500 : "pb-2 pl-[calc(env(safe-area-inset-left)+0.75rem)] pr-[calc(env(safe-area-inset-right)+0.75rem)] pt-2 sm:pb-3 sm:pl-[calc(env(safe-area-inset-left)+1.25rem)] sm:pr-[calc(env(safe-area-inset-right)+1.25rem)] sm:pt-3",4598 : "workspace-topbar pl-[calc(env(safe-area-inset-left)+0.75rem)] pr-[calc(env(safe-area-inset-right)+0.75rem)] sm:pl-[calc(env(safe-area-inset-left)+1.25rem)] sm:pr-[calc(env(safe-area-inset-right)+1.25rem)]",
4501 )}4599 )}
4502 >4600 >
4503 <ChatHeader4601 <ChatHeader
@@ -4513,17 +4611,12 @@ export default function ChatView(props: ChatViewProps) { · 9 unmodified lines
4513 }4611 }
4514 keybindings={keybindings}4612 keybindings={keybindings}
4515 availableEditors={availableEditors}4613 availableEditors={availableEditors}
4516 terminalAvailable={Boolean(activeProject)}
4517 terminalOpen={Boolean(terminalUiState.terminalOpen)}
4518 rightPanelAvailable={Boolean(activeProject)}
4519 rightPanelOpen={rightPanelOpen}
4520 gitCwd={gitCwd}4614 gitCwd={gitCwd}
4521 onRunProjectScript={runProjectScript}4615 onRunProjectScript={runProjectScript}
4522 onAddProjectScript={saveProjectScript}4616 onAddProjectScript={saveProjectScript}
4523 onUpdateProjectScript={updateProjectScript}4617 onUpdateProjectScript={updateProjectScript}
4524 onDeleteProjectScript={deleteProjectScript}4618 onDeleteProjectScript={deleteProjectScript}
4525 onToggleTerminal={toggleTerminalVisibility}4619 rightPanelOpen={rightPanelOpen}
4526 onToggleRightPanel={toggleRightPanel}
4527 />4620 />
4528 </header>4621 </header>
4529 4622
@@ -4730,8 +4823,10 @@ export default function ChatView(props: ChatViewProps) { · 200 unmodified lines
4730 {!shouldUsePlanSidebarSheet && rightPanelOpen && activeThreadRef ? (4823 {!shouldUsePlanSidebarSheet && rightPanelOpen && activeThreadRef ? (
4731 <RightPanelTabs4824 <RightPanelTabs
4732 mode="inline"4825 mode="inline"
4826 maximized={rightPanelMaximized}
4733 surfaces={rightPanelState.surfaces}4827 surfaces={rightPanelState.surfaces}
4734 activeSurfaceId={activeRightPanelSurface?.id ?? null}4828 activeSurfaceId={activeRightPanelSurface?.id ?? null}
4829 pendingSurfaceIds={pendingFileSurfaceIds}
4735 previewSessions={activePreviewState.sessions}4830 previewSessions={activePreviewState.sessions}
4736 terminalLabelsById={activeTerminalLabelsById}4831 terminalLabelsById={activeTerminalLabelsById}
4737 onActivate={activateRightPanelSurface}4832 onActivate={activateRightPanelSurface}
@@ -4739,8 +4834,10 @@ export default function ChatView(props: ChatViewProps) { · 1 unmodified line
4739 onAddBrowser={createBrowserSurface}4834 onAddBrowser={createBrowserSurface}
4740 onAddTerminal={addTerminalSurface}4835 onAddTerminal={addTerminalSurface}
4741 onAddDiff={addDiffSurface}4836 onAddDiff={addDiffSurface}
4837 onAddFiles={addFilesSurface}
4742 browserAvailable={isPreviewSupportedInRuntime()}4838 browserAvailable={isPreviewSupportedInRuntime()}
4743 diffAvailable={isServerThread && isGitRepo}4839 diffAvailable={isServerThread && isGitRepo}
4840 filesAvailable={Boolean(activeProject)}
4744 >4841 >
4745 {activeRightPanelSurface?.kind === "preview" ? (4842 {activeRightPanelSurface?.kind === "preview" ? (
4746 <Suspense fallback={null}>4843 <Suspense fallback={null}>
@@ -4771,11 +4868,27 @@ export default function ChatView(props: ChatViewProps) { · 24 unmodified lines
4771 closeShortcutLabel={closeTerminalShortcutLabel ?? undefined}4868 closeShortcutLabel={closeTerminalShortcutLabel ?? undefined}
4772 />4869 />
4773 ) : activeRightPanelSurface?.kind === "diff" ? (4870 ) : activeRightPanelSurface?.kind === "diff" ? (
4774 <DiffWorkerPoolProvider>4871 <Suspense fallback={null}>
4775 <Suspense fallback={null}>4872 <DiffPanel mode="embedded" />
4776 <DiffPanel mode="embedded" />4873 </Suspense>
4777 </Suspense>4874 ) : (activeRightPanelSurface?.kind === "files" ||
4778 </DiffWorkerPoolProvider>4875 activeRightPanelSurface?.kind === "file") &&
4876 activeProject ? (
4877 <Suspense fallback={null}>
4878 <FilePreviewPanel
4879 key={`${activeProject.environmentId}:${activeProject.cwd}`}
4880 environmentId={activeProject.environmentId}
4881 cwd={activeProject.cwd}
4882 projectName={activeProject.name}
4883 relativePath={
4884 activeRightPanelSurface.kind === "file"
4885 ? activeRightPanelSurface.relativePath
4886 : null
4887 }
4888 onOpenFile={openFileSurface}
4889 onPendingChange={handleFilePendingChange}
4890 />
4891 </Suspense>
4779 ) : null}4892 ) : null}
4780 </RightPanelTabs>4893 </RightPanelTabs>
4781 ) : null}4894 ) : null}
@@ -4786,6 +4899,7 @@ export default function ChatView(props: ChatViewProps) { · 4 unmodified lines
4786 mode="sheet"4899 mode="sheet"
4787 surfaces={rightPanelState.surfaces}4900 surfaces={rightPanelState.surfaces}
4788 activeSurfaceId={activeRightPanelSurface?.id ?? null}4901 activeSurfaceId={activeRightPanelSurface?.id ?? null}
4902 pendingSurfaceIds={pendingFileSurfaceIds}
4789 previewSessions={activePreviewState.sessions}4903 previewSessions={activePreviewState.sessions}
4790 terminalLabelsById={activeTerminalLabelsById}4904 terminalLabelsById={activeTerminalLabelsById}
4791 onActivate={activateRightPanelSurface}4905 onActivate={activateRightPanelSurface}
@@ -4793,8 +4907,10 @@ export default function ChatView(props: ChatViewProps) { · 1 unmodified line
4793 onAddBrowser={createBrowserSurface}4907 onAddBrowser={createBrowserSurface}
4794 onAddTerminal={addTerminalSurface}4908 onAddTerminal={addTerminalSurface}
4795 onAddDiff={addDiffSurface}4909 onAddDiff={addDiffSurface}
4910 onAddFiles={addFilesSurface}
4796 browserAvailable={isPreviewSupportedInRuntime()}4911 browserAvailable={isPreviewSupportedInRuntime()}
4797 diffAvailable={isServerThread && isGitRepo}4912 diffAvailable={isServerThread && isGitRepo}
4913 filesAvailable={Boolean(activeProject)}
4798 >4914 >
4799 {activeRightPanelSurface?.kind === "preview" ? (4915 {activeRightPanelSurface?.kind === "preview" ? (
4800 <Suspense fallback={null}>4916 <Suspense fallback={null}>
@@ -4825,11 +4941,9 @@ export default function ChatView(props: ChatViewProps) { · 24 unmodified lines
4825 closeShortcutLabel={closeTerminalShortcutLabel ?? undefined}4941 closeShortcutLabel={closeTerminalShortcutLabel ?? undefined}
4826 />4942 />
4827 ) : activeRightPanelSurface?.kind === "diff" ? (4943 ) : activeRightPanelSurface?.kind === "diff" ? (
4828 <DiffWorkerPoolProvider>4944 <Suspense fallback={null}>
4829 <Suspense fallback={null}>4945 <DiffPanel mode="embedded" />
4830 <DiffPanel mode="embedded" />4946 </Suspense>
4831 </Suspense>
4832 </DiffWorkerPoolProvider>
4833 ) : activeRightPanelSurface?.kind === "plan" ? (4947 ) : activeRightPanelSurface?.kind === "plan" ? (
4834 <PlanSidebar4948 <PlanSidebar
4835 activePlan={activePlan}4949 activePlan={activePlan}
@@ -4843,6 +4957,24 @@ export default function ChatView(props: ChatViewProps) { · 7 unmodified lines
4843 mode="embedded"4957 mode="embedded"
4844 onClose={closePlanSidebar}4958 onClose={closePlanSidebar}
4845 />4959 />
4960 ) : (activeRightPanelSurface?.kind === "files" ||
4961 activeRightPanelSurface?.kind === "file") &&
4962 activeProject ? (
4963 <Suspense fallback={null}>
4964 <FilePreviewPanel
4965 key={`${activeProject.environmentId}:${activeProject.cwd}`}
4966 environmentId={activeProject.environmentId}
4967 cwd={activeProject.cwd}
4968 projectName={activeProject.name}
4969 relativePath={
4970 activeRightPanelSurface.kind === "file"
4971 ? activeRightPanelSurface.relativePath
4972 : null
4973 }
4974 onOpenFile={openFileSurface}
4975 onPendingChange={handleFilePendingChange}
4976 />
4977 </Suspense>
4846 ) : null}4978 ) : null}
4847 </RightPanelTabs>4979 </RightPanelTabs>
4848 </RightPanelSheet>4980 </RightPanelSheet>
@@ -4854,3 +4986,11 @@ export default function ChatView(props: ChatViewProps) { · 5 unmodified lines
4854 </div>4986 </div>
4855 );4987 );
4856}4988}
4989
4990export default function ChatView(props: ChatViewProps) {
4991 return (
4992 <DiffWorkerPoolProvider>
4993 <ChatViewContent {...props} />
4994 </DiffWorkerPoolProvider>
4995 );
4996}
apps/web/src/components/ComposerPromptEditor.tsx+1−1
@@ -64,7 +64,7 @@ import { · 63 unmodified lines
64 type TerminalContextDraft,64 type TerminalContextDraft,
65} from "~/lib/terminalContext";65} from "~/lib/terminalContext";
66import { cn } from "~/lib/utils";66import { cn } from "~/lib/utils";
67import { basenameOfPath } from "~/vscode-icons";67import { basenameOfPath } from "~/pierre-icons";
68import {68import {
69 COMPOSER_INLINE_CHIP_ICON_CLASS_NAME,69 COMPOSER_INLINE_CHIP_ICON_CLASS_NAME,
70 COMPOSER_INLINE_CHIP_LABEL_CLASS_NAME,70 COMPOSER_INLINE_CHIP_LABEL_CLASS_NAME,
apps/web/src/components/DiffWorkerPoolProvider.tsx+1−0
@@ -47,6 +47,7 @@ export function DiffWorkerPoolProvider({ children }: { children?: ReactNode }) { · 46 unmodified lines
47 highlighterOptions={{47 highlighterOptions={{
48 theme: diffThemeName,48 theme: diffThemeName,
49 tokenizeMaxLineLength: 1_000,49 tokenizeMaxLineLength: 1_000,
50 useTokenTransformer: true,
50 }}51 }}
51 >52 >
52 <DiffWorkerThemeSync themeName={diffThemeName} />53 <DiffWorkerThemeSync themeName={diffThemeName} />
apps/web/src/components/NoActiveThreadState.tsx+2−4
@@ -10,13 +10,11 @@ export function NoActiveThreadState() { · 9 unmodified lines
10 <header10 <header
11 className={cn(11 className={cn(
12 "border-b border-border px-3 sm:px-5",12 "border-b border-border px-3 sm:px-5",
13 isElectron13 isElectron ? "workspace-topbar drag-region" : "workspace-topbar",
14 ? "drag-region flex h-[52px] items-center wco:h-[env(titlebar-area-height)]"
15 : "py-2 sm:py-3",
16 )}14 )}
17 >15 >
18 {isElectron ? (16 {isElectron ? (
19 <span className="text-xs text-muted-foreground/50 wco:pr-[calc(100vw-env(titlebar-area-width)-env(titlebar-area-x)+1em)]">17 <span className="text-xs text-muted-foreground/50 wco:pr-[var(--workspace-native-controls-inset)]">
20 No active thread18 No active thread
21 </span>19 </span>
22 ) : (20 ) : (
apps/web/src/components/RightPanelTabs.tsx+150−71
@@ -1,21 +1,26 @@
1import type { PreviewSessionSnapshot } from "@t3tools/contracts";1import type { PreviewSessionSnapshot } from "@t3tools/contracts";
2import { getTerminalLabel } from "@t3tools/shared/terminalLabels";2import { getTerminalLabel } from "@t3tools/shared/terminalLabels";
3import { ClipboardList, FileDiff, Globe2, Plus, TerminalSquare, X } from "lucide-react";3import { ClipboardList, FileDiff, Files, Globe2, Plus, TerminalSquare, X } from "lucide-react";
4import { type ReactNode, useState } from "react";4import { type ReactNode, useEffect, useRef, useState } from "react";
5 5
6import { isElectron } from "~/env";6import { isElectron } from "~/env";
7import type { RightPanelSurface } from "~/rightPanelStore";7import type { RightPanelSurface } from "~/rightPanelStore";
8import { cn } from "~/lib/utils";8import { cn } from "~/lib/utils";
9import { Menu, MenuItem, MenuPopup, MenuTrigger } from "~/components/ui/menu";
10import { Tooltip, TooltipPopup, TooltipTrigger } from "~/components/ui/tooltip";9import { Tooltip, TooltipPopup, TooltipTrigger } from "~/components/ui/tooltip";
10import { Menu, MenuItem, MenuPopup, MenuTrigger } from "~/components/ui/menu";
11import { ScrollArea } from "~/components/ui/scroll-area";
11import { faviconUrlForOrigin } from "~/lib/favicon";12import { faviconUrlForOrigin } from "~/lib/favicon";
13import { useTheme } from "~/hooks/useTheme";
12 14
13import { PreviewPanelShell, type PreviewPanelMode } from "./preview/PreviewPanelShell";15import { PreviewPanelShell, type PreviewPanelMode } from "./preview/PreviewPanelShell";
16import { PierreEntryIcon } from "./chat/PierreEntryIcon";
14 17
15interface RightPanelTabsProps {18interface RightPanelTabsProps {
16 mode: PreviewPanelMode;19 mode: PreviewPanelMode;
20 maximized?: boolean;
17 surfaces: readonly RightPanelSurface[];21 surfaces: readonly RightPanelSurface[];
18 activeSurfaceId: string | null;22 activeSurfaceId: string | null;
23 pendingSurfaceIds: ReadonlySet<string>;
19 previewSessions: Readonly<Record<string, PreviewSessionSnapshot>>;24 previewSessions: Readonly<Record<string, PreviewSessionSnapshot>>;
20 terminalLabelsById: ReadonlyMap<string, string>;25 terminalLabelsById: ReadonlyMap<string, string>;
21 onActivate: (surface: RightPanelSurface) => void;26 onActivate: (surface: RightPanelSurface) => void;
@@ -23,8 +28,10 @@ interface RightPanelTabsProps { · 1 unmodified line
23 onAddBrowser: () => void;28 onAddBrowser: () => void;
24 onAddTerminal: () => void;29 onAddTerminal: () => void;
25 onAddDiff: () => void;30 onAddDiff: () => void;
31 onAddFiles: () => void;
26 browserAvailable: boolean;32 browserAvailable: boolean;
27 diffAvailable: boolean;33 diffAvailable: boolean;
34 filesAvailable: boolean;
28 children: ReactNode;35 children: ReactNode;
29}36}
30 37
@@ -32,8 +39,10 @@ function RightPanelEmptyState(props: { · 1 unmodified line
32 onAddBrowser: () => void;39 onAddBrowser: () => void;
33 onAddTerminal: () => void;40 onAddTerminal: () => void;
34 onAddDiff: () => void;41 onAddDiff: () => void;
42 onAddFiles: () => void;
35 browserAvailable: boolean;43 browserAvailable: boolean;
36 diffAvailable: boolean;44 diffAvailable: boolean;
45 filesAvailable: boolean;
37}) {46}) {
38 const actions = [47 const actions = [
39 {48 {
@@ -50,6 +59,13 @@ function RightPanelEmptyState(props: { · 10 unmodified lines
50 available: true,59 available: true,
51 onClick: props.onAddTerminal,60 onClick: props.onAddTerminal,
52 },61 },
62 {
63 label: "Files",
64 description: "Browse and read workspace files.",
65 icon: Files,
66 available: props.filesAvailable,
67 onClick: props.onAddFiles,
68 },
53 {69 {
54 label: "Diff",70 label: "Diff",
55 description: "Review changes in this thread.",71 description: "Review changes in this thread.",
@@ -68,7 +84,7 @@ function RightPanelEmptyState(props: { · 12 unmodified lines
68 Choose what to show in the right panel.84 Choose what to show in the right panel.
69 </p>85 </p>
70 </div>86 </div>
71 <div className="grid gap-2 sm:grid-cols-3">87 <div className="grid gap-2 sm:grid-cols-2 lg:grid-cols-4">
72 {actions.map((action) => {88 {actions.map((action) => {
73 const Icon = action.icon;89 const Icon = action.icon;
74 return (90 return (
@@ -101,6 +117,10 @@ function surfaceTitle( · 26 unmodified lines
101 switch (surface.kind) {117 switch (surface.kind) {
102 case "diff":118 case "diff":
103 return "Diff";119 return "Diff";
120 case "files":
121 return "Files";
122 case "file":
123 return surface.relativePath.slice(surface.relativePath.lastIndexOf("/") + 1);
104 case "terminal":124 case "terminal":
105 return (125 return (
106 terminalLabelsById.get(surface.activeTerminalId) ??126 terminalLabelsById.get(surface.activeTerminalId) ??
@@ -140,9 +160,11 @@ function PreviewFavicon({ url }: { url: string | null }) { · 33 unmodified lines
140function SurfaceIcon({160function SurfaceIcon({
141 surface,161 surface,
142 sessions,162 sessions,
163 theme,
143}: {164}: {
144 surface: RightPanelSurface;165 surface: RightPanelSurface;
145 sessions: Readonly<Record<string, PreviewSessionSnapshot>>;166 sessions: Readonly<Record<string, PreviewSessionSnapshot>>;
167 theme: "light" | "dark";
146}) {168}) {
147 switch (surface.kind) {169 switch (surface.kind) {
148 case "preview": {170 case "preview": {
@@ -152,6 +174,17 @@ function SurfaceIcon({ · 3 unmodified lines
152 }174 }
153 case "diff":175 case "diff":
154 return <FileDiff className="size-3.5 shrink-0" />;176 return <FileDiff className="size-3.5 shrink-0" />;
177 case "files":
178 return <Files className="size-3.5 shrink-0" />;
179 case "file":
180 return (
181 <PierreEntryIcon
182 pathValue={surface.relativePath}
183 kind="file"
184 theme={theme}
185 className="size-3.5"
186 />
187 );
155 case "terminal":188 case "terminal":
156 return <TerminalSquare className="size-3.5 shrink-0" />;189 return <TerminalSquare className="size-3.5 shrink-0" />;
157 case "plan":190 case "plan":
@@ -161,80 +194,124 @@ function SurfaceIcon({ · 3 unmodified lines
161 194
162export function RightPanelTabs(props: RightPanelTabsProps) {195export function RightPanelTabs(props: RightPanelTabsProps) {
163 const ownsDesktopTitleBar = isElectron && props.mode === "inline";196 const ownsDesktopTitleBar = isElectron && props.mode === "inline";
197 const { resolvedTheme } = useTheme();
198 const tabListRef = useRef<HTMLDivElement>(null);
199
200 useEffect(() => {
201 const activeTab = tabListRef.current?.querySelector<HTMLElement>("[data-active-tab='true']");
202 activeTab?.scrollIntoView({ block: "nearest", inline: "nearest" });
203 }, [props.activeSurfaceId]);
164 204
165 return (205 return (
166 <PreviewPanelShell mode={props.mode}>206 <PreviewPanelShell
207 mode={props.mode}
208 {...(props.maximized !== undefined ? { maximized: props.maximized } : {})}
209 >
167 <div210 <div
168 className={cn(211 className={cn(
169 "flex shrink-0 items-center px-2",212 "px-2 pr-28",
170 ownsDesktopTitleBar213 props.mode === "inline" ? "workspace-topbar" : "flex h-10 shrink-0 items-center",
171 ? "drag-region h-[52px] wco:h-[env(titlebar-area-height)] wco:pr-[calc(100vw-env(titlebar-area-width)-env(titlebar-area-x)+1em)]"214 ownsDesktopTitleBar &&
172 : "h-10",215 "drag-region wco:pr-[calc(var(--workspace-native-controls-inset)+6rem)]",
173 )}216 )}
217 data-right-panel-tabbar
174 >218 >
175 <div className="flex min-w-0 flex-1 items-center gap-1 overflow-x-auto">219 <ScrollArea
176 {props.surfaces.map((surface) => {220 ref={tabListRef}
177 const active = surface.id === props.activeSurfaceId;221 hideScrollbars
178 const title = surfaceTitle(surface, props.previewSessions, props.terminalLabelsById);222 scrollFade
179 return (223 className="min-w-0 flex-1 rounded-none"
180 <div224 data-right-panel-tab-list
181 key={surface.id}225 >
182 className={cn(226 <div className="flex h-full w-max min-w-full items-center gap-1">
183 "group flex h-7 min-w-0 max-w-52 items-center gap-1.5 rounded-md px-2 text-sm",227 {props.surfaces.map((surface) => {
184 active228 const active = surface.id === props.activeSurfaceId;
185 ? "bg-accent text-foreground"229 const pending = props.pendingSurfaceIds.has(surface.id);
186 : "text-muted-foreground hover:bg-accent/60 hover:text-foreground",230 const title = surfaceTitle(surface, props.previewSessions, props.terminalLabelsById);
187 )}231 return (
188 >232 <div
189 <Tooltip>233 key={surface.id}
190 <TooltipTrigger234 data-active-tab={active}
191 render={235 className={cn(
192 <button236 "group flex h-7 min-w-25 max-w-44 shrink-0 items-center gap-1.5 rounded-md px-2 text-sm",
193 type="button"237 active
194 className="flex min-w-0 flex-1 items-center gap-1.5"238 ? "bg-accent text-foreground"
195 onClick={() => props.onActivate(surface)}239 : "text-muted-foreground hover:bg-accent/60 hover:text-foreground",
196 >240 )}
197 <SurfaceIcon surface={surface} sessions={props.previewSessions} />
198 <span className="truncate">{title}</span>
199 </button>
200 }
201 />
202 <TooltipPopup>{title}</TooltipPopup>
203 </Tooltip>
204 <button
205 type="button"
206 className="rounded p-0.5 opacity-0 hover:bg-muted group-hover:opacity-100 focus:opacity-100"
207 aria-label={`Close ${title}`}
208 onClick={() => props.onCloseSurface(surface)}
209 >241 >
210 <X className="size-3" />242 <Tooltip>
211 </button>243 <TooltipTrigger
212 </div>244 render={
213 );245 <button
214 })}246 type="button"
215 </div>247 className="flex min-w-0 flex-1 items-center gap-1.5"
216 <Menu>248 onClick={() => props.onActivate(surface)}
217 <MenuTrigger249 >
218 className="relative ml-1 inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground"250 <SurfaceIcon
219 aria-label="Add panel surface"251 surface={surface}
220 >252 sessions={props.previewSessions}
221 <Plus className="size-4" />253 theme={resolvedTheme}
222 </MenuTrigger>254 />
223 <MenuPopup align="start" side="bottom" sideOffset={6} className="min-w-44">255 <span className="truncate">{title}</span>
224 <MenuItem onClick={props.onAddBrowser} disabled={!props.browserAvailable}>256 </button>
225 <Globe2 />257 }
226 Browser258 />
227 </MenuItem>259 <TooltipPopup>{title}</TooltipPopup>
228 <MenuItem onClick={props.onAddTerminal}>260 </Tooltip>
229 <TerminalSquare />261 <button
230 Terminal262 type="button"
231 </MenuItem>263 className={cn(
232 <MenuItem onClick={props.onAddDiff} disabled={!props.diffAvailable}>264 "relative flex size-4 shrink-0 items-center justify-center rounded hover:bg-muted focus:opacity-100",
233 <FileDiff />265 pending ? "opacity-100" : "opacity-0 group-hover:opacity-100",
234 Diff266 )}
235 </MenuItem>267 aria-label={`Close ${title}`}
236 </MenuPopup>268 onClick={() => props.onCloseSurface(surface)}
237 </Menu>269 >
270 {pending ? (
271 <>
272 <span
273 className="size-2 rounded-full bg-current group-hover:hidden"
274 aria-hidden
275 />
276 <X className="hidden size-3 group-hover:block" />
277 </>
278 ) : (
279 <X className="size-3" />
280 )}
281 </button>
282 </div>
283 );
284 })}
285 {props.surfaces.length > 0 ? (
286 <Menu>
287 <MenuTrigger
288 className="relative inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground"
289 aria-label="Add panel surface"
290 >
291 <Plus className="size-4" />
292 </MenuTrigger>
293 <MenuPopup align="start" side="bottom" sideOffset={6} className="min-w-44">
294 <MenuItem onClick={props.onAddBrowser} disabled={!props.browserAvailable}>
295 <Globe2 />
296 Browser
297 </MenuItem>
298 <MenuItem onClick={props.onAddTerminal}>
299 <TerminalSquare />
300 Terminal
301 </MenuItem>
302 <MenuItem onClick={props.onAddFiles} disabled={!props.filesAvailable}>
303 <Files />
304 Files
305 </MenuItem>
306 <MenuItem onClick={props.onAddDiff} disabled={!props.diffAvailable}>
307 <FileDiff />
308 Diff
309 </MenuItem>
310 </MenuPopup>
311 </Menu>
312 ) : null}
313 </div>
314 </ScrollArea>
238 </div>315 </div>
239 <div className="flex min-h-0 flex-1 flex-col">316 <div className="flex min-h-0 flex-1 flex-col">
240 {props.activeSurfaceId === null ? (317 {props.activeSurfaceId === null ? (
@@ -242,8 +319,10 @@ export function RightPanelTabs(props: RightPanelTabsProps) { · 1 unmodified line
242 onAddBrowser={props.onAddBrowser}319 onAddBrowser={props.onAddBrowser}
243 onAddTerminal={props.onAddTerminal}320 onAddTerminal={props.onAddTerminal}
244 onAddDiff={props.onAddDiff}321 onAddDiff={props.onAddDiff}
322 onAddFiles={props.onAddFiles}
245 browserAvailable={props.browserAvailable}323 browserAvailable={props.browserAvailable}
246 diffAvailable={props.diffAvailable}324 diffAvailable={props.diffAvailable}
325 filesAvailable={props.filesAvailable}
247 />326 />
248 ) : (327 ) : (
249 props.children328 props.children
apps/web/src/components/chat/ChangedFilesTree.tsx+2−2
@@ -9,7 +9,7 @@ import { · 8 unmodified lines
9import { ChevronRightIcon, FolderIcon, FolderClosedIcon } from "lucide-react";9import { ChevronRightIcon, FolderIcon, FolderClosedIcon } from "lucide-react";
10import { cn } from "~/lib/utils";10import { cn } from "~/lib/utils";
11import { DiffStatLabel, hasNonZeroStat } from "./DiffStatLabel";11import { DiffStatLabel, hasNonZeroStat } from "./DiffStatLabel";
12import { VscodeEntryIcon } from "./VscodeEntryIcon";12import { PierreEntryIcon } from "./PierreEntryIcon";
13import { Button } from "../ui/button";13import { Button } from "../ui/button";
14 14
15const EMPTY_DIRECTORY_OVERRIDES: Record<string, boolean> = {};15const EMPTY_DIRECTORY_OVERRIDES: Record<string, boolean> = {};
@@ -177,7 +177,7 @@ export const ChangedFilesTree = memo(function ChangedFilesTree(props: { · 161 unmodified lines
177 {hasDirectoryNodes || depth > 0 ? (177 {hasDirectoryNodes || depth > 0 ? (
178 <span aria-hidden="true" className="size-3.5 shrink-0" />178 <span aria-hidden="true" className="size-3.5 shrink-0" />
179 ) : null}179 ) : null}
180 <VscodeEntryIcon180 <PierreEntryIcon
181 pathValue={node.path}181 pathValue={node.path}
182 kind="file"182 kind="file"
183 theme={resolvedTheme}183 theme={resolvedTheme}
apps/web/src/components/chat/ChatComposer.tsx+1−1
@@ -79,7 +79,7 @@ import { · 78 unmodified lines
79} from "./composerProviderState";79} from "./composerProviderState";
80import { ContextWindowMeter } from "./ContextWindowMeter";80import { ContextWindowMeter } from "./ContextWindowMeter";
81import { buildExpandedImagePreview, type ExpandedImagePreview } from "./ExpandedImagePreview";81import { buildExpandedImagePreview, type ExpandedImagePreview } from "./ExpandedImagePreview";
82import { basenameOfPath } from "../../vscode-icons";82import { basenameOfPath } from "../../pierre-icons";
83import { cn, randomUUID } from "~/lib/utils";83import { cn, randomUUID } from "~/lib/utils";
84import { Separator } from "../ui/separator";84import { Separator } from "../ui/separator";
85import { Button } from "../ui/button";85import { Button } from "../ui/button";
apps/web/src/components/chat/ChatHeader.tsx+11−64
@@ -9,14 +9,12 @@ import { scopeThreadRef } from "@t3tools/client-runtime"; · 8 unmodified lines
9import { memo } from "react";9import { memo } from "react";
10import GitActionsControl from "../GitActionsControl";10import GitActionsControl from "../GitActionsControl";
11import { type DraftId } from "~/composerDraftStore";11import { type DraftId } from "~/composerDraftStore";
12import { PanelBottomIcon, PanelRightIcon } from "lucide-react";
13import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";12import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
14import ProjectScriptsControl, { type NewProjectScriptInput } from "../ProjectScriptsControl";13import ProjectScriptsControl, { type NewProjectScriptInput } from "../ProjectScriptsControl";
15import { Toggle } from "../ui/toggle";
16import { SidebarTrigger } from "../ui/sidebar";14import { SidebarTrigger } from "../ui/sidebar";
17import { OpenInPicker } from "./OpenInPicker";15import { OpenInPicker } from "./OpenInPicker";
18import { usePrimaryEnvironmentId } from "../../environments/primary";16import { usePrimaryEnvironmentId } from "../../environments/primary/context";
19import { shortcutLabelForCommand } from "../../keybindings";17import { cn } from "~/lib/utils";
20 18
21interface ChatHeaderProps {19interface ChatHeaderProps {
22 activeThreadEnvironmentId: EnvironmentId;20 activeThreadEnvironmentId: EnvironmentId;
@@ -29,17 +27,12 @@ interface ChatHeaderProps { · 6 unmodified lines
29 preferredScriptId: string | null;27 preferredScriptId: string | null;
30 keybindings: ResolvedKeybindingsConfig;28 keybindings: ResolvedKeybindingsConfig;
31 availableEditors: ReadonlyArray<EditorId>;29 availableEditors: ReadonlyArray<EditorId>;
32 terminalAvailable: boolean;
33 terminalOpen: boolean;
34 rightPanelAvailable: boolean;
35 rightPanelOpen: boolean;
36 gitCwd: string | null;30 gitCwd: string | null;
37 onRunProjectScript: (script: ProjectScript) => void;31 onRunProjectScript: (script: ProjectScript) => void;
38 onAddProjectScript: (input: NewProjectScriptInput) => Promise<void>;32 onAddProjectScript: (input: NewProjectScriptInput) => Promise<void>;
39 onUpdateProjectScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void>;33 onUpdateProjectScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void>;
40 onDeleteProjectScript: (scriptId: string) => Promise<void>;34 onDeleteProjectScript: (scriptId: string) => Promise<void>;
41 onToggleTerminal: () => void;35 rightPanelOpen: boolean;
42 onToggleRightPanel: () => void;
43}36}
44 37
45export function shouldShowOpenInPicker(input: {38export function shouldShowOpenInPicker(input: {
@@ -65,17 +58,12 @@ export const ChatHeader = memo(function ChatHeader({ · 19 unmodified lines
65 preferredScriptId,58 preferredScriptId,
66 keybindings,59 keybindings,
67 availableEditors,60 availableEditors,
68 terminalAvailable,
69 terminalOpen,
70 rightPanelAvailable,
71 rightPanelOpen,
72 gitCwd,61 gitCwd,
73 onRunProjectScript,62 onRunProjectScript,
74 onAddProjectScript,63 onAddProjectScript,
75 onUpdateProjectScript,64 onUpdateProjectScript,
76 onDeleteProjectScript,65 onDeleteProjectScript,
77 onToggleTerminal,66 rightPanelOpen,
78 onToggleRightPanel,
79}: ChatHeaderProps) {67}: ChatHeaderProps) {
80 const primaryEnvironmentId = usePrimaryEnvironmentId();68 const primaryEnvironmentId = usePrimaryEnvironmentId();
81 const showOpenInPicker = shouldShowOpenInPicker({69 const showOpenInPicker = shouldShowOpenInPicker({
@@ -83,9 +71,6 @@ export const ChatHeader = memo(function ChatHeader({ · 1 unmodified line
83 activeThreadEnvironmentId,71 activeThreadEnvironmentId,
84 primaryEnvironmentId,72 primaryEnvironmentId,
85 });73 });
86 const terminalShortcutLabel = shortcutLabelForCommand(keybindings, "terminal.toggle");
87 const rightPanelShortcutLabel = shortcutLabelForCommand(keybindings, "rightPanel.toggle");
88
89 return (74 return (
90 <div className="@container/header-actions flex min-w-0 flex-1 flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">75 <div className="@container/header-actions flex min-w-0 flex-1 flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
91 <div className="flex min-w-0 flex-wrap items-center gap-2 overflow-hidden sm:flex-1 sm:flex-nowrap sm:gap-3">76 <div className="flex min-w-0 flex-wrap items-center gap-2 overflow-hidden sm:flex-1 sm:flex-nowrap sm:gap-3">
@@ -104,7 +89,13 @@ export const ChatHeader = memo(function ChatHeader({ · 12 unmodified lines
104 <TooltipPopup side="top">{activeThreadTitle}</TooltipPopup>89 <TooltipPopup side="top">{activeThreadTitle}</TooltipPopup>
105 </Tooltip>90 </Tooltip>
106 </div>91 </div>
107 <div className="flex min-w-0 flex-wrap items-center justify-start gap-2 sm:shrink-0 sm:justify-end @3xl/header-actions:gap-3">92 <div
93 data-chat-header-actions
94 className={cn(
95 "flex min-w-0 flex-wrap items-center justify-start gap-2 sm:shrink-0 sm:justify-end @3xl/header-actions:gap-3",
96 rightPanelOpen ? "pr-0" : "pr-16",
97 )}
98 >
108 {activeProjectScripts && (99 {activeProjectScripts && (
109 <ProjectScriptsControl100 <ProjectScriptsControl
110 scripts={activeProjectScripts}101 scripts={activeProjectScripts}
@@ -130,50 +121,6 @@ export const ChatHeader = memo(function ChatHeader({ · 19 unmodified lines
130 {...(draftId ? { draftId } : {})}121 {...(draftId ? { draftId } : {})}
131 />122 />
132 )}123 )}
133 <Tooltip>
134 <TooltipTrigger
135 render={
136 <Toggle
137 className="shrink-0"
138 pressed={terminalOpen}
139 onPressedChange={onToggleTerminal}
140 aria-label="Toggle terminal drawer"
141 variant="ghost"
142 size="xs"
143 disabled={!terminalAvailable}
144 >
145 <PanelBottomIcon className="size-3.5" />
146 </Toggle>
147 }
148 />
149 <TooltipPopup side="bottom">
150 {terminalAvailable
151 ? `Toggle terminal drawer${terminalShortcutLabel ? ` (${terminalShortcutLabel})` : ""}`
152 : "Terminal drawer is unavailable"}
153 </TooltipPopup>
154 </Tooltip>
155 <Tooltip>
156 <TooltipTrigger
157 render={
158 <Toggle
159 className="shrink-0"
160 pressed={rightPanelOpen}
161 onPressedChange={onToggleRightPanel}
162 aria-label="Toggle right panel"
163 variant="ghost"
164 size="xs"
165 disabled={!rightPanelAvailable}
166 >
167 <PanelRightIcon className="size-3.5" />
168 </Toggle>
169 }
170 />
171 <TooltipPopup side="bottom">
172 {rightPanelAvailable
173 ? `Toggle right panel${rightPanelShortcutLabel ? ` (${rightPanelShortcutLabel})` : ""}`
174 : "Right panel is unavailable"}
175 </TooltipPopup>
176 </Tooltip>
177 </div>124 </div>
178 </div>125 </div>
179 );126 );
apps/web/src/components/chat/ComposerCommandMenu.tsx+2−2
@@ -18,7 +18,7 @@ import { · 17 unmodified lines
18 CommandList,18 CommandList,
19 CommandSeparator,19 CommandSeparator,
20} from "../ui/command";20} from "../ui/command";
21import { VscodeEntryIcon } from "./VscodeEntryIcon";21import { PierreEntryIcon } from "./PierreEntryIcon";
22 22
23export type ComposerCommandItem =23export type ComposerCommandItem =
24 | {24 | {
@@ -228,7 +228,7 @@ const ComposerCommandMenuItem = memo(function ComposerCommandMenuItem(props: { · 203 unmodified lines
228 }}228 }}
229 >229 >
230 {props.item.type === "path" ? (230 {props.item.type === "path" ? (
231 <VscodeEntryIcon231 <PierreEntryIcon
232 pathValue={props.item.path}232 pathValue={props.item.path}
233 kind={props.item.pathKind}233 kind={props.item.pathKind}
234 theme={props.resolvedTheme}234 theme={props.resolvedTheme}
apps/web/src/components/chat/FileTagChip.tsx+3−3
@@ -1,4 +1,4 @@
1import { inferEntryKindFromPath } from "../../vscode-icons";1import { inferEntryKindFromPath } from "../../pierre-icons";
2import {2import {
3 CHAT_INLINE_CHIP_CLASS_NAME,3 CHAT_INLINE_CHIP_CLASS_NAME,
4 CHAT_INLINE_CHIP_LABEL_CLASS_NAME,4 CHAT_INLINE_CHIP_LABEL_CLASS_NAME,
@@ -6,7 +6,7 @@ import { · 1 unmodified line
6 COMPOSER_INLINE_CHIP_ICON_CLASS_NAME,6 COMPOSER_INLINE_CHIP_ICON_CLASS_NAME,
7 COMPOSER_INLINE_CHIP_LABEL_CLASS_NAME,7 COMPOSER_INLINE_CHIP_LABEL_CLASS_NAME,
8} from "../composerInlineChip";8} from "../composerInlineChip";
9import { VscodeEntryIcon } from "./VscodeEntryIcon";9import { PierreEntryIcon } from "./PierreEntryIcon";
10 10
11export const FILE_TAG_CHIP_CLASS_NAME = COMPOSER_INLINE_CHIP_CLASS_NAME;11export const FILE_TAG_CHIP_CLASS_NAME = COMPOSER_INLINE_CHIP_CLASS_NAME;
12export const CHAT_FILE_TAG_CHIP_CLASS_NAME = CHAT_INLINE_CHIP_CLASS_NAME;12export const CHAT_FILE_TAG_CHIP_CLASS_NAME = CHAT_INLINE_CHIP_CLASS_NAME;
@@ -19,7 +19,7 @@ export function FileTagChipContent(props: { · 6 unmodified lines
19}) {19}) {
20 return (20 return (
21 <>21 <>
22 <VscodeEntryIcon22 <PierreEntryIcon
23 pathValue={props.path}23 pathValue={props.path}
24 kind={inferEntryKindFromPath(props.path)}24 kind={inferEntryKindFromPath(props.path)}
25 theme={props.theme}25 theme={props.theme}
apps/web/src/components/chat/MessagesTimeline.browser.tsx+2−6
@@ -7,8 +7,6 @@ import { page } from "vite-plus/test/browser"; · 6 unmodified lines
7import { afterEach, describe, expect, it, vi } from "vite-plus/test";7import { afterEach, describe, expect, it, vi } from "vite-plus/test";
8import { render } from "vitest-browser-react";8import { render } from "vitest-browser-react";
9 9
10import { getVscodeIconUrlForEntry } from "../../vscode-icons";
11
12const scrollToEndSpy = vi.fn();10const scrollToEndSpy = vi.fn();
13const getStateSpy = vi.fn(() => ({ isAtEnd: true }));11const getStateSpy = vi.fn(() => ({ isAtEnd: true }));
14 12
@@ -392,14 +390,12 @@ describe("MessagesTimeline", () => { · 377 unmodified lines
392 const assistantFileLink = document.querySelector(390 const assistantFileLink = document.querySelector(
393 '[data-message-role="assistant"] .chat-markdown-file-link',391 '[data-message-role="assistant"] .chat-markdown-file-link',
394 );392 );
395 const icon = assistantFileLink?.querySelector("img");393 const icon = assistantFileLink?.querySelector("svg[data-pierre-icon]");
396 394
397 expect(assistantFileLink?.textContent).toContain("package.json");395 expect(assistantFileLink?.textContent).toContain("package.json");
398 expect(assistantFileLink?.textContent).toContain("L25");396 expect(assistantFileLink?.textContent).toContain("L25");
399 expect(assistantFileLink?.getAttribute("href")).toBe("/repo/project/path/to/package.json:25");397 expect(assistantFileLink?.getAttribute("href")).toBe("/repo/project/path/to/package.json:25");
400 expect(icon?.getAttribute("src")).toBe(398 expect(icon?.getAttribute("data-pierre-icon")).toBe("t3-file-icon-package-json");
401 getVscodeIconUrlForEntry("/repo/project/path/to/package.json", "file", "dark"),
402 );
403 } finally {399 } finally {
404 await screen.unmount();400 await screen.unmount();
405 }401 }
apps/web/src/components/chat/PanelLayoutControls.tsx+114−0
@@ -0,0 +1,114 @@
1import { Maximize2Icon, Minimize2Icon, PanelBottomIcon, PanelRightIcon } from "lucide-react";
2import { memo } from "react";
3
4import { cn } from "~/lib/utils";
5
6import { Toggle } from "../ui/toggle";
7import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
8
9interface PanelLayoutControlsProps {
10 terminalAvailable: boolean;
11 terminalOpen: boolean;
12 terminalShortcutLabel: string | null;
13 rightPanelAvailable: boolean;
14 rightPanelOpen: boolean;
15 rightPanelShortcutLabel: string | null;
16 rightPanelMaximized: boolean;
17 canMaximizeRightPanel: boolean;
18 onToggleTerminal: () => void;
19 onToggleRightPanel: () => void;
20 onToggleRightPanelMaximized: () => void;
21}
22
23export const PanelLayoutControls = memo(function PanelLayoutControls({
24 terminalAvailable,
25 terminalOpen,
26 terminalShortcutLabel,
27 rightPanelAvailable,
28 rightPanelOpen,
29 rightPanelShortcutLabel,
30 rightPanelMaximized,
31 canMaximizeRightPanel,
32 onToggleTerminal,
33 onToggleRightPanel,
34 onToggleRightPanelMaximized,
35}: PanelLayoutControlsProps) {
36 return (
37 <div className={cn("workspace-titlebar-controls z-50 gap-1")} data-panel-layout-controls>
38 {rightPanelOpen ? (
39 <Tooltip>
40 <TooltipTrigger
41 render={
42 <Toggle
43 className="shrink-0"
44 pressed={rightPanelMaximized}
45 onPressedChange={onToggleRightPanelMaximized}
46 aria-label={rightPanelMaximized ? "Restore panel size" : "Maximize panel"}
47 variant="ghost"
48 size="sm"
49 disabled={!canMaximizeRightPanel}
50 >
51 {rightPanelMaximized ? (
52 <Minimize2Icon className="size-3.5" />
53 ) : (
54 <Maximize2Icon className="size-3.5" />
55 )}
56 </Toggle>
57 }
58 />
59 <TooltipPopup side="bottom">
60 {canMaximizeRightPanel
61 ? rightPanelMaximized
62 ? "Restore panel size"
63 : "Maximize panel"
64 : "Panel maximization is unavailable at this width"}
65 </TooltipPopup>
66 </Tooltip>
67 ) : null}
68 <Tooltip>
69 <TooltipTrigger
70 render={
71 <Toggle
72 className="shrink-0"
73 pressed={terminalOpen}
74 onPressedChange={onToggleTerminal}
75 aria-label="Toggle terminal drawer"
76 variant="ghost"
77 size="sm"
78 disabled={!terminalAvailable}
79 >
80 <PanelBottomIcon className="size-3.5" />
81 </Toggle>
82 }
83 />
84 <TooltipPopup side="bottom">
85 {terminalAvailable
86 ? `Toggle terminal drawer${terminalShortcutLabel ? ` (${terminalShortcutLabel})` : ""}`
87 : "Terminal drawer is unavailable"}
88 </TooltipPopup>
89 </Tooltip>
90 <Tooltip>
91 <TooltipTrigger
92 render={
93 <Toggle
94 className="shrink-0"
95 pressed={rightPanelOpen}
96 onPressedChange={onToggleRightPanel}
97 aria-label="Toggle right panel"
98 variant="ghost"
99 size="sm"
100 disabled={!rightPanelAvailable}
101 >
102 <PanelRightIcon className="size-3.5" />
103 </Toggle>
104 }
105 />
106 <TooltipPopup side="bottom">
107 {rightPanelAvailable
108 ? `Toggle right panel${rightPanelShortcutLabel ? ` (${rightPanelShortcutLabel})` : ""}`
109 : "Right panel is unavailable"}
110 </TooltipPopup>
111 </Tooltip>
112 </div>
113 );
114});
apps/web/src/components/chat/PierreEntryIcon.tsx+95−0
@@ -0,0 +1,95 @@
1import { FileIcon, FolderIcon } from "lucide-react";
2import { memo, useInsertionEffect, useMemo } from "react";
3
4import { ensurePierreIconSprite, resolvePierreIconForEntry } from "../../pierre-icons";
5import { cn } from "~/lib/utils";
6
7const ICON_COLORS: Record<string, readonly [light: string, dark: string]> = {
8 astro: ["#a631be", "#d568ea"],
9 babel: ["#d5a910", "#ffd452"],
10 bash: ["#199f43", "#5ecc71"],
11 biome: ["#1a85d4", "#69b1ff"],
12 bootstrap: ["#693acf", "#9d6afb"],
13 browserslist: ["#d5a910", "#ffd452"],
14 bun: ["#594c5b", "#79697b"],
15 c: ["#1a85d4", "#69b1ff"],
16 claude: ["#d47628", "#ffa359"],
17 cpp: ["#1a85d4", "#69b1ff"],
18 css: ["#693acf", "#9d6afb"],
19 database: ["#a631be", "#d568ea"],
20 default: ["#84848a", "#adadb1"],
21 docker: ["#1a85d4", "#69b1ff"],
22 eslint: ["#693acf", "#9d6afb"],
23 git: ["#ff8c5b", "#d5512f"],
24 go: ["#1ca1c7", "#68cdf2"],
25 graphql: ["#d32a61", "#ff678d"],
26 html: ["#d47628", "#ffa359"],
27 image: ["#d32a61", "#ff678d"],
28 javascript: ["#d5a910", "#ffd452"],
29 json: ["#d47628", "#ffa359"],
30 markdown: ["#199f43", "#5ecc71"],
31 mcp: ["#17a5af", "#64d1db"],
32 nextjs: ["#84848a", "#adadb1"],
33 npm: ["#d52c36", "#ff6762"],
34 oxc: ["#1ca1c7", "#68cdf2"],
35 postcss: ["#d52c36", "#ff6762"],
36 prettier: ["#17a5af", "#64d1db"],
37 python: ["#1a85d4", "#69b1ff"],
38 react: ["#1ca1c7", "#68cdf2"],
39 ruby: ["#d52c36", "#ff6762"],
40 rust: ["#d47628", "#ffa359"],
41 sass: ["#d32a61", "#ff678d"],
42 stylelint: ["#84848a", "#adadb1"],
43 svelte: ["#d52c36", "#ff6762"],
44 svg: ["#d47628", "#ffa359"],
45 svgo: ["#199f43", "#5ecc71"],
46 swift: ["#d47628", "#ffa359"],
47 table: ["#17a5af", "#64d1db"],
48 tailwind: ["#1ca1c7", "#68cdf2"],
49 terraform: ["#693acf", "#9d6afb"],
50 text: ["#84848a", "#adadb1"],
51 typescript: ["#1a85d4", "#69b1ff"],
52 vite: ["#a631be", "#d568ea"],
53 vscode: ["#1a85d4", "#69b1ff"],
54 vue: ["#199f43", "#5ecc71"],
55 wasm: ["#693acf", "#9d6afb"],
56 webpack: ["#1a85d4", "#69b1ff"],
57 yml: ["#d52c36", "#ff6762"],
58 zig: ["#d47628", "#ffa359"],
59 zip: ["#d47628", "#ffa359"],
60};
61
62export const PierreEntryIcon = memo(function PierreEntryIcon(props: {
63 pathValue: string;
64 kind: "file" | "directory";
65 theme: "light" | "dark";
66 className?: string;
67}) {
68 useInsertionEffect(ensurePierreIconSprite, []);
69 const icon = useMemo(
70 () => resolvePierreIconForEntry(props.pathValue, props.kind),
71 [props.kind, props.pathValue],
72 );
73
74 if (!icon) {
75 return props.kind === "directory" ? (
76 <FolderIcon className={cn("size-4 text-muted-foreground/80", props.className)} />
77 ) : (
78 <FileIcon className={cn("size-4 text-muted-foreground/80", props.className)} />
79 );
80 }
81
82 const colors = ICON_COLORS[icon.token ?? "default"] ?? ICON_COLORS.default;
83 return (
84 <svg
85 aria-hidden="true"
86 data-pierre-icon={icon.name}
87 data-icon-token={icon.token}
88 className={cn("size-4 shrink-0", props.className)}
89 style={{ color: colors?.[props.theme === "light" ? 0 : 1] }}
90 viewBox="0 0 16 16"
91 >
92 <use href={`#${icon.name}`} />
93 </svg>
94 );
95});
apps/web/src/components/chat/VscodeEntryIcon.tsx+0−37
@@ -1,37 +0,0 @@
1import { memo, useMemo, useState } from "react";
2import { getVscodeIconUrlForEntry } from "../../vscode-icons";
3import { FileIcon, FolderIcon } from "lucide-react";
4import { cn } from "~/lib/utils";
5
6export const VscodeEntryIcon = memo(function VscodeEntryIcon(props: {
7 pathValue: string;
8 kind: "file" | "directory";
9 theme: "light" | "dark";
10 className?: string;
11}) {
12 const [failedIconUrl, setFailedIconUrl] = useState<string | null>(null);
13 const iconUrl = useMemo(
14 () => getVscodeIconUrlForEntry(props.pathValue, props.kind, props.theme),
15 [props.kind, props.pathValue, props.theme],
16 );
17 const failed = failedIconUrl === iconUrl;
18
19 if (failed) {
20 return props.kind === "directory" ? (
21 <FolderIcon className={cn("size-4 text-muted-foreground/80", props.className)} />
22 ) : (
23 <FileIcon className={cn("size-4 text-muted-foreground/80", props.className)} />
24 );
25 }
26
27 return (
28 <img
29 src={iconUrl}
30 alt=""
31 aria-hidden="true"
32 className={cn("size-4 shrink-0", props.className)}
33 loading="lazy"
34 onError={() => setFailedIconUrl(iconUrl)}
35 />
36 );
37});
apps/web/src/components/files/FileBrowserPanel.tsx+128−0
@@ -0,0 +1,128 @@
1import type { EnvironmentId, ProjectEntry } from "@t3tools/contracts";
2import { FileTree, useFileTree } from "@pierre/trees/react";
3import { RefreshCw, Search } from "lucide-react";
4import { useEffect, useMemo, useRef } from "react";
5
6import { useTheme } from "~/hooks/useTheme";
7import { cn } from "~/lib/utils";
8import { T3_PIERRE_ICONS } from "~/pierre-icons";
9
10import { useProjectEntriesQuery } from "./projectFilesQueryState";
11
12interface FileBrowserPanelProps {
13 environmentId: EnvironmentId;
14 cwd: string;
15 projectName: string;
16 onOpenFile: (relativePath: string) => void;
17}
18
19const TREE_UNSAFE_CSS = `
20 :host {
21 --trees-bg-override: transparent;
22 --trees-selected-bg-override: color-mix(in srgb, currentColor 12%, transparent);
23 --trees-hover-bg-override: color-mix(in srgb, currentColor 7%, transparent);
24 --trees-border-color-override: color-mix(in srgb, currentColor 14%, transparent);
25 --trees-font-family-override: var(--font-sans);
26 --trees-font-size-override: 12px;
27 }
28 button[data-type='item'] { border-radius: 5px; }
29`;
30
31function treePath(entry: ProjectEntry): string {
32 return entry.kind === "directory" ? `${entry.path}/` : entry.path;
33}
34
35export default function FileBrowserPanel({
36 environmentId,
37 cwd,
38 projectName,
39 onOpenFile,
40}: FileBrowserPanelProps) {
41 const { resolvedTheme } = useTheme();
42 const entriesQuery = useProjectEntriesQuery(environmentId, cwd);
43 const entries = entriesQuery.data?.entries ?? [];
44 const entryKinds = useMemo(
45 () => new Map(entries.map((entry) => [entry.path, entry.kind] as const)),
46 [entries],
47 );
48 const entryKindsRef = useRef<ReadonlyMap<string, ProjectEntry["kind"]>>(entryKinds);
49 const treePaths = useMemo(() => entries.map(treePath), [entries]);
50 const previousTreePathsRef = useRef<readonly string[]>([]);
51
52 const { model } = useFileTree({
53 density: "compact",
54 fileTreeSearchMode: "hide-non-matches",
55 flattenEmptyDirectories: true,
56 initialExpansion: 1,
57 icons: T3_PIERRE_ICONS,
58 onSelectionChange: (selectedPaths) => {
59 const selectedPath = selectedPaths.at(-1)?.replace(/\/$/, "");
60 if (selectedPath && entryKindsRef.current.get(selectedPath) === "file") {
61 onOpenFile(selectedPath);
62 }
63 },
64 paths: [],
65 search: true,
66 unsafeCSS: TREE_UNSAFE_CSS,
67 });
68
69 useEffect(() => {
70 if (previousTreePathsRef.current === treePaths) return;
71 entryKindsRef.current = entryKinds;
72 previousTreePathsRef.current = treePaths;
73 model.resetPaths(treePaths);
74 }, [entryKinds, model, treePaths]);
75
76 const fileCount = useMemo(
77 () => entries.reduce((count, entry) => count + (entry.kind === "file" ? 1 : 0), 0),
78 [entries],
79 );
80
81 return (
82 <div
83 className="flex min-h-0 flex-1 flex-col bg-background"
84 data-file-browser-panel={`${environmentId}:${cwd}`}
85 >
86 <div className="flex h-9 shrink-0 items-center gap-2 border-b border-border/60 px-3">
87 <div className="min-w-0 flex-1">
88 <div className="truncate text-xs font-medium text-foreground">{projectName}</div>
89 <div className="truncate text-[10px] leading-none text-muted-foreground">
90 {entriesQuery.isPending && entriesQuery.data === null
91 ? "Indexing…"
92 : `${fileCount.toLocaleString()} files`}
93 {entriesQuery.data?.truncated ? " · partial" : ""}
94 </div>
95 </div>
96 <button
97 type="button"
98 className="rounded-md p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground"
99 aria-label="Search workspace files"
100 onClick={() => model.openSearch()}
101 >
102 <Search className="size-3.5" />
103 </button>
104 <button
105 type="button"
106 className="rounded-md p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground"
107 aria-label="Refresh workspace files"
108 onClick={entriesQuery.refresh}
109 >
110 <RefreshCw className={cn("size-3.5", entriesQuery.isPending && "animate-spin")} />
111 </button>
112 </div>
113 {entriesQuery.error && entriesQuery.data === null ? (
114 <div className="p-4 text-xs leading-relaxed text-destructive">{entriesQuery.error}</div>
115 ) : (
116 <FileTree
117 model={model}
118 aria-label={`${projectName} files`}
119 className="min-h-0 flex-1 overflow-hidden"
120 style={{
121 colorScheme: resolvedTheme,
122 ["--trees-fg-override" as string]: "var(--foreground)",
123 }}
124 />
125 )}
126 </div>
127 );
128}
apps/web/src/components/files/FilePreviewPanel.tsx+298−0
@@ -0,0 +1,298 @@
1import type { EnvironmentId } from "@t3tools/contracts";
2import { Editor } from "@pierre/diffs/editor";
3import { EditorProvider, File, Virtualizer } from "@pierre/diffs/react";
4import { ChevronRight, FolderTree, LoaderCircle } from "lucide-react";
5import { useEffect, useMemo, useRef, useState } from "react";
6
7import { ensureEnvironmentApi } from "~/environmentApi";
8import { useTheme } from "~/hooks/useTheme";
9import { resolveDiffThemeName } from "~/lib/diffRendering";
10import { cn } from "~/lib/utils";
11import { ScrollArea } from "~/components/ui/scroll-area";
12import { Toggle } from "~/components/ui/toggle";
13import { Tooltip, TooltipPopup, TooltipTrigger } from "~/components/ui/tooltip";
14
15import FileBrowserPanel from "./FileBrowserPanel";
16import { projectFileCacheKey } from "./fileContentRevision";
17import { fileBreadcrumbs } from "./filePath";
18import { FileSaveCoordinator } from "./fileSaveCoordinator";
19import {
20 confirmProjectFileQueryData,
21 setProjectFileQueryData,
22 useProjectFileQuery,
23} from "./projectFilesQueryState";
24
25interface FilePreviewPanelProps {
26 environmentId: EnvironmentId;
27 cwd: string;
28 projectName: string;
29 relativePath: string | null;
30 onOpenFile: (relativePath: string) => void;
31 onPendingChange: (relativePath: string, pending: boolean) => void;
32}
33
34const FILE_EXPLORER_STORAGE_KEY = "t3code.fileExplorerOpen";
35const FILE_SAVE_DEBOUNCE_MS = 500;
36
37interface EditableFileSurfaceProps {
38 environmentId: EnvironmentId;
39 cwd: string;
40 relativePath: string;
41 contents: string;
42 resolvedTheme: "light" | "dark";
43 onPendingChange: (relativePath: string, pending: boolean) => void;
44}
45
46function EditableFileSurface({
47 environmentId,
48 cwd,
49 relativePath,
50 contents,
51 resolvedTheme,
52 onPendingChange,
53}: EditableFileSurfaceProps) {
54 const saveCoordinator = useMemo(
55 () =>
56 new FileSaveCoordinator({
57 debounceMs: FILE_SAVE_DEBOUNCE_MS,
58 onPendingChange: (pending) => onPendingChange(relativePath, pending),
59 persist: async (nextContents) => {
60 await ensureEnvironmentApi(environmentId).projects.writeFile({
61 cwd,
62 relativePath,
63 contents: nextContents,
64 });
65 },
66 onConfirmed: (confirmedContents) => {
67 confirmProjectFileQueryData(environmentId, cwd, relativePath, confirmedContents);
68 },
69 }),
70 [cwd, environmentId, onPendingChange, relativePath],
71 );
72 const editor = useMemo(
73 () =>
74 new Editor({
75 onChange: (file) => {
76 setProjectFileQueryData(environmentId, cwd, relativePath, file.contents);
77 saveCoordinator.change(file.contents);
78 },
79 }),
80 [cwd, environmentId, relativePath, saveCoordinator],
81 );
82
83 useEffect(
84 () => () => {
85 editor.cleanUp();
86 saveCoordinator.dispose();
87 },
88 [editor, saveCoordinator],
89 );
90
91 return (
92 <EditorProvider editor={editor}>
93 <Virtualizer
94 className="file-preview-virtualizer min-h-0 flex-1 overflow-auto"
95 config={{
96 overscrollSize: 600,
97 intersectionObserverMargin: 1200,
98 }}
99 >
100 <File
101 file={{
102 name: relativePath,
103 contents,
104 cacheKey: projectFileCacheKey(cwd, relativePath, contents),
105 }}
106 options={{
107 disableFileHeader: true,
108 overflow: "scroll",
109 theme: resolveDiffThemeName(resolvedTheme),
110 themeType: resolvedTheme,
111 }}
112 className="min-h-full"
113 contentEditable
114 />
115 </Virtualizer>
116 </EditorProvider>
117 );
118}
119
120function initialExplorerOpen(): boolean {
121 try {
122 return window.localStorage.getItem(FILE_EXPLORER_STORAGE_KEY) !== "false";
123 } catch {
124 return true;
125 }
126}
127
128export default function FilePreviewPanel({
129 environmentId,
130 cwd,
131 projectName,
132 relativePath,
133 onOpenFile,
134 onPendingChange,
135}: FilePreviewPanelProps) {
136 const { resolvedTheme } = useTheme();
137 const file = useProjectFileQuery(environmentId, cwd, relativePath);
138 const [explorerOpen, setExplorerOpen] = useState(initialExplorerOpen);
139 const breadcrumbRef = useRef<HTMLDivElement>(null);
140 const breadcrumbs = useMemo(
141 () => (relativePath ? fileBreadcrumbs(projectName, relativePath) : []),
142 [projectName, relativePath],
143 );
144
145 useEffect(() => {
146 const currentCrumb = breadcrumbRef.current?.querySelector<HTMLElement>(
147 "[data-current-file-crumb='true']",
148 );
149 currentCrumb?.scrollIntoView({ block: "nearest", inline: "end" });
150 }, [relativePath]);
151
152 const toggleExplorer = () => {
153 setExplorerOpen((current) => {
154 const next = !current;
155 try {
156 window.localStorage.setItem(FILE_EXPLORER_STORAGE_KEY, String(next));
157 } catch {}
158 return next;
159 });
160 };
161
162 return (
163 <div className="flex min-h-0 flex-1 flex-col overflow-hidden bg-background">
164 {relativePath ? (
165 <div className="flex h-11 shrink-0 items-center gap-2 border-y border-border/60 px-3">
166 <ScrollArea
167 ref={breadcrumbRef}
168 hideScrollbars
169 scrollFade
170 className="min-w-0 flex-1 rounded-none"
171 data-file-breadcrumbs
172 >
173 <div className="flex h-full w-max min-w-full items-center text-xs">
174 {breadcrumbs.map((crumb, index) => (
175 <div
176 key={crumb.path || "project"}
177 className="flex min-w-0 shrink-0 items-center"
178 data-current-file-crumb={crumb.kind === "file"}
179 >
180 {index > 0 ? (
181 <ChevronRight className="mx-1 size-3.5 shrink-0 text-muted-foreground/60" />
182 ) : null}
183 <span
184 className={cn(
185 "max-w-40 truncate",
186 crumb.kind === "file"
187 ? "font-medium text-foreground"
188 : "text-muted-foreground",
189 )}
190 title={crumb.path || projectName}
191 >
192 {crumb.label}
193 </span>
194 </div>
195 ))}
196 </div>
197 </ScrollArea>
198 <Tooltip>
199 <TooltipTrigger
200 render={
201 <Toggle
202 className="shrink-0"
203 pressed={explorerOpen}
204 onPressedChange={toggleExplorer}
205 aria-label={explorerOpen ? "Hide file explorer" : "Show file explorer"}
206 variant="ghost"
207 size="sm"
208 >
209 <FolderTree className="size-3.5" />
210 </Toggle>
211 }
212 />
213 <TooltipPopup>
214 {explorerOpen ? "Hide file explorer" : "Show file explorer"}
215 </TooltipPopup>
216 </Tooltip>
217 </div>
218 ) : null}
219 {relativePath && file.data?.truncated ? (
220 <div className="shrink-0 border-b border-amber-500/20 bg-amber-500/8 px-3 py-1.5 text-[11px] text-amber-700 dark:text-amber-300">
221 Preview limited to the first 1 MB of a {file.data.byteLength.toLocaleString()} byte file.
222 </div>
223 ) : null}
224 <div className="flex min-h-0 flex-1 overflow-hidden">
225 <div
226 className={cn(
227 "min-w-0 flex-1 flex-col overflow-hidden",
228 relativePath ? "flex" : "hidden",
229 )}
230 >
231 {relativePath && file.error && file.data === null ? (
232 <div className="flex min-h-0 flex-1 items-center justify-center px-6 text-center text-xs leading-relaxed text-destructive">
233 {file.error}
234 </div>
235 ) : relativePath && file.data === null ? (
236 <div className="flex min-h-0 flex-1 items-center justify-center text-muted-foreground">
237 <LoaderCircle className="size-5 animate-spin" />
238 </div>
239 ) : relativePath && file.data ? (
240 file.data.truncated ? (
241 <Virtualizer
242 key={`${relativePath}:${resolvedTheme}:${file.data.byteLength}`}
243 className="file-preview-virtualizer min-h-0 flex-1 overflow-auto"
244 config={{
245 overscrollSize: 600,
246 intersectionObserverMargin: 1200,
247 }}
248 >
249 <File
250 file={{
251 name: relativePath,
252 contents: file.data.contents,
253 cacheKey: projectFileCacheKey(cwd, relativePath, file.data.contents),
254 }}
255 options={{
256 disableFileHeader: true,
257 overflow: "scroll",
258 theme: resolveDiffThemeName(resolvedTheme),
259 themeType: resolvedTheme,
260 }}
261 className="min-h-full"
262 />
263 </Virtualizer>
264 ) : (
265 <EditableFileSurface
266 key={`${relativePath}:${resolvedTheme}`}
267 environmentId={environmentId}
268 cwd={cwd}
269 relativePath={relativePath}
270 contents={file.data.contents}
271 resolvedTheme={resolvedTheme}
272 onPendingChange={onPendingChange}
273 />
274 )
275 ) : null}
276 </div>
277 {explorerOpen || relativePath === null ? (
278 <aside
279 className={cn(
280 "flex min-h-0 shrink-0 bg-background",
281 relativePath
282 ? "w-[min(22rem,46%)] min-w-64 border-l border-border/60"
283 : "min-w-0 flex-1",
284 )}
285 >
286 <FileBrowserPanel
287 key={`${environmentId}:${cwd}`}
288 environmentId={environmentId}
289 cwd={cwd}
290 projectName={projectName}
291 onOpenFile={onOpenFile}
292 />
293 </aside>
294 ) : null}
295 </div>
296 </div>
297 );
298}
apps/web/src/components/files/fileContentRevision.test.ts+15−0
@@ -0,0 +1,15 @@
1import { describe, expect, it } from "vite-plus/test";
2
3import { fileContentRevision, projectFileCacheKey } from "./fileContentRevision";
4
5describe("fileContentRevision", () => {
6 it("changes for same-length edits", () => {
7 expect(fileContentRevision("nodeVersion")).not.toBe(fileContentRevision("nodeVeasdrs"));
8 });
9
10 it("keeps identical contents stable", () => {
11 expect(projectFileCacheKey("/repo", "file.json", "contents")).toBe(
12 projectFileCacheKey("/repo", "file.json", "contents"),
13 );
14 });
15});
apps/web/src/components/files/fileContentRevision.ts+12−0
@@ -0,0 +1,12 @@
1export function fileContentRevision(contents: string): string {
2 let hash = 2_166_136_261;
3 for (let index = 0; index < contents.length; index += 1) {
4 hash ^= contents.charCodeAt(index);
5 hash = Math.imul(hash, 16_777_619);
6 }
7 return `${contents.length}:${(hash >>> 0).toString(36)}`;
8}
9
10export function projectFileCacheKey(cwd: string, relativePath: string, contents: string): string {
11 return `${cwd}:${relativePath}:${fileContentRevision(contents)}`;
12}
apps/web/src/components/files/filePath.test.ts+23−0
@@ -0,0 +1,23 @@
1import { describe, expect, it } from "vite-plus/test";
2
3import { fileBreadcrumbs } from "./filePath";
4
5describe("fileBreadcrumbs", () => {
6 it("builds project, directory, and file crumbs", () => {
7 expect(fileBreadcrumbs("t3code", "apps/web/src/main.tsx")).toEqual([
8 { label: "t3code", path: "", kind: "project" },
9 { label: "apps", path: "apps", kind: "directory" },
10 { label: "web", path: "apps/web", kind: "directory" },
11 { label: "src", path: "apps/web/src", kind: "directory" },
12 { label: "main.tsx", path: "apps/web/src/main.tsx", kind: "file" },
13 ]);
14 });
15
16 it("normalizes repeated separators", () => {
17 expect(fileBreadcrumbs("workspace", "/src//index.ts").map((crumb) => crumb.label)).toEqual([
18 "workspace",
19 "src",
20 "index.ts",
21 ]);
22 });
23});
apps/web/src/components/files/filePath.ts+17−0
@@ -0,0 +1,17 @@
1export interface FileBreadcrumb {
2 label: string;
3 path: string;
4 kind: "project" | "directory" | "file";
5}
6
7export function fileBreadcrumbs(projectName: string, relativePath: string): FileBreadcrumb[] {
8 const parts = relativePath.split("/").filter(Boolean);
9 return [
10 { label: projectName, path: "", kind: "project" },
11 ...parts.map((part, index) => ({
12 label: part,
13 path: parts.slice(0, index + 1).join("/"),
14 kind: index === parts.length - 1 ? ("file" as const) : ("directory" as const),
15 })),
16 ];
17}
apps/web/src/components/files/fileSaveCoordinator.test.ts+87−0
@@ -0,0 +1,87 @@
1import { afterEach, describe, expect, it, vi } from "vite-plus/test";
2
3import { FileSaveCoordinator } from "./fileSaveCoordinator";
4
5function deferred() {
6 let resolve!: () => void;
7 const promise = new Promise<void>((resolvePromise) => {
8 resolve = resolvePromise;
9 });
10 return { promise, resolve };
11}
12
13describe("FileSaveCoordinator", () => {
14 afterEach(() => {
15 vi.useRealTimers();
16 });
17
18 it("debounces edits and persists only the latest contents", async () => {
19 vi.useFakeTimers();
20 const persist = vi.fn<(contents: string) => Promise<void>>().mockResolvedValue(undefined);
21 const onPendingChange = vi.fn();
22 const onConfirmed = vi.fn();
23 const coordinator = new FileSaveCoordinator({
24 debounceMs: 500,
25 persist,
26 onPendingChange,
27 onConfirmed,
28 });
29
30 coordinator.change("first");
31 await vi.advanceTimersByTimeAsync(300);
32 coordinator.change("latest");
33 await vi.advanceTimersByTimeAsync(499);
34 expect(persist).not.toHaveBeenCalled();
35
36 await vi.advanceTimersByTimeAsync(1);
37 expect(persist).toHaveBeenCalledOnce();
38 expect(persist).toHaveBeenCalledWith("latest");
39 expect(onConfirmed).toHaveBeenCalledWith("latest");
40 expect(onPendingChange.mock.calls).toEqual([[true], [true], [false]]);
41 });
42
43 it("keeps pending state until an edit made during a write is also saved", async () => {
44 vi.useFakeTimers();
45 const firstWrite = deferred();
46 const persist = vi
47 .fn<(contents: string) => Promise<void>>()
48 .mockReturnValueOnce(firstWrite.promise)
49 .mockResolvedValueOnce(undefined);
50 const onPendingChange = vi.fn();
51 const coordinator = new FileSaveCoordinator({
52 debounceMs: 500,
53 persist,
54 onPendingChange,
55 onConfirmed: vi.fn(),
56 });
57
58 coordinator.change("first");
59 await vi.advanceTimersByTimeAsync(500);
60 coordinator.change("latest");
61 await vi.advanceTimersByTimeAsync(500);
62 expect(persist).toHaveBeenCalledTimes(1);
63
64 firstWrite.resolve();
65 await vi.runAllTimersAsync();
66 expect(persist).toHaveBeenCalledTimes(2);
67 expect(persist).toHaveBeenLastCalledWith("latest");
68 expect(onPendingChange.mock.calls.at(-1)).toEqual([false]);
69 });
70
71 it("leaves the file pending when the latest write fails", async () => {
72 vi.useFakeTimers();
73 const onPendingChange = vi.fn();
74 const coordinator = new FileSaveCoordinator({
75 debounceMs: 500,
76 persist: vi.fn().mockRejectedValue(new Error("write failed")),
77 onPendingChange,
78 onConfirmed: vi.fn(),
79 });
80
81 coordinator.change("latest");
82 await vi.advanceTimersByTimeAsync(500);
83 await Promise.resolve();
84 expect(onPendingChange).toHaveBeenCalledWith(true);
85 expect(onPendingChange).not.toHaveBeenCalledWith(false);
86 });
87});
apps/web/src/components/files/fileSaveCoordinator.ts+75−0
@@ -0,0 +1,75 @@
1export interface FileSaveCoordinatorOptions {
2 readonly debounceMs: number;
3 readonly persist: (contents: string) => Promise<void>;
4 readonly onPendingChange: (pending: boolean) => void;
5 readonly onConfirmed: (contents: string) => void;
6}
7
8export class FileSaveCoordinator {
9 private timer: ReturnType<typeof setTimeout> | null = null;
10 private latestContents = "";
11 private latestRevision = 0;
12 private lastChangeAt = 0;
13 private saving = false;
14 private disposed = false;
15
16 constructor(private readonly options: FileSaveCoordinatorOptions) {}
17
18 change(contents: string): void {
19 this.latestContents = contents;
20 this.latestRevision += 1;
21 this.lastChangeAt = Date.now();
22 this.options.onPendingChange(true);
23 this.schedule(this.options.debounceMs);
24 }
25
26 dispose(): void {
27 this.disposed = true;
28 this.clearTimer();
29 if (this.latestRevision > 0) void this.persistLatest();
30 }
31
32 private schedule(delay: number): void {
33 this.clearTimer();
34 this.timer = setTimeout(() => {
35 this.timer = null;
36 void this.persistLatest();
37 }, delay);
38 }
39
40 private clearTimer(): void {
41 if (this.timer === null) return;
42 clearTimeout(this.timer);
43 this.timer = null;
44 }
45
46 private async persistLatest(): Promise<void> {
47 if (this.saving || this.latestRevision === 0) return;
48
49 this.saving = true;
50 const contents = this.latestContents;
51 const revision = this.latestRevision;
52 let succeeded = false;
53 try {
54 await this.options.persist(contents);
55 succeeded = true;
56 this.options.onConfirmed(contents);
57 } catch {}
58
59 this.saving = false;
60 if (revision === this.latestRevision) {
61 if (succeeded) this.options.onPendingChange(false);
62 return;
63 }
64
65 const remainingDebounce = Math.max(
66 0,
67 this.options.debounceMs - (Date.now() - this.lastChangeAt),
68 );
69 if (this.disposed) {
70 void this.persistLatest();
71 } else {
72 this.schedule(remainingDebounce);
73 }
74 }
75}
apps/web/src/components/files/projectFilesQueryState.test.ts+129−0
@@ -0,0 +1,129 @@
1import type {
2 EnvironmentApi,
3 ProjectListEntriesResult,
4 ProjectReadFileResult,
5} from "@t3tools/contracts";
6import { EnvironmentId } from "@t3tools/contracts";
7import * as Option from "effect/Option";
8import { AsyncResult, AtomRegistry } from "effect/unstable/reactivity";
9import { afterEach, describe, expect, it, vi } from "vite-plus/test";
10
11import {
12 __resetEnvironmentApiOverridesForTests,
13 __setEnvironmentApiOverrideForTests,
14} from "~/environmentApi";
15import { appAtomRegistry } from "~/rpc/atomRegistry";
16
17import {
18 __resetProjectFileQueryDataForTests,
19 confirmProjectFileQueryData,
20 getProjectEntriesQueryAtom,
21 getProjectFileQueryAtom,
22 resolveProjectFileQueryData,
23 setProjectFileQueryData,
24} from "./projectFilesQueryState";
25
26const environmentId = EnvironmentId.make("environment-project-files-query-test");
27
28function deferred<A>() {
29 let resolve!: (value: A) => void;
30 const promise = new Promise<A>((resolvePromise) => {
31 resolve = resolvePromise;
32 });
33 return { promise, resolve };
34}
35
36describe("project files queries", () => {
37 afterEach(() => {
38 __resetProjectFileQueryDataForTests();
39 __resetEnvironmentApiOverridesForTests();
40 vi.unstubAllGlobals();
41 });
42
43 it("retains cached entries while explicitly revalidating", async () => {
44 vi.stubGlobal("window", {});
45 const first = {
46 entries: [{ path: "README.md", kind: "file" }],
47 truncated: false,
48 } satisfies ProjectListEntriesResult;
49 const second = {
50 entries: [
51 { path: "README.md", kind: "file" },
52 { path: "src", kind: "directory" },
53 ],
54 truncated: false,
55 } satisfies ProjectListEntriesResult;
56 const revalidation = deferred<ProjectListEntriesResult>();
57 const listEntries = vi
58 .fn<EnvironmentApi["projects"]["listEntries"]>()
59 .mockResolvedValueOnce(first)
60 .mockReturnValueOnce(revalidation.promise);
61 __setEnvironmentApiOverrideForTests(environmentId, {
62 projects: { listEntries },
63 } as unknown as EnvironmentApi);
64 const registry = AtomRegistry.make();
65 const atom = getProjectEntriesQueryAtom(environmentId, "/repo");
66
67 registry.get(atom);
68 await vi.waitFor(() => {
69 expect(Option.getOrNull(AsyncResult.value(registry.get(atom)))).toEqual(first);
70 });
71
72 registry.refresh(atom);
73 await vi.waitFor(() => expect(listEntries).toHaveBeenCalledTimes(2));
74 const refreshing = registry.get(atom);
75 expect(refreshing.waiting).toBe(true);
76 expect(Option.getOrNull(AsyncResult.value(refreshing))).toEqual(first);
77
78 revalidation.resolve(second);
79 await vi.waitFor(() => {
80 expect(Option.getOrNull(AsyncResult.value(registry.get(atom)))).toEqual(second);
81 });
82 registry.dispose();
83 });
84
85 it("keeps the latest optimistic draft when an older write finishes", async () => {
86 vi.stubGlobal("window", {});
87 const initial = {
88 relativePath: "convex.json",
89 contents: '{"nodeVersion":"20"}',
90 byteLength: 20,
91 truncated: false,
92 } satisfies ProjectReadFileResult;
93 const readFile = vi.fn<EnvironmentApi["projects"]["readFile"]>().mockResolvedValue(initial);
94 __setEnvironmentApiOverrideForTests(environmentId, {
95 projects: { readFile },
96 } as unknown as EnvironmentApi);
97 const atom = getProjectFileQueryAtom(environmentId, "/repo", "convex.json");
98
99 appAtomRegistry.get(atom);
100 await vi.waitFor(() => {
101 expect(Option.getOrNull(AsyncResult.value(appAtomRegistry.get(atom)))).toEqual(initial);
102 });
103
104 setProjectFileQueryData(environmentId, "/repo", "convex.json", '{"nodeVersion":"220"}');
105 setProjectFileQueryData(environmentId, "/repo", "convex.json", '{"nodeVersion":"22"}');
106
107 expect(
108 confirmProjectFileQueryData(environmentId, "/repo", "convex.json", '{"nodeVersion":"220"}'),
109 ).toBe(false);
110
111 expect(
112 resolveProjectFileQueryData(
113 environmentId,
114 "/repo",
115 "convex.json",
116 Option.getOrNull(AsyncResult.value(appAtomRegistry.get(atom))),
117 ),
118 ).toEqual({
119 relativePath: "convex.json",
120 contents: '{"nodeVersion":"22"}',
121 byteLength: 20,
122 truncated: false,
123 });
124
125 expect(
126 confirmProjectFileQueryData(environmentId, "/repo", "convex.json", '{"nodeVersion":"22"}'),
127 ).toBe(true);
128 });
129});
apps/web/src/components/files/projectFilesQueryState.ts+206−0
@@ -0,0 +1,206 @@
1import { useAtomValue } from "@effect/atom-react";
2import type {
3 EnvironmentId,
4 ProjectListEntriesResult,
5 ProjectReadFileResult,
6} from "@t3tools/contracts";
7import * as Cause from "effect/Cause";
8import * as Data from "effect/Data";
9import * as Effect from "effect/Effect";
10import * as Option from "effect/Option";
11import { AsyncResult, Atom } from "effect/unstable/reactivity";
12import { useCallback, useEffect } from "react";
13
14import { ensureEnvironmentApi } from "~/environmentApi";
15import { appAtomRegistry } from "~/rpc/atomRegistry";
16
17const PROJECT_QUERY_STALE_TIME_MS = 30_000;
18const PROJECT_QUERY_IDLE_TTL_MS = 5 * 60_000;
19const EMPTY_PROJECT_FILE_PATH = "";
20interface OptimisticProjectFile {
21 readonly data: ProjectReadFileResult;
22 readonly confirmed: boolean;
23}
24
25const optimisticProjectFiles = new Map<string, OptimisticProjectFile>();
26
27class ProjectQueryError extends Data.TaggedError("ProjectQueryError")<{
28 readonly message: string;
29 readonly cause?: unknown;
30}> {}
31
32function queryError(message: string, cause: unknown): ProjectQueryError {
33 return new ProjectQueryError({ message, cause });
34}
35
36function entriesKey(environmentId: EnvironmentId, cwd: string): string {
37 return [environmentId, cwd].map(encodeURIComponent).join("|");
38}
39
40function fileKey(environmentId: EnvironmentId, cwd: string, relativePath: string): string {
41 return [environmentId, cwd, relativePath].map(encodeURIComponent).join("|");
42}
43
44function keyParts(key: string): string[] {
45 return key.split("|").map(decodeURIComponent);
46}
47
48const projectEntriesQueryAtom = Atom.family((key: string) =>
49 Atom.make(
50 Effect.tryPromise({
51 try: () => {
52 const [environmentId, cwd] = keyParts(key) as [EnvironmentId, string];
53 return ensureEnvironmentApi(environmentId).projects.listEntries({ cwd });
54 },
55 catch: (cause) => queryError("Could not load workspace files.", cause),
56 }),
57 ).pipe(
58 Atom.swr({
59 staleTime: PROJECT_QUERY_STALE_TIME_MS,
60 revalidateOnMount: true,
61 }),
62 Atom.setIdleTTL(PROJECT_QUERY_IDLE_TTL_MS),
63 Atom.withLabel(`projects:entries:${key}`),
64 ),
65);
66
67const projectFileQueryAtom = Atom.family((key: string) =>
68 Atom.make(
69 Effect.tryPromise({
70 try: () => {
71 const [environmentId, cwd, relativePath] = keyParts(key) as [EnvironmentId, string, string];
72 if (relativePath === EMPTY_PROJECT_FILE_PATH) return Promise.resolve(null);
73 return ensureEnvironmentApi(environmentId).projects.readFile({ cwd, relativePath });
74 },
75 catch: (cause) => queryError("Could not read workspace file.", cause),
76 }),
77 ).pipe(
78 Atom.swr({
79 staleTime: PROJECT_QUERY_STALE_TIME_MS,
80 revalidateOnMount: true,
81 }),
82 Atom.setIdleTTL(PROJECT_QUERY_IDLE_TTL_MS),
83 Atom.withLabel(`projects:file:${key}`),
84 ),
85);
86
87interface ProjectQueryState<A> {
88 readonly data: A | null;
89 readonly error: string | null;
90 readonly isPending: boolean;
91 readonly refresh: () => void;
92}
93
94export function getProjectEntriesQueryAtom(environmentId: EnvironmentId, cwd: string) {
95 return projectEntriesQueryAtom(entriesKey(environmentId, cwd));
96}
97
98export function getProjectFileQueryAtom(
99 environmentId: EnvironmentId,
100 cwd: string,
101 relativePath: string | null,
102) {
103 return projectFileQueryAtom(fileKey(environmentId, cwd, relativePath ?? EMPTY_PROJECT_FILE_PATH));
104}
105
106export function setProjectFileQueryData(
107 environmentId: EnvironmentId,
108 cwd: string,
109 relativePath: string,
110 contents: string,
111): void {
112 const key = fileKey(environmentId, cwd, relativePath);
113 optimisticProjectFiles.set(key, {
114 confirmed: false,
115 data: {
116 relativePath,
117 contents,
118 byteLength: new TextEncoder().encode(contents).byteLength,
119 truncated: false,
120 },
121 });
122}
123
124export function confirmProjectFileQueryData(
125 environmentId: EnvironmentId,
126 cwd: string,
127 relativePath: string,
128 contents: string,
129): boolean {
130 const key = fileKey(environmentId, cwd, relativePath);
131 const optimisticFile = optimisticProjectFiles.get(key);
132 if (optimisticFile?.data.contents !== contents) return false;
133
134 optimisticProjectFiles.set(key, { ...optimisticFile, confirmed: true });
135 appAtomRegistry.refresh(getProjectFileQueryAtom(environmentId, cwd, relativePath));
136 return true;
137}
138
139export function resolveProjectFileQueryData(
140 environmentId: EnvironmentId,
141 cwd: string,
142 relativePath: string | null,
143 data: ProjectReadFileResult | null,
144): ProjectReadFileResult | null {
145 if (relativePath === null) return data;
146 return optimisticProjectFiles.get(fileKey(environmentId, cwd, relativePath))?.data ?? data;
147}
148
149export function __resetProjectFileQueryDataForTests(): void {
150 optimisticProjectFiles.clear();
151}
152
153function errorMessage<A>(result: AsyncResult.AsyncResult<A, unknown>): string | null {
154 if (result._tag !== "Failure") return null;
155 const cause = Cause.squash(result.cause);
156 return cause instanceof Error ? cause.message : "Workspace query failed.";
157}
158
159export function useProjectEntriesQuery(
160 environmentId: EnvironmentId,
161 cwd: string,
162): ProjectQueryState<ProjectListEntriesResult> {
163 const atom = getProjectEntriesQueryAtom(environmentId, cwd);
164 const result = useAtomValue(atom);
165 const refresh = useCallback(() => appAtomRegistry.refresh(atom), [atom]);
166 return {
167 data: Option.getOrNull(AsyncResult.value(result)),
168 error: errorMessage(result),
169 isPending: result.waiting,
170 refresh,
171 };
172}
173
174export function useProjectFileQuery(
175 environmentId: EnvironmentId,
176 cwd: string,
177 relativePath: string | null,
178): ProjectQueryState<ProjectReadFileResult> {
179 const atom = getProjectFileQueryAtom(environmentId, cwd, relativePath);
180 const result = useAtomValue(atom);
181 const refresh = useCallback(() => appAtomRegistry.refresh(atom), [atom]);
182 const data = Option.getOrNull(AsyncResult.value(result));
183 const optimisticFile =
184 relativePath === null
185 ? undefined
186 : optimisticProjectFiles.get(fileKey(environmentId, cwd, relativePath));
187
188 useEffect(() => {
189 if (
190 relativePath === null ||
191 optimisticFile === undefined ||
192 !optimisticFile.confirmed ||
193 data?.contents !== optimisticFile.data.contents
194 ) {
195 return;
196 }
197 optimisticProjectFiles.delete(fileKey(environmentId, cwd, relativePath));
198 }, [cwd, data?.contents, environmentId, optimisticFile, relativePath]);
199
200 return {
201 data: optimisticFile?.data ?? data,
202 error: errorMessage(result),
203 isPending: result.waiting,
204 refresh,
205 };
206}
apps/web/src/components/preview/PreviewPanelShell.tsx+13−4
@@ -21,7 +21,11 @@ const PREVIEW_PANEL_DEFAULT_WIDTH = 540; · 20 unmodified lines
21 * via a drag handle on the left edge; width persists per browser. In21 * via a drag handle on the left edge; width persists per browser. In
22 * sheet/sidebar modes the parent owns the size.22 * sheet/sidebar modes the parent owns the size.
23 */23 */
24export function PreviewPanelShell(props: { mode: PreviewPanelMode; children: ReactNode }) {24export function PreviewPanelShell(props: {
25 mode: PreviewPanelMode;
26 maximized?: boolean;
27 children: ReactNode;
28}) {
25 const useDragRegion = isElectron && props.mode !== "sheet" && props.mode !== "embedded";29 const useDragRegion = isElectron && props.mode !== "sheet" && props.mode !== "embedded";
26 const isInline = props.mode === "inline";30 const isInline = props.mode === "inline";
27 const maxWidth = useViewportClampedMaxWidth();31 const maxWidth = useViewportClampedMaxWidth();
@@ -37,12 +41,17 @@ export function PreviewPanelShell(props: { mode: PreviewPanelMode; children: Rea · 9 unmodified lines
37 <div41 <div
38 className={cn(42 className={cn(
39 "relative flex h-full min-h-0 min-w-0 flex-col self-stretch bg-background",43 "relative flex h-full min-h-0 min-w-0 flex-col self-stretch bg-background",
40 isInline ? "shrink-0 border-l border-border" : "w-full",44 isInline
45 ? props.maximized
46 ? "flex-1 border-l border-border"
47 : "shrink-0 border-l border-border"
48 : "w-full",
41 )}49 )}
42 style={isInline ? { width: `${width}px` } : undefined}50 style={isInline && !props.maximized ? { width: `${width}px` } : undefined}
43 data-preview-panel-mode={props.mode}51 data-preview-panel-mode={props.mode}
52 data-preview-panel-maximized={props.maximized ? "true" : "false"}
44 >53 >
45 {isInline ? <RightPanelResizeHandle handlers={handlers} /> : null}54 {isInline && !props.maximized ? <RightPanelResizeHandle handlers={handlers} /> : null}
46 {useDragRegion ? <div className="electron-drag-region h-0 w-full" aria-hidden /> : null}55 {useDragRegion ? <div className="electron-drag-region h-0 w-full" aria-hidden /> : null}
47 {props.children}56 {props.children}
48 </div>57 </div>
apps/web/src/environmentApi.ts+2−0
@@ -19,6 +19,8 @@ export function createEnvironmentApi(rpcClient: WsRpcClient): EnvironmentApi { · 18 unmodified lines
19 onMetadata: (callback, options) => rpcClient.terminal.onMetadata(callback, options),19 onMetadata: (callback, options) => rpcClient.terminal.onMetadata(callback, options),
20 },20 },
21 projects: {21 projects: {
22 listEntries: rpcClient.projects.listEntries,
23 readFile: rpcClient.projects.readFile,
22 searchEntries: rpcClient.projects.searchEntries,24 searchEntries: rpcClient.projects.searchEntries,
23 writeFile: rpcClient.projects.writeFile,25 writeFile: rpcClient.projects.writeFile,
24 },26 },
apps/web/src/environments/runtime/service.threadSubscriptions.test.ts+2−0
@@ -121,6 +121,8 @@ vi.mock("@t3tools/client-runtime", async (importOriginal) => { · 120 unmodified lines
121 subscribePorts: vi.fn(() => () => undefined),121 subscribePorts: vi.fn(() => () => undefined),
122 },122 },
123 projects: {123 projects: {
124 listEntries: vi.fn(),
125 readFile: vi.fn(),
124 searchEntries: vi.fn(),126 searchEntries: vi.fn(),
125 writeFile: vi.fn(),127 writeFile: vi.fn(),
126 },128 },
apps/web/src/index.css+43−0
@@ -4,6 +4,30 @@ · 3 unmodified lines
4/* Window Controls Overlay: active when Electron exposes native titlebar control geometry. */4/* Window Controls Overlay: active when Electron exposes native titlebar control geometry. */
5@custom-variant wco (&:is(.wco, .wco *));5@custom-variant wco (&:is(.wco, .wco *));
6 6
7:root {
8 --workspace-topbar-height: 52px;
9 --workspace-controls-top: 0px;
10 --workspace-controls-right: calc(env(safe-area-inset-right) + 0.75rem);
11 --workspace-native-controls-inset: 0px;
12}
13
14@media (width >= 40rem) {
15 :root {
16 --workspace-controls-right: calc(env(safe-area-inset-right) + 1.25rem);
17 }
18}
19
20.wco {
21 --workspace-topbar-height: env(titlebar-area-height, 52px);
22 --workspace-controls-top: env(titlebar-area-y, 0px);
23 --workspace-controls-right: calc(
24 100vw - env(titlebar-area-width, 100vw) - env(titlebar-area-x, 0px) + 1em
25 );
26 --workspace-native-controls-inset: calc(
27 100vw - env(titlebar-area-width, 100vw) - env(titlebar-area-x, 0px) + 1em
28 );
29}
30
7@theme inline {31@theme inline {
8 --animate-skeleton: skeleton 2s -1s infinite linear;32 --animate-skeleton: skeleton 2s -1s infinite linear;
9 --font-sans:33 --font-sans:
@@ -71,6 +95,25 @@ · 61 unmodified lines
71 }95 }
72}96}
73 97
98@layer components {
99 .workspace-topbar {
100 display: flex;
101 height: var(--workspace-topbar-height);
102 min-height: var(--workspace-topbar-height);
103 flex-shrink: 0;
104 align-items: center;
105 }
106
107 .workspace-titlebar-controls {
108 position: absolute;
109 top: var(--workspace-controls-top);
110 right: var(--workspace-controls-right);
111 display: flex;
112 height: var(--workspace-topbar-height);
113 align-items: center;
114 }
115}
116
74/* Safe-area inset utilities for surfaces that opt into edge-to-edge rendering.117/* Safe-area inset utilities for surfaces that opt into edge-to-edge rendering.
75 Insets resolve to 0 when the browser already constrains the layout viewport118 Insets resolve to 0 when the browser already constrains the layout viewport
76 to the safe area. */119 to the safe area. */
apps/web/src/localApi.test.ts+2−0
@@ -55,6 +55,8 @@ const rpcClientMock = { · 54 unmodified lines
55 ),55 ),
56 },56 },
57 projects: {57 projects: {
58 listEntries: vi.fn(),
59 readFile: vi.fn(),
58 searchEntries: vi.fn(),60 searchEntries: vi.fn(),
59 writeFile: vi.fn(),61 writeFile: vi.fn(),
60 },62 },
apps/web/src/markdown-links.test.ts+9−0
@@ -83,6 +83,7 @@ describe("resolveMarkdownFileLinkTarget", () => { · 82 unmodified lines
83 ),83 ),
84 ).toMatchObject({84 ).toMatchObject({
85 displayPath: "t3code/apps/web/src/session-logic.ts:501",85 displayPath: "t3code/apps/web/src/session-logic.ts:501",
86 workspaceRelativePath: "apps/web/src/session-logic.ts",
86 });87 });
87 });88 });
88 89
@@ -95,6 +96,14 @@ describe("resolveMarkdownFileLinkTarget", () => { · 6 unmodified lines
95 ).toMatchObject({96 ).toMatchObject({
96 displayPath:97 displayPath:
97 "t3code/apps/web/src/components/chat/MessagesTimeline.virtualization.browser.tsx",98 "t3code/apps/web/src/components/chat/MessagesTimeline.virtualization.browser.tsx",
99 workspaceRelativePath:
100 "apps/web/src/components/chat/MessagesTimeline.virtualization.browser.tsx",
101 });
102 });
103
104 it("does not create a preview path for files outside the workspace", () => {
105 expect(resolveMarkdownFileLinkMeta("/tmp/report.ts", "/repo/project")).toMatchObject({
106 workspaceRelativePath: null,
98 });107 });
99 });108 });
100 109
apps/web/src/markdown-links.ts+15−0
@@ -26,6 +26,7 @@ export interface MarkdownFileLinkMeta { · 25 unmodified lines
26 filePath: string;26 filePath: string;
27 targetPath: string;27 targetPath: string;
28 displayPath: string;28 displayPath: string;
29 workspaceRelativePath: string | null;
29 basename: string;30 basename: string;
30 line?: number;31 line?: number;
31 column?: number;32 column?: number;
@@ -174,6 +175,19 @@ function basenameOfPath(path: string): string { · 142 unmodified lines
174 return separatorIndex >= 0 ? path.slice(separatorIndex + 1) : path;175 return separatorIndex >= 0 ? path.slice(separatorIndex + 1) : path;
175}176}
176 177
178function workspaceRelativePath(path: string, workspaceRoot: string | undefined): string | null {
179 if (!workspaceRoot) return null;
180 const normalizedPath = normalizeWindowsDrivePath(path.replaceAll("\\", "/"));
181 const normalizedRoot = normalizeWindowsDrivePath(workspaceRoot.replaceAll("\\", "/")).replace(
182 /\/+$/,
183 "",
184 );
185 const pathForCompare = normalizedPath.toLowerCase();
186 const rootForCompare = normalizedRoot.toLowerCase();
187 if (!pathForCompare.startsWith(`${rootForCompare}/`)) return null;
188 return normalizedPath.slice(normalizedRoot.length + 1);
189}
190
177export function resolveMarkdownFileLinkMeta(191export function resolveMarkdownFileLinkMeta(
178 href: string | undefined,192 href: string | undefined,
179 cwd?: string,193 cwd?: string,
@@ -191,6 +205,7 @@ export function resolveMarkdownFileLinkMeta( · 11 unmodified lines
191 filePath: path,205 filePath: path,
192 targetPath,206 targetPath,
193 displayPath: formatWorkspaceRelativePath(targetPath, cwd),207 displayPath: formatWorkspaceRelativePath(targetPath, cwd),
208 workspaceRelativePath: workspaceRelativePath(path, cwd),
194 basename: basenameOfPath(path),209 basename: basenameOfPath(path),
195 ...(lineNumber !== undefined ? { line: lineNumber } : {}),210 ...(lineNumber !== undefined ? { line: lineNumber } : {}),
196 ...(columnNumber !== undefined ? { column: columnNumber } : {}),211 ...(columnNumber !== undefined ? { column: columnNumber } : {}),
apps/web/src/pierre-icons.test.ts+57−0
@@ -0,0 +1,57 @@
1import { assert, describe, it } from "vite-plus/test";
2
3import {
4 hasSpecificPierreIconForFileName,
5 resolvePierreIconForEntry,
6 syntheticFileNameForLanguageId,
7 T3_PIERRE_ICONS,
8} from "./pierre-icons";
9
10describe("Pierre file icons", () => {
11 it("uses Pierre exact filename and complete-set extension mappings", () => {
12 assert.equal(resolvePierreIconForEntry("Dockerfile", "file")?.token, "docker");
13 assert.equal(resolvePierreIconForEntry("src/Button.tsx", "file")?.token, "react");
14 assert.equal(resolvePierreIconForEntry("vite.config.ts", "file")?.token, "vite");
15 });
16
17 it("extends Pierre with T3-specific exact filename icons", () => {
18 assert.equal(
19 resolvePierreIconForEntry("package.json", "file")?.name,
20 "t3-file-icon-package-json",
21 );
22 assert.equal(
23 resolvePierreIconForEntry("config/tsconfig.json", "file")?.name,
24 "t3-file-icon-tsconfig",
25 );
26 assert.equal(resolvePierreIconForEntry("AGENTS.md", "file")?.name, "t3-file-icon-agents");
27 assert.equal(resolvePierreIconForEntry("CLAUDE.md", "file")?.name, "t3-file-icon-claude");
28 assert.equal(resolvePierreIconForEntry("README.md", "file")?.name, "t3-file-icon-readme");
29 assert.equal(resolvePierreIconForEntry("pnpm-lock.yaml", "file")?.name, "t3-file-icon-pnpm");
30 assert.equal(
31 resolvePierreIconForEntry("pnpm-workspace.yaml", "file")?.name,
32 "t3-file-icon-pnpm",
33 );
34 });
35
36 it("ships every custom icon referenced by the extended resolver", () => {
37 const customIconNames = new Set(Object.values(T3_PIERRE_ICONS.byFileName));
38 for (const iconName of customIconNames) {
39 assert.include(T3_PIERRE_ICONS.spriteSheet, `id="${iconName}"`);
40 }
41 });
42
43 it("uses the Pierre default icon for unknown file types", () => {
44 assert.equal(resolvePierreIconForEntry("artifact.unknown-ext", "file")?.token, "default");
45 assert.isFalse(hasSpecificPierreIconForFileName("artifact.unknown-ext"));
46 });
47
48 it("leaves directory rendering to the shared folder fallback", () => {
49 assert.isNull(resolvePierreIconForEntry("packages/client-runtime", "directory"));
50 });
51
52 it("normalizes common markdown fence language aliases", () => {
53 assert.equal(syntheticFileNameForLanguageId("typescript"), "file.ts");
54 assert.equal(syntheticFileNameForLanguageId("shellscript"), "file.sh");
55 assert.equal(syntheticFileNameForLanguageId("python"), "file.py");
56 });
57});
apps/web/src/pierre-icons.ts+117−0
@@ -0,0 +1,117 @@
1import {
2 createFileTreeIconResolver,
3 getBuiltInSpriteSheet,
4 type FileTreeIcons,
5} from "@pierre/trees";
6
7export interface PierreIconResolution {
8 name: string;
9 token?: string;
10}
11
12const PIERRE_ICON_SPRITE_ID = "t3code-pierre-file-icon-sprite";
13
14const T3_FILE_ICON_SPRITE = `
15<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" aria-hidden="true">
16 <symbol id="t3-file-icon-package-json" viewBox="0 0 32 32">
17 <path d="M2 2H30V30H2" fill="#c12127" />
18 <path d="M7.25 7.25h17.5v17.5h-3.5v-14H16v14H7.25" fill="#fff" />
19 </symbol>
20 <symbol id="t3-file-icon-tsconfig" viewBox="0 0 32 32">
21 <path d="M23.827 8.243a4.424 4.424 0 0 1 2.223 1.281 5.853 5.853 0 0 1 .852 1.143c.011.045-1.534 1.083-2.471 1.662-.034.023-.169-.124-.322-.35a2.014 2.014 0 0 0-1.67-1c-1.077-.074-1.771.49-1.766 1.433a1.3 1.3 0 0 0 .153.666c.237.49.677.784 2.059 1.383 2.544 1.1 3.636 1.817 4.31 2.843a5.158 5.158 0 0 1 .416 4.333 4.764 4.764 0 0 1-3.932 2.815 10.9 10.9 0 0 1-2.708-.028 6.531 6.531 0 0 1-3.616-1.884 6.278 6.278 0 0 1-.926-1.371 2.655 2.655 0 0 1 .327-.208c.158-.09.756-.434 1.32-.761l1.024-.6.214.312a4.771 4.771 0 0 0 1.35 1.292 3.3 3.3 0 0 0 3.458-.175 1.545 1.545 0 0 0 .2-1.974c-.276-.4-.84-.727-2.443-1.422a8.8 8.8 0 0 1-3.349-2.055 4.687 4.687 0 0 1-.976-1.777 7.116 7.116 0 0 1-.062-2.268 4.332 4.332 0 0 1 3.644-3.374 9 9 0 0 1 2.71.01ZM15.484 9.726l.011 1.454h-4.63v13.148H7.6V11.183H2.97V9.755a13.986 13.986 0 0 1 .04-1.466c.017-.023 2.832-.034 6.245-.028l6.211.017Z" fill="#007acc" />
22 <path d="m27.075 25.107.363-.361c1.68.055 1.706 0 1.78-.177l.462-1.124.034-.107-.038-.093c-.02-.049-.081-.2-1.13-1.2v-.526c1.211-1.166 1.185-1.226 1.116-1.4l-.46-1.136c-.069-.17-.1-.237-1.763-.191l-.364-.367a8.138 8.138 0 0 0-.057-1.657l-.047-.106-1.2-.525c-.177-.081-.239-.11-1.372 1.124l-.509-.008c-1.167-1.245-1.222-1.223-1.4-1.152l-1.115.452c-.175.071-.236.1-.169 1.79l-.36.359c-1.68-.055-1.7 0-1.778.177L18.606 20l-.036.108.038.094c.02.048.078.194 1.13 1.2v.525c-1.211 1.166-1.184 1.226-1.115 1.4l.459 1.137c.07.174.1.236 1.763.192l.363.377a8.169 8.169 0 0 0 .055 1.654l.047.107 1.208.528c.176.073.236.1 1.366-1.13l.509.006c1.168 1.247 1.228 1.223 1.4 1.154l1.113-.45c.176-.075.237-.102.169-1.795Zm-4.788-2.632a2 2 0 1 1 2.618 1.14 2.023 2.023 0 0 1-2.618-1.14Z" fill="#99b8c4" />
23 </symbol>
24 <symbol id="t3-file-icon-agents" viewBox="0 0 32 32">
25 <path fill="currentColor" d="M27.2 16c0-6.19-5.01-11.2-11.2-11.2C9.81 4.8 4.8 9.81 4.8 16S9.81 27.2 16 27.2c6.19 0 11.2-5.01 11.2-11.2Zm-5.6 2.1a1.4 1.4 0 1 1 0 2.8h-4.2a1.4 1.4 0 1 1 0-2.8Zm-11.2-6.8c.622-.373 1.42-.208 1.84.361l.079.119 2.1 3.5.088.171c.15.351.15.748 0 1.1l-.088.171-2.1 3.5a1.4 1.4 0 0 1-2.4-1.44L11.59 16l-1.67-2.78-.067-.127c-.302-.642-.075-1.42.547-1.79ZM30 16c0 7.73-6.27 14-14 14S2 23.73 2 16 8.27 2 16 2s14 6.27 14 14Z" />
26 </symbol>
27 <symbol id="t3-file-icon-claude" viewBox="0 0 32 32">
28 <path fill="#d97757" d="m7.5 20.61 5.5-3.08.1-.27-.1-.15h-.27l-.92-.06a234.2 234.2 0 0 1-8.51-.34l-.67-.14-.62-.82.06-.41.56-.38.8.07c3.08.2 6.15.4 9.21.72h.46l.06-.19-.16-.1-.12-.12-2.74-1.86c-2.04-1.3-3.55-2.43-5.38-3.68l-.43-.54-.18-1.18.76-.84 1.03.07.26.07c2.03 1.6 4.1 3.14 6.17 4.66l.43.36.17-.12.02-.09-.2-.32c-1.43-2.6-2.55-4.62-4-6.96l-.2-.72c-.08-.3-.13-.55-.13-.85l.87-1.18.49-.16 1.16.16.49.42c1.4 3.31 2.76 5.93 4.23 8.83l.29.97.1.3h.19v-.17c.65-3.42 0-6.57 1.22-9.53l.87-.57.68.33.56.8-.08.51c-.37 2.62-.92 5.22-1.4 7.82h.24l.29-.29a68.66 68.66 0 0 1 4.91-5.94l.64-.5h1.2l.89 1.32-.4 1.36a53.45 53.45 0 0 0-4.66 6.47l.09.13.22-.02c2.4-.57 4.84-.99 7.27-1.4l.97.45.1.46-.37.94c-3 .72-6 1.34-9 2.05l-.05.04.06.07c2.7.26 5.62.3 7.99.47l.92.61.55.75-.1.56-1.4.73c-2.93-.7-5.19-1.22-7.91-1.9h-.22v.13c2.18 2.12 4.6 4.27 6.54 6.07l.15.68-.38.53-.4-.06c-2.04-1.43-3.9-3.09-5.8-4.7h-.15v.2l.52.76c1.14 1.79 2.64 3.25 2.87 5.37l-.2.41-.7.25-.78-.14a73.16 73.16 0 0 1-4.58-7.04l-.17.09-.78 8.46-.37.43-.85.33-.71-.54-.38-.87a114 114 0 0 0 1.53-7.97l.2-.73-.01-.05-.16.02a76.67 76.67 0 0 1-6.23 7.88l-.48.2-.84-.44.08-.77.47-.69c2.07-2.57 3.54-4.66 5.54-7v-.19h-.07l-7.4 4.8-1.31.17-.57-.53.07-.87.27-.28 2.23-1.53Z" />
29 </symbol>
30 <symbol id="t3-file-icon-readme" viewBox="0 0 32 32">
31 <rect x="2.5" y="7.955" width="27" height="16.091" fill="none" stroke="#b48a5a" />
32 <path fill="#b48a5a" d="M5.909 20.636v-9.272h2.727l2.728 3.409 2.727-3.409h2.727v9.272h-2.727v-5.318l-2.727 3.409-2.728-3.409v5.318H5.91Zm17.046 0-4.091-4.5h2.727v-4.772h2.727v4.772h2.727l-4.09 4.5Z" />
33 </symbol>
34 <symbol id="t3-file-icon-pnpm" viewBox="0 0 32 32">
35 <path fill="#f9ad00" d="M30 10.75h-8.749V2H30Zm-9.626 0h-8.75V2h8.75Zm-9.625 0H2V2h8.749ZM30 20.375h-8.749v-8.75H30Z" />
36 <path fill="currentColor" d="M20.374 20.375h-8.75v-8.75h8.75Zm0 9.625h-8.75v-8.75h8.75ZM30 30h-8.749v-8.75H30Zm-19.251 0H2v-8.75h8.749Z" />
37 </symbol>
38</svg>`;
39
40export const T3_PIERRE_ICONS = {
41 set: "complete",
42 colored: true,
43 spriteSheet: T3_FILE_ICON_SPRITE,
44 byFileName: {
45 "package.json": "t3-file-icon-package-json",
46 "tsconfig.json": "t3-file-icon-tsconfig",
47 "agents.md": "t3-file-icon-agents",
48 "claude.md": "t3-file-icon-claude",
49 "readme.md": "t3-file-icon-readme",
50 "pnpm-lock.yaml": "t3-file-icon-pnpm",
51 "pnpm-workspace.yaml": "t3-file-icon-pnpm",
52 },
53} satisfies FileTreeIcons;
54
55const completeIconResolver = createFileTreeIconResolver(T3_PIERRE_ICONS);
56
57const LANGUAGE_EXTENSION_ALIASES: Record<string, string> = {
58 bash: "sh",
59 csharp: "cs",
60 dockerfile: "dockerfile",
61 javascript: "js",
62 jsx: "jsx",
63 markdown: "md",
64 mdx: "mdx",
65 plaintext: "txt",
66 python: "py",
67 ruby: "rb",
68 rust: "rs",
69 shell: "sh",
70 shellscript: "sh",
71 swift: "swift",
72 typescript: "ts",
73 tsx: "tsx",
74 yaml: "yml",
75};
76
77export function basenameOfPath(pathValue: string): string {
78 const slashIndex = pathValue.lastIndexOf("/");
79 return slashIndex === -1 ? pathValue : pathValue.slice(slashIndex + 1);
80}
81
82export function inferEntryKindFromPath(pathValue: string): "file" | "directory" {
83 const base = basenameOfPath(pathValue);
84 if (base.startsWith(".") && !base.slice(1).includes(".")) return "directory";
85 return base.includes(".") ? "file" : "directory";
86}
87
88export function syntheticFileNameForLanguageId(languageId: string): string {
89 const normalized = languageId.toLowerCase();
90 return `file.${LANGUAGE_EXTENSION_ALIASES[normalized] ?? normalized}`;
91}
92
93export function resolvePierreIconForEntry(
94 pathValue: string,
95 kind: "file" | "directory",
96): PierreIconResolution | null {
97 if (kind === "directory") return null;
98 return completeIconResolver.resolveIcon("file-tree-icon-file", pathValue);
99}
100
101export function hasSpecificPierreIconForFileName(fileName: string): boolean {
102 return resolvePierreIconForEntry(fileName, "file")?.token !== "default";
103}
104
105export function ensurePierreIconSprite(): void {
106 if (typeof document === "undefined" || document.getElementById(PIERRE_ICON_SPRITE_ID)) return;
107 const container = document.createElement("div");
108 container.id = PIERRE_ICON_SPRITE_ID;
109 container.setAttribute("aria-hidden", "true");
110 container.style.position = "absolute";
111 container.style.width = "0";
112 container.style.height = "0";
113 container.style.overflow = "hidden";
114 container.style.pointerEvents = "none";
115 container.innerHTML = `${getBuiltInSpriteSheet("complete")}${T3_FILE_ICON_SPRITE}`;
116 document.body.prepend(container);
117}
apps/web/src/rightPanelStore.test.ts+48−0
@@ -88,6 +88,54 @@ describe("rightPanelStore", () => { · 87 unmodified lines
88 ).toHaveLength(2);88 ).toHaveLength(2);
89 });89 });
90 90
91 it("keeps files as a singleton surface", () => {
92 useRightPanelStore.getState().open(refA, "files");
93 useRightPanelStore.getState().open(refA, "files");
94 expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({
95 isOpen: true,
96 activeSurfaceId: "files",
97 surfaces: [{ id: "files", kind: "files" }],
98 });
99 });
100
101 it("replaces the standalone explorer with peer file surfaces", () => {
102 useRightPanelStore.getState().open(refA, "files");
103 useRightPanelStore.getState().openFile(refA, "src/index.ts");
104 useRightPanelStore.getState().openFile(refA, "src/index.ts");
105 useRightPanelStore.getState().openFile(refA, "README.md");
106
107 expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({
108 isOpen: true,
109 activeSurfaceId: "file:README.md",
110 surfaces: [
111 { id: "file:src/index.ts", kind: "file", relativePath: "src/index.ts" },
112 { id: "file:README.md", kind: "file", relativePath: "README.md" },
113 ],
114 });
115 });
116
117 it("removes persisted file surfaces when their workspace no longer exists", () => {
118 useRightPanelStore.getState().openFile(refA, "src/index.ts");
119 useRightPanelStore.getState().open(refA, "plan");
120 useRightPanelStore.getState().openFile(refA, "README.md");
121
122 useRightPanelStore.getState().reconcileFileSurfaces(refA, false);
123
124 expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({
125 isOpen: true,
126 activeSurfaceId: "plan",
127 surfaces: [{ id: "plan", kind: "plan" }],
128 });
129
130 useRightPanelStore.getState().openFile(refB, "conductor.json");
131 useRightPanelStore.getState().reconcileFileSurfaces(refB, false);
132 expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refB)).toEqual({
133 isOpen: false,
134 activeSurfaceId: null,
135 surfaces: [],
136 });
137 });
138
91 it("close hides the panel without clearing its selected surface", () => {139 it("close hides the panel without clearing its selected surface", () => {
92 useRightPanelStore.getState().open(refA, "plan");140 useRightPanelStore.getState().open(refA, "plan");
93 useRightPanelStore.getState().close(refA);141 useRightPanelStore.getState().close(refA);
apps/web/src/rightPanelStore.ts+52−7
@@ -4,8 +4,8 @@ · 3 unmodified lines
4 * This is intentionally a shallow workspace model: it owns an ordered set of4 * This is intentionally a shallow workspace model: it owns an ordered set of
5 * surface descriptors and the active surface, while each feature continues to5 * surface descriptors and the active surface, while each feature continues to
6 * own its durable resource state. Browser surfaces point at preview tab ids,6 * own its durable resource state. Browser surfaces point at preview tab ids,
7 * terminal surfaces point at terminal session ids, and diff/plan remain7 * terminal surfaces point at terminal session ids, file surfaces point at
8 * singleton surfaces.8 * workspace paths, and diff/plan/files remain singleton surfaces.
9 */9 */
10import { scopedThreadKey } from "@t3tools/client-runtime";10import { scopedThreadKey } from "@t3tools/client-runtime";
11import type { ScopedThreadRef } from "@t3tools/contracts";11import type { ScopedThreadRef } from "@t3tools/contracts";
@@ -14,7 +14,7 @@ import { createJSONStorage, persist } from "zustand/middleware"; · 2 unmodified lines
14 14
15import { resolveStorage } from "./lib/storage";15import { resolveStorage } from "./lib/storage";
16 16
17export const RIGHT_PANEL_KINDS = ["plan", "diff", "preview", "terminal"] as const;17export const RIGHT_PANEL_KINDS = ["plan", "diff", "files", "file", "preview", "terminal"] as const;
18export type RightPanelKind = (typeof RIGHT_PANEL_KINDS)[number];18export type RightPanelKind = (typeof RIGHT_PANEL_KINDS)[number];
19 19
20export type RightPanelSurface =20export type RightPanelSurface =
@@ -29,10 +29,12 @@ export type RightPanelSurface = · 8 unmodified lines
29 splitDirection?: "horizontal" | "vertical";29 splitDirection?: "horizontal" | "vertical";
30 }30 }
31 | { id: "diff"; kind: "diff" }31 | { id: "diff"; kind: "diff" }
32 | { id: "files"; kind: "files" }
33 | { id: `file:${string}`; kind: "file"; relativePath: string }
32 | { id: "plan"; kind: "plan" };34 | { id: "plan"; kind: "plan" };
33 35
34const RIGHT_PANEL_STORAGE_KEY = "t3code:right-panel-state:v2";36const RIGHT_PANEL_STORAGE_KEY = "t3code:right-panel-state:v2";
35const RIGHT_PANEL_STORAGE_VERSION = 5;37const RIGHT_PANEL_STORAGE_VERSION = 6;
36 38
37export interface ThreadRightPanelState {39export interface ThreadRightPanelState {
38 isOpen: boolean;40 isOpen: boolean;
@@ -42,8 +44,9 @@ export interface ThreadRightPanelState { · 3 unmodified lines
42 44
43interface RightPanelStoreState {45interface RightPanelStoreState {
44 byThreadKey: Record<string, ThreadRightPanelState>;46 byThreadKey: Record<string, ThreadRightPanelState>;
45 open: (ref: ScopedThreadRef, kind: Exclude<RightPanelKind, "terminal">) => void;47 open: (ref: ScopedThreadRef, kind: Exclude<RightPanelKind, "file" | "terminal">) => void;
46 openBrowser: (ref: ScopedThreadRef, tabId: string | null) => void;48 openBrowser: (ref: ScopedThreadRef, tabId: string | null) => void;
49 openFile: (ref: ScopedThreadRef, relativePath: string) => void;
47 openTerminal: (ref: ScopedThreadRef, terminalId: string) => void;50 openTerminal: (ref: ScopedThreadRef, terminalId: string) => void;
48 splitTerminal: (51 splitTerminal: (
49 ref: ScopedThreadRef,52 ref: ScopedThreadRef,
@@ -56,10 +59,11 @@ interface RightPanelStoreState { · 6 unmodified lines
56 activateSurface: (ref: ScopedThreadRef, surfaceId: string) => void;59 activateSurface: (ref: ScopedThreadRef, surfaceId: string) => void;
57 closeSurface: (ref: ScopedThreadRef, surfaceId: string) => void;60 closeSurface: (ref: ScopedThreadRef, surfaceId: string) => void;
58 reconcileBrowserSurfaces: (ref: ScopedThreadRef, tabIds: readonly string[]) => void;61 reconcileBrowserSurfaces: (ref: ScopedThreadRef, tabIds: readonly string[]) => void;
62 reconcileFileSurfaces: (ref: ScopedThreadRef, workspaceAvailable: boolean) => void;
59 show: (ref: ScopedThreadRef) => void;63 show: (ref: ScopedThreadRef) => void;
60 close: (ref: ScopedThreadRef) => void;64 close: (ref: ScopedThreadRef) => void;
61 toggleVisibility: (ref: ScopedThreadRef) => void;65 toggleVisibility: (ref: ScopedThreadRef) => void;
62 toggle: (ref: ScopedThreadRef, kind: Exclude<RightPanelKind, "terminal">) => void;66 toggle: (ref: ScopedThreadRef, kind: Exclude<RightPanelKind, "file" | "terminal">) => void;
63 removeThread: (ref: ScopedThreadRef) => void;67 removeThread: (ref: ScopedThreadRef) => void;
64}68}
65 69
@@ -70,11 +74,13 @@ const EMPTY_THREAD_STATE: ThreadRightPanelState = { · 4 unmodified lines
70};74};
71 75
72const singletonSurface = (76const singletonSurface = (
73 kind: Exclude<RightPanelKind, "preview" | "terminal">,77 kind: Exclude<RightPanelKind, "file" | "preview" | "terminal">,
74): RightPanelSurface => {78): RightPanelSurface => {
75 switch (kind) {79 switch (kind) {
76 case "diff":80 case "diff":
77 return { id: "diff", kind };81 return { id: "diff", kind };
82 case "files":
83 return { id: "files", kind };
78 case "plan":84 case "plan":
79 return { id: "plan", kind };85 return { id: "plan", kind };
80 }86 }
@@ -85,6 +91,12 @@ const browserSurface = (tabId: string | null): RightPanelSurface => · 4 unmodified lines
85 ? { id: `browser:${tabId}`, kind: "preview", resourceId: tabId }91 ? { id: `browser:${tabId}`, kind: "preview", resourceId: tabId }
86 : { id: "browser:new", kind: "preview", resourceId: null };92 : { id: "browser:new", kind: "preview", resourceId: null };
87 93
94const fileSurface = (relativePath: string): RightPanelSurface => ({
95 id: `file:${relativePath}`,
96 kind: "file",
97 relativePath,
98});
99
88const terminalSurface = (terminalId: string): RightPanelSurface => ({100const terminalSurface = (terminalId: string): RightPanelSurface => ({
89 id: `terminal:${terminalId}`,101 id: `terminal:${terminalId}`,
90 kind: "terminal",102 kind: "terminal",
@@ -213,6 +225,18 @@ export const useRightPanelStore = create<RightPanelStoreState>()( · 122 unmodified lines
213 return upsertSurface({ ...current, surfaces: withoutPlaceholder }, surface);225 return upsertSurface({ ...current, surfaces: withoutPlaceholder }, surface);
214 }),226 }),
215 })),227 })),
228 openFile: (ref, relativePath) =>
229 set((state) => ({
230 byThreadKey: updateThread(state.byThreadKey, scopedThreadKey(ref), (current) => {
231 const withoutStandaloneExplorer = current.surfaces.filter(
232 (surface) => surface.kind !== "files",
233 );
234 return upsertSurface(
235 { ...current, surfaces: withoutStandaloneExplorer },
236 fileSurface(relativePath),
237 );
238 }),
239 })),
216 openTerminal: (ref, terminalId) =>240 openTerminal: (ref, terminalId) =>
217 set((state) => ({241 set((state) => ({
218 byThreadKey: updateThread(state.byThreadKey, scopedThreadKey(ref), (current) =>242 byThreadKey: updateThread(state.byThreadKey, scopedThreadKey(ref), (current) =>
@@ -339,6 +363,27 @@ export const useRightPanelStore = create<RightPanelStoreState>()( · 120 unmodified lines
339 };363 };
340 }),364 }),
341 })),365 })),
366 reconcileFileSurfaces: (ref, workspaceAvailable) =>
367 set((state) => ({
368 byThreadKey: updateThread(state.byThreadKey, scopedThreadKey(ref), (current) => {
369 if (workspaceAvailable) return current;
370 const surfaces = current.surfaces.filter(
371 (surface) => surface.kind !== "files" && surface.kind !== "file",
372 );
373 if (surfaces.length === current.surfaces.length) return current;
374 const activeStillExists = surfaces.some(
375 (surface) => surface.id === current.activeSurfaceId,
376 );
377 return {
378 ...current,
379 isOpen: surfaces.length > 0 ? current.isOpen : false,
380 surfaces,
381 activeSurfaceId: activeStillExists
382 ? current.activeSurfaceId
383 : (surfaces.at(-1)?.id ?? null),
384 };
385 }),
386 })),
342 show: (ref) =>387 show: (ref) =>
343 set((state) => ({388 set((state) => ({
344 byThreadKey: updateThread(state.byThreadKey, scopedThreadKey(ref), (current) =>389 byThreadKey: updateThread(state.byThreadKey, scopedThreadKey(ref), (current) =>
apps/web/src/vscode-icons-language-associations.json+0−481
@@ -1,481 +0,0 @@
1{
2 "version": "12.17.0",
3 "source": "https://raw.githubusercontent.com/vscode-icons/vscode-icons/v12.17.0/src/iconsManifest/languages.ts",
4 "generatedAt": "2026-02-17T20:03:11.362Z",
5 "extensionToLanguageId": {
6 "as": "actionscript",
7 "ada": "ada",
8 "prw": "advpl",
9 "affect": "affectscript",
10 "agda": "agda",
11 "al": "al",
12 "alloy": "alloy",
13 "ansible": "ansible",
14 "g4": "antlr",
15 "any": "anyscript",
16 "htaccess": "apacheconf",
17 "cls": "apex",
18 "apib": "apiblueprint",
19 "apl": "apl",
20 "applescript": "applescript",
21 "adoc": "asciidoc",
22 "asp": "asp",
23 "asm": "arm",
24 "astro": "astro",
25 "ats": "ats",
26 "ahk": "ahk",
27 "au3": "autoit",
28 "am": "automake",
29 "avcs": "avro",
30 "azcli": "azcli",
31 "azure-pipelines.yml": "azure-pipelines",
32 "bal": "ballerina",
33 "bat": "bat",
34 "bats": "bats",
35 "bzl": "bazel",
36 "bf": "befunge",
37 "bicep": "bicep",
38 "bib": "bibtex",
39 "biml": "biml",
40 "bin": "code-text-binary",
41 "rast": "biome_syntax_tree",
42 "blade.php": "blade",
43 "blitzbasic": "blitzbasic",
44 "bolt": "bolt",
45 "bsq": "bosque",
46 "bru": "bruno",
47 "buf.yaml": "buf",
48 "lockb": "bun.lockb",
49 "c": "c",
50 "c3": "c3",
51 "cal": "c-al",
52 "cabal": "cabal",
53 "capnp": "capnp",
54 "casc": "casc",
55 "cddl": "cddl",
56 "ceylon": "ceylon",
57 "cfc": "cfc",
58 "cfm": "cfmhtml",
59 "circom": "circom",
60 "clojure": "clojure",
61 "clojurescript": "clojurescript",
62 "yml": "manifest-yaml",
63 "cmake": "cmake",
64 "cbl": "cobol",
65 "ql": "ql",
66 "coffee": "coffeescript",
67 "cfml": "cfml",
68 "confluence": "confluence",
69 "ckbk": "cookbook",
70 "instructions.md": "instructions",
71 "cpp": "cpp",
72 "cr": "crystal",
73 "cs": "csharp",
74 "css": "css",
75 "wxss": "css",
76 "acss": "css",
77 "mcss": "css",
78 "pcss": "css",
79 "postcss": "css",
80 "nss": "css",
81 "ccss": "css",
82 "less": "css",
83 "scss": "css",
84 "sass": "css",
85 "csv": "csv",
86 "feature": "cucumber",
87 "cu": "cuda",
88 "cypher": "cypher",
89 "pyx": "cython",
90 "dal": "dal",
91 "dart": "dart",
92 "dhall": "dhall",
93 "html": "django-html",
94 "diff": "diff",
95 "d": "d",
96 "hcl": "dockerbake",
97 "dtx": "doctex",
98 "env": "dotenv",
99 "dot": "dotjs",
100 "dox": "doxygen",
101 "drawio": "drawio",
102 "drl": "drools",
103 "dtd": "dtd",
104 "dust": "dustjs",
105 "dylan": "dylan",
106 "earthfile": "earthfile",
107 "edge": "edge",
108 "eex": "eex",
109 "es": "es",
110 "ex": "elixir",
111 "elm": "elm",
112 "erb": "erb",
113 "erl": "erlang",
114 "yaml": "esphome",
115 "xlsx": "excel",
116 "falcon": "falcon",
117 "fql": "fql",
118 "f": "fortran",
119 "ftl": "ftl",
120 "fs": "fsharp",
121 "fthtml": "fthtml",
122 "gspec": "galen",
123 "gml": "gml-gms",
124 "gcode": "gcode",
125 "gen": "genstat",
126 "git": "git-commit",
127 "gleam": "gleam",
128 "gjs": "glimmer-js",
129 "glsl": "glsl",
130 "glyphs": "glyphs",
131 "gp": "gnuplot",
132 "go": "go",
133 "mod": "go.mod",
134 "work": "go.work",
135 "api": "goctl",
136 "gd": "gdscript",
137 "gradle.kts": "gradle-kotlin-dsl",
138 "gr": "grain",
139 "gql": "graphql",
140 "gv": "dot",
141 "grit": "grit",
142 "groovy": "groovy",
143 "haml": "haml",
144 "hbs": "handlebars",
145 "prg": "harbour",
146 "hs": "haskell",
147 "haxe": "haxe",
148 "helm.tpl": "helm",
149 "hjson": "hjson",
150 "hlsl": "hlsl",
151 "hosts": "hosts",
152 "http": "http",
153 "aff": "hunspell.aff",
154 "hy": "hy",
155 "hypr": "hypr",
156 "icl": "icl",
157 "imba": "imba",
158 "4gl": "4GL",
159 "ini": "ini",
160 "ink": "ink",
161 "iss": "innosetup",
162 "io": "io",
163 "janet": "janet",
164 "java": "java",
165 "js": "javascript",
166 "mjs": "javascript",
167 "cjs": "javascript",
168 "es6": "javascript",
169 "jake": "javascript",
170 "jsm": "javascript",
171 "jsx": "javascript",
172 "njs": "javascript",
173 "pac": "javascript",
174 "sjs": "javascript",
175 "ssjs": "javascript",
176 "xsjs": "javascript",
177 "xsjslib": "javascript",
178 "jekyll": "jekyll",
179 "jenkins": "jenkins",
180 "jest-snap": "jest-snapshot",
181 "jinja": "jinja",
182 "json": "json",
183 "jsonc": "jsonc",
184 "jsonl": "jsonl",
185 "jsonnet": "jsonnet",
186 "json5": "json5",
187 "jl": "julia",
188 "id": "iodine",
189 "k": "k",
190 "kv": "kivy",
191 "ks": "kos",
192 "kt": "kotlin",
193 "kusto": "kusto",
194 "tex": "latex",
195 "lark": "lark",
196 "lat": "latino",
197 "lean": "lean4",
198 "flex": "lex",
199 "ly": "lilypond",
200 "lisp": "lisp",
201 "lhs": "literate haskell",
202 "log": "log",
203 "lol": "lolcode",
204 "lsl": "lsl",
205 "lua": "lua",
206 "m4": "m4",
207 "mk": "makefile",
208 "md": "markdown",
209 "markdown": "markdown",
210 "mdown": "markdown",
211 "mkd": "markdown",
212 "marko": "marko",
213 "mat": "matlab",
214 "ms": "maxscript",
215 "mdx": "mdx",
216 "mediawiki": "mediawiki",
217 "mel": "mel",
218 "mmd": "mermaid",
219 "metal": "metal",
220 "mcfunction": "bc-mcfunction",
221 "mjml": "mjml",
222 "pq": "powerquery",
223 "mojo": "mojo",
224 "ep": "mojolicious",
225 "mongo": "mongo",
226 "mson": "mson",
227 "mv": "mv",
228 "mvt": "mvt",
229 "ne": "nearley",
230 "nginx": "nginx",
231 "nim": "nim",
232 "nimble": "nimble",
233 "nix": "nix",
234 "nsi": "nsis",
235 "nunjucks": "nunjucks",
236 "nu": "nushell",
237 "m": "objective-c",
238 "mm": "objective-cpp",
239 "ml": "ocaml",
240 "o3": "ogone",
241 "w": "abl",
242 "things": "openhab",
243 "org": "org",
244 "pas": "pascal",
245 "pddl": "pddl",
246 "plan": "plan",
247 "happenings": "happenings",
248 "pl": "perl",
249 "pl6": "perl6",
250 "pgsql": "pgsql",
251 "php": "php",
252 "php4": "php",
253 "php5": "php",
254 "php7": "php",
255 "phtml": "php",
256 "phps": "php",
257 "phpt": "php",
258 "phar": "php",
259 "aw": "php",
260 "ctp": "php",
261 "inc": "php",
262 "php3": "php",
263 "php8": "php",
264 "module": "php",
265 "theme": "php",
266 "tpl": "php",
267 "pine": "pine",
268 "txt": "plaintext",
269 "dbgasm": "platformio-debug.disassembly",
270 "ddl": "plsql",
271 "po": "po",
272 "dib": "polyglot-notebook",
273 "polymer": "polymer",
274 "pony": "pony",
275 "ps": "postscript",
276 "ps1": "powershell",
277 "prisma": "prisma",
278 "pde": "pde",
279 "pro": "prolog",
280 "rules": "prometheus",
281 "properties": "java-properties",
282 "proto": "proto3",
283 "pug": "jade",
284 "pp": "puppet",
285 "purs": "purescript",
286 "arr": "pyret",
287 "pyscript": "pyscript",
288 "py": "python",
289 "pyi": "python",
290 "pyw": "python",
291 "pyc": "python",
292 "pyd": "python",
293 "pyo": "python",
294 "pxd": "python",
295 "pxi": "python",
296 "rpy": "python",
297 "cpy": "python",
298 "ipy": "python",
299 "pyowo": "pythowo",
300 "qvs": "qlik",
301 "qml": "qml",
302 "qs": "qsharp",
303 "r": "r",
304 "rkt": "racket",
305 "raku": "raku",
306 "cshtml": "razor",
307 "raml": "raml",
308 "re": "reason",
309 "red": "red",
310 "res": "rescript",
311 "rst": "restructuredtext",
312 "rex": "rexx",
313 "tag": "riot",
314 "ripple": "ripple",
315 "rmd": "rmd",
316 "rnc": "rnc",
317 "robot": "robot",
318 "rb": "ruby",
319 "rbw": "ruby",
320 "rake": "ruby",
321 "gemspec": "ruby",
322 "ru": "ruby",
323 "rjs": "ruby",
324 "rxml": "ruby",
325 "rsel": "ruby",
326 "builder": "ruby",
327 "jbuilder": "ruby",
328 "rabl": "ruby",
329 "prawn": "ruby",
330 "thor": "ruby",
331 "rs": "rust",
332 "sl": "s-lang",
333 "san": "san",
334 "sas": "SAS",
335 "sbt": "sbt",
336 "scad": "scad",
337 "scala": "scala",
338 "sce": "scilab",
339 "sdl": "sdl",
340 "code-search": "search-result",
341 "shader": "shaderlab",
342 "sh": "shellscript",
343 "bash": "shellscript",
344 "zsh": "shellscript",
345 "ksh": "shellscript",
346 "fish": "shellscript",
347 "slang": "slang",
348 "ice": "slice",
349 "slim": "slim",
350 "slint": "slint",
351 "sln": "sln",
352 "ss": "silverstripe",
353 "sn": "sino",
354 "eskip": "eskip",
355 "smk": "snakemake",
356 "code-snippets": "snippets",
357 "snort": "snort",
358 "sol": "solidity",
359 "rq": "sparql",
360 "sqf": "sqf",
361 "sql": "sql",
362 "nut": "squirrel",
363 "stan": "stan",
364 "bazel": "starlark",
365 "do": "stata",
366 "stencil": "stencil",
367 "html.stencil": "stencil-html",
368 "st.css": "stylable",
369 "styled": "source.css.styled",
370 "styl": "stylus",
371 "svelte": "svelte",
372 "svg": "svg",
373 "swagger": "Swagger",
374 "swift": "swift",
375 "swig": "swig",
376 "link": "systemd-conf",
377 "sv": "systemverilog",
378 "tt": "t4",
379 "teal": "teal",
380 "templ": "templ",
381 "tt3": "tt",
382 "tera": "tera",
383 "tf": "terraform",
384 "sty": "tex",
385 "textile": "textile",
386 "json-tmlanguage": "json-tmlanguage",
387 "yaml-tmlanguage": "yaml-tmlanguage",
388 "toit": "toit",
389 "toml": "toml",
390 "toon": "toon",
391 "ttcn3": "ttcn",
392 "tuc": "tuc",
393 "twig": "twig",
394 "ts": "typescript",
395 "mts": "typescript",
396 "cts": "typescript",
397 "tsx": "typescriptreact",
398 "typoscript": "typoscript",
399 "ua": "uiua",
400 "u": "unison",
401 "vb": "vb",
402 "wsf": "vbscript",
403 "vm": "velocity",
404 "vto": "vento",
405 "v": "verilog",
406 "vhdl": "vhdl",
407 "vim": "viml",
408 "vitest-snap": "vitest-snapshot",
409 "volt": "volt",
410 "vroom": "vroom",
411 "vue": "vue",
412 "vue2": "vue",
413 "vue3": "vue",
414 "vues": "vue",
415 "vuex": "vue",
416 "vuerc": "vue",
417 "vy": "vyper",
418 "wai": "wai",
419 "wasm": "wasm",
420 "wat": "wasm",
421 "wast": "wasm",
422 "wsm": "wasm",
423 "wast.js": "wasm",
424 "wit": "wasm",
425 "witx": "wasm",
426 "wy": "wenyan",
427 "wgsl": "wgsl",
428 "wt": "wikitext",
429 "wl": "wolfram",
430 "wurst": "wurstlang",
431 "xcompose": "xcompose",
432 "xmake.lua": "xmake",
433 "xml": "xml",
434 "xquery": "xquery",
435 "xsl": "xsl",
436 "bison": "yacc",
437 "yang": "yang",
438 "zig": "zig",
439 "zip": "zip"
440 },
441 "fileNameToLanguageId": {
442 "browserslist": "browserslist",
443 ".browserslistrc": "browserslist",
444 "caddyfile": "caddyfile",
445 "cmakelists.txt": "cmake",
446 "cmakecache.txt": "cmake-cache",
447 "docker-compose.yml": "dockercompose",
448 "docker-compose.yaml": "dockercompose",
449 "compose.yaml": "dockercompose",
450 "compose.yml": "dockercompose",
451 "dockerfile": "dockerfile",
452 "dockerfile.dev": "dockerfile",
453 "dockerfile.prod": "dockerfile",
454 "dockerfile.test": "dockerfile",
455 ".editorconfig": "editorconfig",
456 "justfile": "just",
457 "meson.build": "meson",
458 ".php-version": "php",
459 "artisan": "php",
460 "requirements.txt": "pip-requirements",
461 "requirements-dev.txt": "pip-requirements",
462 "requirements-test.txt": "pip-requirements",
463 "requirements-prod.txt": "pip-requirements",
464 "setup.py": "python",
465 "setup.cfg": "python",
466 ".python-version": "python",
467 "pipfile": "python",
468 "pipfile.lock": "python",
469 "robots.txt": "robots-txt",
470 "config.ru": "ruby",
471 ".bashrc": "shellscript",
472 ".zshrc": "shellscript",
473 ".profile": "shellscript",
474 ".bash_profile": "shellscript",
475 ".bash_aliases": "shellscript",
476 ".zprofile": "shellscript",
477 "tiltfile": "tiltfile",
478 ".vue-cli-rc": "vue",
479 "yarn.lock": "yarnlock"
480 }
481}
apps/web/src/vscode-icons-manifest.json+0−8351
@@ -1,8351 +0,0 @@
1{
2 "iconDefinitions": {
3 "_file": { "iconPath": "../../icons/default_file.svg" },
4 "_folder": { "iconPath": "../../icons/default_folder.svg" },
5 "_folder_open": { "iconPath": "../../icons/default_folder_opened.svg" },
6 "_root_folder": { "iconPath": "../../icons/default_root_folder.svg" },
7 "_root_folder_open": { "iconPath": "../../icons/default_root_folder_opened.svg" },
8 "_file_light": { "iconPath": "" },
9 "_folder_light": { "iconPath": "" },
10 "_folder_light_open": { "iconPath": "" },
11 "_root_folder_light": { "iconPath": "" },
12 "_root_folder_light_open": { "iconPath": "" },
13 "_fd_android": { "iconPath": "../../icons/folder_type_android.svg" },
14 "_fd_android_open": { "iconPath": "../../icons/folder_type_android_opened.svg" },
15 "_fd_angular": { "iconPath": "../../icons/folder_type_angular.svg" },
16 "_fd_angular_open": { "iconPath": "../../icons/folder_type_angular_opened.svg" },
17 "_fd_apache": { "iconPath": "../../icons/folder_type_apache.svg" },
18 "_fd_apache_open": { "iconPath": "../../icons/folder_type_apache_opened.svg" },
19 "_fd_api": { "iconPath": "../../icons/folder_type_api.svg" },
20 "_fd_api_open": { "iconPath": "../../icons/folder_type_api_opened.svg" },
21 "_fd_app": { "iconPath": "../../icons/folder_type_app.svg" },
22 "_fd_app_open": { "iconPath": "../../icons/folder_type_app_opened.svg" },
23 "_fd_arangodb": { "iconPath": "../../icons/folder_type_arangodb.svg" },
24 "_fd_arangodb_open": { "iconPath": "../../icons/folder_type_arangodb_opened.svg" },
25 "_fd_asset": { "iconPath": "../../icons/folder_type_asset.svg" },
26 "_fd_asset_open": { "iconPath": "../../icons/folder_type_asset_opened.svg" },
27 "_fd_astro": { "iconPath": "../../icons/folder_type_astro.svg" },
28 "_fd_astro_open": { "iconPath": "../../icons/folder_type_astro_opened.svg" },
29 "_fd_audio": { "iconPath": "../../icons/folder_type_audio.svg" },
30 "_fd_audio_open": { "iconPath": "../../icons/folder_type_audio_opened.svg" },
31 "_fd_aurelia": { "iconPath": "../../icons/folder_type_aurelia.svg" },
32 "_fd_aurelia_open": { "iconPath": "../../icons/folder_type_aurelia_opened.svg" },
33 "_fd_aws": { "iconPath": "../../icons/folder_type_aws.svg" },
34 "_fd_aws_open": { "iconPath": "../../icons/folder_type_aws_opened.svg" },
35 "_fd_azure": { "iconPath": "../../icons/folder_type_azure.svg" },
36 "_fd_azure_open": { "iconPath": "../../icons/folder_type_azure_opened.svg" },
37 "_fd_azurepipelines": { "iconPath": "../../icons/folder_type_azurepipelines.svg" },
38 "_fd_azurepipelines_open": { "iconPath": "../../icons/folder_type_azurepipelines_opened.svg" },
39 "_fd_binary": { "iconPath": "../../icons/folder_type_binary.svg" },
40 "_fd_binary_open": { "iconPath": "../../icons/folder_type_binary_opened.svg" },
41 "_fd_bloc": { "iconPath": "../../icons/folder_type_bloc.svg" },
42 "_fd_bloc_open": { "iconPath": "../../icons/folder_type_bloc_opened.svg" },
43 "_fd_blueprint": { "iconPath": "../../icons/folder_type_blueprint.svg" },
44 "_fd_blueprint_open": { "iconPath": "../../icons/folder_type_blueprint_opened.svg" },
45 "_fd_bot": { "iconPath": "../../icons/folder_type_bot.svg" },
46 "_fd_bot_open": { "iconPath": "../../icons/folder_type_bot_opened.svg" },
47 "_fd_bower": { "iconPath": "../../icons/folder_type_bower.svg" },
48 "_fd_bower_open": { "iconPath": "../../icons/folder_type_bower_opened.svg" },
49 "_fd_buildkite": { "iconPath": "../../icons/folder_type_buildkite.svg" },
50 "_fd_buildkite_open": { "iconPath": "../../icons/folder_type_buildkite_opened.svg" },
51 "_fd_cake": { "iconPath": "../../icons/folder_type_cake.svg" },
52 "_fd_cake_open": { "iconPath": "../../icons/folder_type_cake_opened.svg" },
53 "_fd_cargo": { "iconPath": "../../icons/folder_type_cargo.svg" },
54 "_fd_cargo_open": { "iconPath": "../../icons/folder_type_cargo_opened.svg" },
55 "_fd_certificate": { "iconPath": "../../icons/folder_type_certificate.svg" },
56 "_fd_certificate_open": { "iconPath": "../../icons/folder_type_certificate_opened.svg" },
57 "_fd_changesets": { "iconPath": "../../icons/folder_type_changesets.svg" },
58 "_fd_changesets_open": { "iconPath": "../../icons/folder_type_changesets_opened.svg" },
59 "_fd_chef": { "iconPath": "../../icons/folder_type_chef.svg" },
60 "_fd_chef_open": { "iconPath": "../../icons/folder_type_chef_opened.svg" },
61 "_fd_circleci": { "iconPath": "../../icons/folder_type_circleci.svg" },
62 "_fd_circleci_open": { "iconPath": "../../icons/folder_type_circleci_opened.svg" },
63 "_fd_claude": { "iconPath": "../../icons/folder_type_claude.svg" },
64 "_fd_claude_open": { "iconPath": "../../icons/folder_type_claude_opened.svg" },
65 "_fd_cli": { "iconPath": "../../icons/folder_type_cli.svg" },
66 "_fd_cli_open": { "iconPath": "../../icons/folder_type_cli_opened.svg" },
67 "_fd_client": { "iconPath": "../../icons/folder_type_client.svg" },
68 "_fd_client_open": { "iconPath": "../../icons/folder_type_client_opened.svg" },
69 "_fd_cloudflare": { "iconPath": "../../icons/folder_type_cloudflare.svg" },
70 "_fd_cloudflare_open": { "iconPath": "../../icons/folder_type_cloudflare_opened.svg" },
71 "_fd_cmake": { "iconPath": "../../icons/folder_type_cmake.svg" },
72 "_fd_cmake_open": { "iconPath": "../../icons/folder_type_cmake_opened.svg" },
73 "_fd_common": { "iconPath": "../../icons/folder_type_common.svg" },
74 "_fd_common_open": { "iconPath": "../../icons/folder_type_common_opened.svg" },
75 "_fd_component": { "iconPath": "../../icons/folder_type_component.svg" },
76 "_fd_component_open": { "iconPath": "../../icons/folder_type_component_opened.svg" },
77 "_fd_composer": { "iconPath": "../../icons/folder_type_composer.svg" },
78 "_fd_composer_open": { "iconPath": "../../icons/folder_type_composer_opened.svg" },
79 "_fd_config": { "iconPath": "../../icons/folder_type_config.svg" },
80 "_fd_config_open": { "iconPath": "../../icons/folder_type_config_opened.svg" },
81 "_fd_controller": { "iconPath": "../../icons/folder_type_controller.svg" },
82 "_fd_controller_open": { "iconPath": "../../icons/folder_type_controller_opened.svg" },
83 "_fd_coverage": { "iconPath": "../../icons/folder_type_coverage.svg" },
84 "_fd_coverage_open": { "iconPath": "../../icons/folder_type_coverage_opened.svg" },
85 "_fd_css": { "iconPath": "../../icons/folder_type_css.svg" },
86 "_fd_css_open": { "iconPath": "../../icons/folder_type_css_opened.svg" },
87 "_fd_cubit": { "iconPath": "../../icons/folder_type_cubit.svg" },
88 "_fd_cubit_open": { "iconPath": "../../icons/folder_type_cubit_opened.svg" },
89 "_fd_cursor": { "iconPath": "../../icons/folder_type_cursor.svg" },
90 "_fd_cursor_open": { "iconPath": "../../icons/folder_type_cursor_opened.svg" },
91 "_fd_light_cursor": { "iconPath": "../../icons/folder_type_light_cursor.svg" },
92 "_fd_light_cursor_open": { "iconPath": "../../icons/folder_type_light_cursor_opened.svg" },
93 "_fd_cypress": { "iconPath": "../../icons/folder_type_cypress.svg" },
94 "_fd_cypress_open": { "iconPath": "../../icons/folder_type_cypress_opened.svg" },
95 "_fd_light_cypress": { "iconPath": "../../icons/folder_type_light_cypress.svg" },
96 "_fd_light_cypress_open": { "iconPath": "../../icons/folder_type_light_cypress_opened.svg" },
97 "_fd_dapr": { "iconPath": "../../icons/folder_type_dapr.svg" },
98 "_fd_dapr_open": { "iconPath": "../../icons/folder_type_dapr_opened.svg" },
99 "_fd_dart": { "iconPath": "../../icons/folder_type_dart.svg" },
100 "_fd_dart_open": { "iconPath": "../../icons/folder_type_dart_opened.svg" },
101 "_fd_datadog": { "iconPath": "../../icons/folder_type_datadog.svg" },
102 "_fd_datadog_open": { "iconPath": "../../icons/folder_type_datadog_opened.svg" },
103 "_fd_db": { "iconPath": "../../icons/folder_type_db.svg" },
104 "_fd_db_open": { "iconPath": "../../icons/folder_type_db_opened.svg" },
105 "_fd_debian": { "iconPath": "../../icons/folder_type_debian.svg" },
106 "_fd_debian_open": { "iconPath": "../../icons/folder_type_debian_opened.svg" },
107 "_fd_dependabot": { "iconPath": "../../icons/folder_type_dependabot.svg" },
108 "_fd_dependabot_open": { "iconPath": "../../icons/folder_type_dependabot_opened.svg" },
109 "_fd_devcontainer": { "iconPath": "../../icons/folder_type_devcontainer.svg" },
110 "_fd_devcontainer_open": { "iconPath": "../../icons/folder_type_devcontainer_opened.svg" },
111 "_fd_dist": { "iconPath": "../../icons/folder_type_dist.svg" },
112 "_fd_dist_open": { "iconPath": "../../icons/folder_type_dist_opened.svg" },
113 "_fd_docker": { "iconPath": "../../icons/folder_type_docker.svg" },
114 "_fd_docker_open": { "iconPath": "../../icons/folder_type_docker_opened.svg" },
115 "_fd_docs": { "iconPath": "../../icons/folder_type_docs.svg" },
116 "_fd_docs_open": { "iconPath": "../../icons/folder_type_docs_opened.svg" },
117 "_fd_e2e": { "iconPath": "../../icons/folder_type_e2e.svg" },
118 "_fd_e2e_open": { "iconPath": "../../icons/folder_type_e2e_opened.svg" },
119 "_fd_elasticbeanstalk": { "iconPath": "../../icons/folder_type_elasticbeanstalk.svg" },
120 "_fd_elasticbeanstalk_open": {
121 "iconPath": "../../icons/folder_type_elasticbeanstalk_opened.svg"
122 },
123 "_fd_electron": { "iconPath": "../../icons/folder_type_electron.svg" },
124 "_fd_electron_open": { "iconPath": "../../icons/folder_type_electron_opened.svg" },
125 "_fd_light_electron": { "iconPath": "../../icons/folder_type_light_electron.svg" },
126 "_fd_light_electron_open": { "iconPath": "../../icons/folder_type_light_electron_opened.svg" },
127 "_fd_expo": { "iconPath": "../../icons/folder_type_expo.svg" },
128 "_fd_expo_open": { "iconPath": "../../icons/folder_type_expo_opened.svg" },
129 "_fd_light_expo": { "iconPath": "../../icons/folder_type_light_expo.svg" },
130 "_fd_light_expo_open": { "iconPath": "../../icons/folder_type_light_expo_opened.svg" },
131 "_fd_favicon": { "iconPath": "../../icons/folder_type_favicon.svg" },
132 "_fd_favicon_open": { "iconPath": "../../icons/folder_type_favicon_opened.svg" },
133 "_fd_flow": { "iconPath": "../../icons/folder_type_flow.svg" },
134 "_fd_flow_open": { "iconPath": "../../icons/folder_type_flow_opened.svg" },
135 "_fd_flutter": { "iconPath": "../../icons/folder_type_flutter.svg" },
136 "_fd_flutter_open": { "iconPath": "../../icons/folder_type_flutter_opened.svg" },
137 "_fd_fonts": { "iconPath": "../../icons/folder_type_fonts.svg" },
138 "_fd_fonts_open": { "iconPath": "../../icons/folder_type_fonts_opened.svg" },
139 "_fd_light_fonts": { "iconPath": "../../icons/folder_type_light_fonts.svg" },
140 "_fd_light_fonts_open": { "iconPath": "../../icons/folder_type_light_fonts_opened.svg" },
141 "_fd_frontcommerce": { "iconPath": "../../icons/folder_type_frontcommerce.svg" },
142 "_fd_frontcommerce_open": { "iconPath": "../../icons/folder_type_frontcommerce_opened.svg" },
143 "_fd_gcp": { "iconPath": "../../icons/folder_type_gcp.svg" },
144 "_fd_gcp_open": { "iconPath": "../../icons/folder_type_gcp_opened.svg" },
145 "_fd_gemini": { "iconPath": "../../icons/folder_type_gemini.svg" },
146 "_fd_gemini_open": { "iconPath": "../../icons/folder_type_gemini_opened.svg" },
147 "_fd_git": { "iconPath": "../../icons/folder_type_git.svg" },
148 "_fd_git_open": { "iconPath": "../../icons/folder_type_git_opened.svg" },
149 "_fd_github": { "iconPath": "../../icons/folder_type_github.svg" },
150 "_fd_github_open": { "iconPath": "../../icons/folder_type_github_opened.svg" },
151 "_fd_gitlab": { "iconPath": "../../icons/folder_type_gitlab.svg" },
152 "_fd_gitlab_open": { "iconPath": "../../icons/folder_type_gitlab_opened.svg" },
153 "_fd_godot": { "iconPath": "../../icons/folder_type_godot.svg" },
154 "_fd_godot_open": { "iconPath": "../../icons/folder_type_godot_opened.svg" },
155 "_fd_gradle": { "iconPath": "../../icons/folder_type_gradle.svg" },
156 "_fd_gradle_open": { "iconPath": "../../icons/folder_type_gradle_opened.svg" },
157 "_fd_light_gradle": { "iconPath": "../../icons/folder_type_light_gradle.svg" },
158 "_fd_light_gradle_open": { "iconPath": "../../icons/folder_type_light_gradle_opened.svg" },
159 "_fd_graphql": { "iconPath": "../../icons/folder_type_graphql.svg" },
160 "_fd_graphql_open": { "iconPath": "../../icons/folder_type_graphql_opened.svg" },
161 "_fd_grunt": { "iconPath": "../../icons/folder_type_grunt.svg" },
162 "_fd_grunt_open": { "iconPath": "../../icons/folder_type_grunt_opened.svg" },
163 "_fd_gulp": { "iconPath": "../../icons/folder_type_gulp.svg" },
164 "_fd_gulp_open": { "iconPath": "../../icons/folder_type_gulp_opened.svg" },
165 "_fd_haxelib": { "iconPath": "../../icons/folder_type_haxelib.svg" },
166 "_fd_haxelib_open": { "iconPath": "../../icons/folder_type_haxelib_opened.svg" },
167 "_fd_helper": { "iconPath": "../../icons/folder_type_helper.svg" },
168 "_fd_helper_open": { "iconPath": "../../icons/folder_type_helper_opened.svg" },
169 "_fd_histoire": { "iconPath": "../../icons/folder_type_histoire.svg" },
170 "_fd_histoire_open": { "iconPath": "../../icons/folder_type_histoire_opened.svg" },
171 "_fd_hook": { "iconPath": "../../icons/folder_type_hook.svg" },
172 "_fd_hook_open": { "iconPath": "../../icons/folder_type_hook_opened.svg" },
173 "_fd_husky": { "iconPath": "../../icons/folder_type_husky.svg" },
174 "_fd_husky_open": { "iconPath": "../../icons/folder_type_husky_opened.svg" },
175 "_fd_idea": { "iconPath": "../../icons/folder_type_idea.svg" },
176 "_fd_idea_open": { "iconPath": "../../icons/folder_type_idea_opened.svg" },
177 "_fd_images": { "iconPath": "../../icons/folder_type_images.svg" },
178 "_fd_images_open": { "iconPath": "../../icons/folder_type_images_opened.svg" },
179 "_fd_include": { "iconPath": "../../icons/folder_type_include.svg" },
180 "_fd_include_open": { "iconPath": "../../icons/folder_type_include_opened.svg" },
181 "_fd_interfaces": { "iconPath": "../../icons/folder_type_interfaces.svg" },
182 "_fd_interfaces_open": { "iconPath": "../../icons/folder_type_interfaces_opened.svg" },
183 "_fd_ios": { "iconPath": "../../icons/folder_type_ios.svg" },
184 "_fd_ios_open": { "iconPath": "../../icons/folder_type_ios_opened.svg" },
185 "_fd_js": { "iconPath": "../../icons/folder_type_js.svg" },
186 "_fd_js_open": { "iconPath": "../../icons/folder_type_js_opened.svg" },
187 "_fd_json": { "iconPath": "../../icons/folder_type_json.svg" },
188 "_fd_json_open": { "iconPath": "../../icons/folder_type_json_opened.svg" },
189 "_fd_junie": { "iconPath": "../../icons/folder_type_junie.svg" },
190 "_fd_junie_open": { "iconPath": "../../icons/folder_type_junie_opened.svg" },
191 "_fd_kotlin": { "iconPath": "../../icons/folder_type_kotlin.svg" },
192 "_fd_kotlin_open": { "iconPath": "../../icons/folder_type_kotlin_opened.svg" },
193 "_fd_kubernetes": { "iconPath": "../../icons/folder_type_kubernetes.svg" },
194 "_fd_kubernetes_open": { "iconPath": "../../icons/folder_type_kubernetes_opened.svg" },
195 "_fd_less": { "iconPath": "../../icons/folder_type_less.svg" },
196 "_fd_less_open": { "iconPath": "../../icons/folder_type_less_opened.svg" },
197 "_fd_library": { "iconPath": "../../icons/folder_type_library.svg" },
198 "_fd_library_open": { "iconPath": "../../icons/folder_type_library_opened.svg" },
199 "_fd_linux": { "iconPath": "../../icons/folder_type_linux.svg" },
200 "_fd_linux_open": { "iconPath": "../../icons/folder_type_linux_opened.svg" },
201 "_fd_locale": { "iconPath": "../../icons/folder_type_locale.svg" },
202 "_fd_locale_open": { "iconPath": "../../icons/folder_type_locale_opened.svg" },
203 "_fd_log": { "iconPath": "../../icons/folder_type_log.svg" },
204 "_fd_log_open": { "iconPath": "../../icons/folder_type_log_opened.svg" },
205 "_fd_macos": { "iconPath": "../../icons/folder_type_macos.svg" },
206 "_fd_macos_open": { "iconPath": "../../icons/folder_type_macos_opened.svg" },
207 "_fd_mariadb": { "iconPath": "../../icons/folder_type_mariadb.svg" },
208 "_fd_mariadb_open": { "iconPath": "../../icons/folder_type_mariadb_opened.svg" },
209 "_fd_maven": { "iconPath": "../../icons/folder_type_maven.svg" },
210 "_fd_maven_open": { "iconPath": "../../icons/folder_type_maven_opened.svg" },
211 "_fd_mediawiki": { "iconPath": "../../icons/folder_type_mediawiki.svg" },
212 "_fd_mediawiki_open": { "iconPath": "../../icons/folder_type_mediawiki_opened.svg" },
213 "_fd_memcached": { "iconPath": "../../icons/folder_type_memcached.svg" },
214 "_fd_memcached_open": { "iconPath": "../../icons/folder_type_memcached_opened.svg" },
215 "_fd_meteor": { "iconPath": "../../icons/folder_type_meteor.svg" },
216 "_fd_meteor_open": { "iconPath": "../../icons/folder_type_meteor_opened.svg" },
217 "_fd_light_meteor": { "iconPath": "../../icons/folder_type_light_meteor.svg" },
218 "_fd_light_meteor_open": { "iconPath": "../../icons/folder_type_light_meteor_opened.svg" },
219 "_fd_middleware": { "iconPath": "../../icons/folder_type_middleware.svg" },
220 "_fd_middleware_open": { "iconPath": "../../icons/folder_type_middleware_opened.svg" },
221 "_fd_minecraft": { "iconPath": "../../icons/folder_type_minecraft.svg" },
222 "_fd_minecraft_open": { "iconPath": "../../icons/folder_type_minecraft_opened.svg" },
223 "_fd_minikube": { "iconPath": "../../icons/folder_type_minikube.svg" },
224 "_fd_minikube_open": { "iconPath": "../../icons/folder_type_minikube_opened.svg" },
225 "_fd_mjml": { "iconPath": "../../icons/folder_type_mjml.svg" },
226 "_fd_mjml_open": { "iconPath": "../../icons/folder_type_mjml_opened.svg" },
227 "_fd_mock": { "iconPath": "../../icons/folder_type_mock.svg" },
228 "_fd_mock_open": { "iconPath": "../../icons/folder_type_mock_opened.svg" },
229 "_fd_model": { "iconPath": "../../icons/folder_type_model.svg" },
230 "_fd_model_open": { "iconPath": "../../icons/folder_type_model_opened.svg" },
231 "_fd_module": { "iconPath": "../../icons/folder_type_module.svg" },
232 "_fd_module_open": { "iconPath": "../../icons/folder_type_module_opened.svg" },
233 "_fd_mojo": { "iconPath": "../../icons/folder_type_mojo.svg" },
234 "_fd_mojo_open": { "iconPath": "../../icons/folder_type_mojo_opened.svg" },
235 "_fd_mongodb": { "iconPath": "../../icons/folder_type_mongodb.svg" },
236 "_fd_mongodb_open": { "iconPath": "../../icons/folder_type_mongodb_opened.svg" },
237 "_fd_mypy": { "iconPath": "../../icons/folder_type_mypy.svg" },
238 "_fd_mypy_open": { "iconPath": "../../icons/folder_type_mypy_opened.svg" },
239 "_fd_light_mypy": { "iconPath": "../../icons/folder_type_light_mypy.svg" },
240 "_fd_light_mypy_open": { "iconPath": "../../icons/folder_type_light_mypy_opened.svg" },
241 "_fd_mysql": { "iconPath": "../../icons/folder_type_mysql.svg" },
242 "_fd_mysql_open": { "iconPath": "../../icons/folder_type_mysql_opened.svg" },
243 "_fd_light_mysql": { "iconPath": "../../icons/folder_type_light_mysql.svg" },
244 "_fd_light_mysql_open": { "iconPath": "../../icons/folder_type_light_mysql_opened.svg" },
245 "_fd_netlify": { "iconPath": "../../icons/folder_type_netlify.svg" },
246 "_fd_netlify_open": { "iconPath": "../../icons/folder_type_netlify_opened.svg" },
247 "_fd_next": { "iconPath": "../../icons/folder_type_next.svg" },
248 "_fd_next_open": { "iconPath": "../../icons/folder_type_next_opened.svg" },
249 "_fd_nginx": { "iconPath": "../../icons/folder_type_nginx.svg" },
250 "_fd_nginx_open": { "iconPath": "../../icons/folder_type_nginx_opened.svg" },
251 "_fd_nix": { "iconPath": "../../icons/folder_type_nix.svg" },
252 "_fd_nix_open": { "iconPath": "../../icons/folder_type_nix_opened.svg" },
253 "_fd_node": { "iconPath": "../../icons/folder_type_node.svg" },
254 "_fd_node_open": { "iconPath": "../../icons/folder_type_node_opened.svg" },
255 "_fd_light_node": { "iconPath": "../../icons/folder_type_light_node.svg" },
256 "_fd_light_node_open": { "iconPath": "../../icons/folder_type_light_node_opened.svg" },
257 "_fd_notebooks": { "iconPath": "../../icons/folder_type_notebooks.svg" },
258 "_fd_notebooks_open": { "iconPath": "../../icons/folder_type_notebooks_opened.svg" },
259 "_fd_notification": { "iconPath": "../../icons/folder_type_notification.svg" },
260 "_fd_notification_open": { "iconPath": "../../icons/folder_type_notification_opened.svg" },
261 "_fd_nuget": { "iconPath": "../../icons/folder_type_nuget.svg" },
262 "_fd_nuget_open": { "iconPath": "../../icons/folder_type_nuget_opened.svg" },
263 "_fd_nuxt": { "iconPath": "../../icons/folder_type_nuxt.svg" },
264 "_fd_nuxt_open": { "iconPath": "../../icons/folder_type_nuxt_opened.svg" },
265 "_fd_package": { "iconPath": "../../icons/folder_type_package.svg" },
266 "_fd_package_open": { "iconPath": "../../icons/folder_type_package_opened.svg" },
267 "_fd_paket": { "iconPath": "../../icons/folder_type_paket.svg" },
268 "_fd_paket_open": { "iconPath": "../../icons/folder_type_paket_opened.svg" },
269 "_fd_php": { "iconPath": "../../icons/folder_type_php.svg" },
270 "_fd_php_open": { "iconPath": "../../icons/folder_type_php_opened.svg" },
271 "_fd_platformio": { "iconPath": "../../icons/folder_type_platformio.svg" },
272 "_fd_platformio_open": { "iconPath": "../../icons/folder_type_platformio_opened.svg" },
273 "_fd_plugin": { "iconPath": "../../icons/folder_type_plugin.svg" },
274 "_fd_plugin_open": { "iconPath": "../../icons/folder_type_plugin_opened.svg" },
275 "_fd_prisma": { "iconPath": "../../icons/folder_type_prisma.svg" },
276 "_fd_prisma_open": { "iconPath": "../../icons/folder_type_prisma_opened.svg" },
277 "_fd_private": { "iconPath": "../../icons/folder_type_private.svg" },
278 "_fd_private_open": { "iconPath": "../../icons/folder_type_private_opened.svg" },
279 "_fd_public": { "iconPath": "../../icons/folder_type_public.svg" },
280 "_fd_public_open": { "iconPath": "../../icons/folder_type_public_opened.svg" },
281 "_fd_pytest": { "iconPath": "../../icons/folder_type_pytest.svg" },
282 "_fd_pytest_open": { "iconPath": "../../icons/folder_type_pytest_opened.svg" },
283 "_fd_python": { "iconPath": "../../icons/folder_type_python.svg" },
284 "_fd_python_open": { "iconPath": "../../icons/folder_type_python_opened.svg" },
285 "_fd_ravendb": { "iconPath": "../../icons/folder_type_ravendb.svg" },
286 "_fd_ravendb_open": { "iconPath": "../../icons/folder_type_ravendb_opened.svg" },
287 "_fd_redis": { "iconPath": "../../icons/folder_type_redis.svg" },
288 "_fd_redis_open": { "iconPath": "../../icons/folder_type_redis_opened.svg" },
289 "_fd_redux": { "iconPath": "../../icons/folder_type_redux.svg" },
290 "_fd_redux_open": { "iconPath": "../../icons/folder_type_redux_opened.svg" },
291 "_fd_light_redux": { "iconPath": "../../icons/folder_type_light_redux.svg" },
292 "_fd_light_redux_open": { "iconPath": "../../icons/folder_type_light_redux_opened.svg" },
293 "_fd_route": { "iconPath": "../../icons/folder_type_route.svg" },
294 "_fd_route_open": { "iconPath": "../../icons/folder_type_route_opened.svg" },
295 "_fd_sass": { "iconPath": "../../icons/folder_type_sass.svg" },
296 "_fd_sass_open": { "iconPath": "../../icons/folder_type_sass_opened.svg" },
297 "_fd_light_sass": { "iconPath": "../../icons/folder_type_light_sass.svg" },
298 "_fd_light_sass_open": { "iconPath": "../../icons/folder_type_light_sass_opened.svg" },
299 "_fd_script": { "iconPath": "../../icons/folder_type_script.svg" },
300 "_fd_script_open": { "iconPath": "../../icons/folder_type_script_opened.svg" },
301 "_fd_seedkit": { "iconPath": "../../icons/folder_type_seedkit.svg" },
302 "_fd_seedkit_open": { "iconPath": "../../icons/folder_type_seedkit_opened.svg" },
303 "_fd_server": { "iconPath": "../../icons/folder_type_server.svg" },
304 "_fd_server_open": { "iconPath": "../../icons/folder_type_server_opened.svg" },
305 "_fd_services": { "iconPath": "../../icons/folder_type_services.svg" },
306 "_fd_services_open": { "iconPath": "../../icons/folder_type_services_opened.svg" },
307 "_fd_shared": { "iconPath": "../../icons/folder_type_shared.svg" },
308 "_fd_shared_open": { "iconPath": "../../icons/folder_type_shared_opened.svg" },
309 "_fd_snaplet": { "iconPath": "../../icons/folder_type_snaplet.svg" },
310 "_fd_snaplet_open": { "iconPath": "../../icons/folder_type_snaplet_opened.svg" },
311 "_fd_spin": { "iconPath": "../../icons/folder_type_spin.svg" },
312 "_fd_spin_open": { "iconPath": "../../icons/folder_type_spin_opened.svg" },
313 "_fd_src": { "iconPath": "../../icons/folder_type_src.svg" },
314 "_fd_src_open": { "iconPath": "../../icons/folder_type_src_opened.svg" },
315 "_fd_sso": { "iconPath": "../../icons/folder_type_sso.svg" },
316 "_fd_sso_open": { "iconPath": "../../icons/folder_type_sso_opened.svg" },
317 "_fd_story": { "iconPath": "../../icons/folder_type_story.svg" },
318 "_fd_story_open": { "iconPath": "../../icons/folder_type_story_opened.svg" },
319 "_fd_style": { "iconPath": "../../icons/folder_type_style.svg" },
320 "_fd_style_open": { "iconPath": "../../icons/folder_type_style_opened.svg" },
321 "_fd_supabase": { "iconPath": "../../icons/folder_type_supabase.svg" },
322 "_fd_supabase_open": { "iconPath": "../../icons/folder_type_supabase_opened.svg" },
323 "_fd_svelte": { "iconPath": "../../icons/folder_type_svelte.svg" },
324 "_fd_svelte_open": { "iconPath": "../../icons/folder_type_svelte_opened.svg" },
325 "_fd_tauri": { "iconPath": "../../icons/folder_type_tauri.svg" },
326 "_fd_tauri_open": { "iconPath": "../../icons/folder_type_tauri_opened.svg" },
327 "_fd_temp": { "iconPath": "../../icons/folder_type_temp.svg" },
328 "_fd_temp_open": { "iconPath": "../../icons/folder_type_temp_opened.svg" },
329 "_fd_template": { "iconPath": "../../icons/folder_type_template.svg" },
330 "_fd_template_open": { "iconPath": "../../icons/folder_type_template_opened.svg" },
331 "_fd_test": { "iconPath": "../../icons/folder_type_test.svg" },
332 "_fd_test_open": { "iconPath": "../../icons/folder_type_test_opened.svg" },
333 "_fd_theme": { "iconPath": "../../icons/folder_type_theme.svg" },
334 "_fd_theme_open": { "iconPath": "../../icons/folder_type_theme_opened.svg" },
335 "_fd_tools": { "iconPath": "../../icons/folder_type_tools.svg" },
336 "_fd_tools_open": { "iconPath": "../../icons/folder_type_tools_opened.svg" },
337 "_fd_travis": { "iconPath": "../../icons/folder_type_travis.svg" },
338 "_fd_travis_open": { "iconPath": "../../icons/folder_type_travis_opened.svg" },
339 "_fd_trunk": { "iconPath": "../../icons/folder_type_trunk.svg" },
340 "_fd_trunk_open": { "iconPath": "../../icons/folder_type_trunk_opened.svg" },
341 "_fd_turbo": { "iconPath": "../../icons/folder_type_turbo.svg" },
342 "_fd_turbo_open": { "iconPath": "../../icons/folder_type_turbo_opened.svg" },
343 "_fd_typescript": { "iconPath": "../../icons/folder_type_typescript.svg" },
344 "_fd_typescript_open": { "iconPath": "../../icons/folder_type_typescript_opened.svg" },
345 "_fd_typings": { "iconPath": "../../icons/folder_type_typings.svg" },
346 "_fd_typings_open": { "iconPath": "../../icons/folder_type_typings_opened.svg" },
347 "_fd_vagrant": { "iconPath": "../../icons/folder_type_vagrant.svg" },
348 "_fd_vagrant_open": { "iconPath": "../../icons/folder_type_vagrant_opened.svg" },
349 "_fd_vercel": { "iconPath": "../../icons/folder_type_vercel.svg" },
350 "_fd_vercel_open": { "iconPath": "../../icons/folder_type_vercel_opened.svg" },
351 "_fd_video": { "iconPath": "../../icons/folder_type_video.svg" },
352 "_fd_video_open": { "iconPath": "../../icons/folder_type_video_opened.svg" },
353 "_fd_view": { "iconPath": "../../icons/folder_type_view.svg" },
354 "_fd_view_open": { "iconPath": "../../icons/folder_type_view_opened.svg" },
355 "_fd_vitepress": { "iconPath": "../../icons/folder_type_vitepress.svg" },
356 "_fd_vitepress_open": { "iconPath": "../../icons/folder_type_vitepress_opened.svg" },
357 "_fd_vs": { "iconPath": "../../icons/folder_type_vs.svg" },
358 "_fd_vs_open": { "iconPath": "../../icons/folder_type_vs_opened.svg" },
359 "_fd_vscode": { "iconPath": "../../icons/folder_type_vscode.svg" },
360 "_fd_vscode_open": { "iconPath": "../../icons/folder_type_vscode_opened.svg" },
361 "_fd_vscode_test": { "iconPath": "../../icons/folder_type_vscode_test.svg" },
362 "_fd_vscode_test_open": { "iconPath": "../../icons/folder_type_vscode_test_opened.svg" },
363 "_fd_wasm": { "iconPath": "../../icons/folder_type_wasm.svg" },
364 "_fd_wasm_open": { "iconPath": "../../icons/folder_type_wasm_opened.svg" },
365 "_fd_webpack": { "iconPath": "../../icons/folder_type_webpack.svg" },
366 "_fd_webpack_open": { "iconPath": "../../icons/folder_type_webpack_opened.svg" },
367 "_fd_windows": { "iconPath": "../../icons/folder_type_windows.svg" },
368 "_fd_windows_open": { "iconPath": "../../icons/folder_type_windows_opened.svg" },
369 "_fd_windsurf": { "iconPath": "../../icons/folder_type_windsurf.svg" },
370 "_fd_windsurf_open": { "iconPath": "../../icons/folder_type_windsurf_opened.svg" },
371 "_fd_wit": { "iconPath": "../../icons/folder_type_wit.svg" },
372 "_fd_wit_open": { "iconPath": "../../icons/folder_type_wit_opened.svg" },
373 "_fd_www": { "iconPath": "../../icons/folder_type_www.svg" },
374 "_fd_www_open": { "iconPath": "../../icons/folder_type_www_opened.svg" },
375 "_fd_yarn": { "iconPath": "../../icons/folder_type_yarn.svg" },
376 "_fd_yarn_open": { "iconPath": "../../icons/folder_type_yarn_opened.svg" },
377 "_fd_zed": { "iconPath": "../../icons/folder_type_zed.svg" },
378 "_fd_zed_open": { "iconPath": "../../icons/folder_type_zed_opened.svg" },
379 "_fd_light_zed": { "iconPath": "../../icons/folder_type_light_zed.svg" },
380 "_fd_light_zed_open": { "iconPath": "../../icons/folder_type_light_zed_opened.svg" },
381 "_f_access": { "iconPath": "../../icons/file_type_access.svg" },
382 "_f_actionscript": { "iconPath": "../../icons/file_type_actionscript.svg" },
383 "_f_ada": { "iconPath": "../../icons/file_type_ada.svg" },
384 "_f_light_ada": { "iconPath": "../../icons/file_type_light_ada.svg" },
385 "_f_advpl": { "iconPath": "../../icons/file_type_advpl.svg" },
386 "_f_affectscript": { "iconPath": "../../icons/file_type_affectscript.svg" },
387 "_f_affinity": { "iconPath": "../../icons/file_type_affinity.svg" },
388 "_f_affinitydesigner": { "iconPath": "../../icons/file_type_affinitydesigner.svg" },
389 "_f_affinityphoto": { "iconPath": "../../icons/file_type_affinityphoto.svg" },
390 "_f_affinitypublisher": { "iconPath": "../../icons/file_type_affinitypublisher.svg" },
391 "_f_agda": { "iconPath": "../../icons/file_type_agda.svg" },
392 "_f_light_agda": { "iconPath": "../../icons/file_type_light_agda.svg" },
393 "_f_agents": { "iconPath": "../../icons/file_type_agents.svg" },
394 "_f_light_agents": { "iconPath": "../../icons/file_type_light_agents.svg" },
395 "_f_ai": { "iconPath": "../../icons/file_type_ai.svg" },
396 "_f_al": { "iconPath": "../../icons/file_type_al.svg" },
397 "_f_al_dal": { "iconPath": "../../icons/file_type_al_dal.svg" },
398 "_f_alchemy": { "iconPath": "../../icons/file_type_alchemy.svg" },
399 "_f_allcontributors": { "iconPath": "../../icons/file_type_allcontributors.svg" },
400 "_f_alloy": { "iconPath": "../../icons/file_type_alloy.svg" },
401 "_f_allure": { "iconPath": "../../icons/file_type_allure.svg" },
402 "_f_angular": { "iconPath": "../../icons/file_type_angular.svg" },
403 "_f_ansible": { "iconPath": "../../icons/file_type_ansible.svg" },
404 "_f_antlers_html": { "iconPath": "../../icons/file_type_antlers_html.svg" },
405 "_f_antlr": { "iconPath": "../../icons/file_type_antlr.svg" },
406 "_f_anyscript": { "iconPath": "../../icons/file_type_anyscript.svg" },
407 "_f_apache": { "iconPath": "../../icons/file_type_apache.svg" },
408 "_f_apex": { "iconPath": "../../icons/file_type_apex.svg" },
409 "_f_api_extractor": { "iconPath": "../../icons/file_type_api_extractor.svg" },
410 "_f_apib": { "iconPath": "../../icons/file_type_apib.svg" },
411 "_f_apl": { "iconPath": "../../icons/file_type_apl.svg" },
412 "_f_light_apl": { "iconPath": "../../icons/file_type_light_apl.svg" },
413 "_f_applescript": { "iconPath": "../../icons/file_type_applescript.svg" },
414 "_f_appscript": { "iconPath": "../../icons/file_type_appscript.svg" },
415 "_f_appsemble": { "iconPath": "../../icons/file_type_appsemble.svg" },
416 "_f_appveyor": { "iconPath": "../../icons/file_type_appveyor.svg" },
417 "_f_arduino": { "iconPath": "../../icons/file_type_arduino.svg" },
418 "_f_asciidoc": { "iconPath": "../../icons/file_type_asciidoc.svg" },
419 "_f_aseprite": { "iconPath": "../../icons/file_type_aseprite.svg" },
420 "_f_asp": { "iconPath": "../../icons/file_type_asp.svg" },
421 "_f_aspx": { "iconPath": "../../icons/file_type_aspx.svg" },
422 "_f_assembly": { "iconPath": "../../icons/file_type_assembly.svg" },
423 "_f_assembly_script": { "iconPath": "../../icons/file_type_assembly_script.svg" },
424 "_f_astro": { "iconPath": "../../icons/file_type_astro.svg" },
425 "_f_light_astro": { "iconPath": "../../icons/file_type_light_astro.svg" },
426 "_f_astroconfig": { "iconPath": "../../icons/file_type_astroconfig.svg" },
427 "_f_light_astroconfig": { "iconPath": "../../icons/file_type_light_astroconfig.svg" },
428 "_f_atom": { "iconPath": "../../icons/file_type_atom.svg" },
429 "_f_ats": { "iconPath": "../../icons/file_type_ats.svg" },
430 "_f_attw": { "iconPath": "../../icons/file_type_attw.svg" },
431 "_f_audio": { "iconPath": "../../icons/file_type_audio.svg" },
432 "_f_aurelia": { "iconPath": "../../icons/file_type_aurelia.svg" },
433 "_f_autohotkey": { "iconPath": "../../icons/file_type_autohotkey.svg" },
434 "_f_autoit": { "iconPath": "../../icons/file_type_autoit.svg" },
435 "_f_avif": { "iconPath": "../../icons/file_type_avif.svg" },
436 "_f_avro": { "iconPath": "../../icons/file_type_avro.svg" },
437 "_f_awk": { "iconPath": "../../icons/file_type_awk.svg" },
438 "_f_aws": { "iconPath": "../../icons/file_type_aws.svg" },
439 "_f_azure": { "iconPath": "../../icons/file_type_azure.svg" },
440 "_f_azurepipelines": { "iconPath": "../../icons/file_type_azurepipelines.svg" },
441 "_f_babel": { "iconPath": "../../icons/file_type_babel.svg" },
442 "_f_light_babel": { "iconPath": "../../icons/file_type_light_babel.svg" },
443 "_f_bak": { "iconPath": "../../icons/file_type_bak.svg" },
444 "_f_ballerina": { "iconPath": "../../icons/file_type_ballerina.svg" },
445 "_f_bat": { "iconPath": "../../icons/file_type_bat.svg" },
446 "_f_bats": { "iconPath": "../../icons/file_type_bats.svg" },
447 "_f_bazaar": { "iconPath": "../../icons/file_type_bazaar.svg" },
448 "_f_bazel": { "iconPath": "../../icons/file_type_bazel.svg" },
449 "_f_bazel_ignore": { "iconPath": "../../icons/file_type_bazel_ignore.svg" },
450 "_f_bazel_version": { "iconPath": "../../icons/file_type_bazel_version.svg" },
451 "_f_befunge": { "iconPath": "../../icons/file_type_befunge.svg" },
452 "_f_bicep": { "iconPath": "../../icons/file_type_bicep.svg" },
453 "_f_biml": { "iconPath": "../../icons/file_type_biml.svg" },
454 "_f_binary": { "iconPath": "../../icons/file_type_binary.svg" },
455 "_f_biome": { "iconPath": "../../icons/file_type_biome.svg" },
456 "_f_bitbucketpipeline": { "iconPath": "../../icons/file_type_bitbucketpipeline.svg" },
457 "_f_bithound": { "iconPath": "../../icons/file_type_bithound.svg" },
458 "_f_blade": { "iconPath": "../../icons/file_type_blade.svg" },
459 "_f_blender": { "iconPath": "../../icons/file_type_blender.svg" },
460 "_f_blitzbasic": { "iconPath": "../../icons/file_type_blitzbasic.svg" },
461 "_f_bolt": { "iconPath": "../../icons/file_type_bolt.svg" },
462 "_f_bosque": { "iconPath": "../../icons/file_type_bosque.svg" },
463 "_f_bower": { "iconPath": "../../icons/file_type_bower.svg" },
464 "_f_browserslist": { "iconPath": "../../icons/file_type_browserslist.svg" },
465 "_f_bruno": { "iconPath": "../../icons/file_type_bruno.svg" },
466 "_f_buckbuild": { "iconPath": "../../icons/file_type_buckbuild.svg" },
467 "_f_buf": { "iconPath": "../../icons/file_type_buf.svg" },
468 "_f_bun": { "iconPath": "../../icons/file_type_bun.svg" },
469 "_f_bundlemon": { "iconPath": "../../icons/file_type_bundlemon.svg" },
470 "_f_bundler": { "iconPath": "../../icons/file_type_bundler.svg" },
471 "_f_bunfig": { "iconPath": "../../icons/file_type_bunfig.svg" },
472 "_f_c": { "iconPath": "../../icons/file_type_c.svg" },
473 "_f_c3": { "iconPath": "../../icons/file_type_c3.svg" },
474 "_f_c_al": { "iconPath": "../../icons/file_type_c_al.svg" },
475 "_f_cabal": { "iconPath": "../../icons/file_type_cabal.svg" },
476 "_f_caddy": { "iconPath": "../../icons/file_type_caddy.svg" },
477 "_f_cake": { "iconPath": "../../icons/file_type_cake.svg" },
478 "_f_cakephp": { "iconPath": "../../icons/file_type_cakephp.svg" },
479 "_f_capacitor": { "iconPath": "../../icons/file_type_capacitor.svg" },
480 "_f_capnp": { "iconPath": "../../icons/file_type_capnp.svg" },
481 "_f_cargo": { "iconPath": "../../icons/file_type_cargo.svg" },
482 "_f_casc": { "iconPath": "../../icons/file_type_casc.svg" },
483 "_f_cddl": { "iconPath": "../../icons/file_type_cddl.svg" },
484 "_f_cert": { "iconPath": "../../icons/file_type_cert.svg" },
485 "_f_ceylon": { "iconPath": "../../icons/file_type_ceylon.svg" },
486 "_f_cf": { "iconPath": "../../icons/file_type_cf.svg" },
487 "_f_cfc": { "iconPath": "../../icons/file_type_cfc.svg" },
488 "_f_cfm": { "iconPath": "../../icons/file_type_cfm.svg" },
489 "_f_changie": { "iconPath": "../../icons/file_type_changie.svg" },
490 "_f_cheader": { "iconPath": "../../icons/file_type_cheader.svg" },
491 "_f_chef": { "iconPath": "../../icons/file_type_chef.svg" },
492 "_f_chef_cookbook": { "iconPath": "../../icons/file_type_chef_cookbook.svg" },
493 "_f_circleci": { "iconPath": "../../icons/file_type_circleci.svg" },
494 "_f_light_circleci": { "iconPath": "../../icons/file_type_light_circleci.svg" },
495 "_f_circom": { "iconPath": "../../icons/file_type_circom.svg" },
496 "_f_class": { "iconPath": "../../icons/file_type_class.svg" },
497 "_f_claude": { "iconPath": "../../icons/file_type_claude.svg" },
498 "_f_clojure": { "iconPath": "../../icons/file_type_clojure.svg" },
499 "_f_clojurescript": { "iconPath": "../../icons/file_type_clojurescript.svg" },
500 "_f_cloudflare": { "iconPath": "../../icons/file_type_cloudflare.svg" },
501 "_f_cloudfoundry": { "iconPath": "../../icons/file_type_cloudfoundry.svg" },
502 "_f_light_cloudfoundry": { "iconPath": "../../icons/file_type_light_cloudfoundry.svg" },
503 "_f_cmake": { "iconPath": "../../icons/file_type_cmake.svg" },
504 "_f_cobol": { "iconPath": "../../icons/file_type_cobol.svg" },
505 "_f_codacy": { "iconPath": "../../icons/file_type_codacy.svg" },
506 "_f_light_codacy": { "iconPath": "../../icons/file_type_light_codacy.svg" },
507 "_f_codeclimate": { "iconPath": "../../icons/file_type_codeclimate.svg" },
508 "_f_light_codeclimate": { "iconPath": "../../icons/file_type_light_codeclimate.svg" },
509 "_f_codecov": { "iconPath": "../../icons/file_type_codecov.svg" },
510 "_f_codekit": { "iconPath": "../../icons/file_type_codekit.svg" },
511 "_f_codemagic": { "iconPath": "../../icons/file_type_codemagic.svg" },
512 "_f_codeowners": { "iconPath": "../../icons/file_type_codeowners.svg" },
513 "_f_light_codeowners": { "iconPath": "../../icons/file_type_light_codeowners.svg" },
514 "_f_codeql": { "iconPath": "../../icons/file_type_codeql.svg" },
515 "_f_coderabbit": { "iconPath": "../../icons/file_type_coderabbit.svg" },
516 "_f_coffeelint": { "iconPath": "../../icons/file_type_coffeelint.svg" },
517 "_f_coffeescript": { "iconPath": "../../icons/file_type_coffeescript.svg" },
518 "_f_commitizen": { "iconPath": "../../icons/file_type_commitizen.svg" },
519 "_f_commitlint": { "iconPath": "../../icons/file_type_commitlint.svg" },
520 "_f_compass": { "iconPath": "../../icons/file_type_compass.svg" },
521 "_f_composer": { "iconPath": "../../icons/file_type_composer.svg" },
522 "_f_conan": { "iconPath": "../../icons/file_type_conan.svg" },
523 "_f_conda": { "iconPath": "../../icons/file_type_conda.svg" },
524 "_f_config": { "iconPath": "../../icons/file_type_config.svg" },
525 "_f_light_config": { "iconPath": "../../icons/file_type_light_config.svg" },
526 "_f_confluence": { "iconPath": "../../icons/file_type_confluence.svg" },
527 "_f_copilot": { "iconPath": "../../icons/file_type_copilot.svg" },
528 "_f_light_copilot": { "iconPath": "../../icons/file_type_light_copilot.svg" },
529 "_f_coverage": { "iconPath": "../../icons/file_type_coverage.svg" },
530 "_f_coveralls": { "iconPath": "../../icons/file_type_coveralls.svg" },
531 "_f_cpp": { "iconPath": "../../icons/file_type_cpp.svg" },
532 "_f_cppheader": { "iconPath": "../../icons/file_type_cppheader.svg" },
533 "_f_craco": { "iconPath": "../../icons/file_type_craco.svg" },
534 "_f_crowdin": { "iconPath": "../../icons/file_type_crowdin.svg" },
535 "_f_crystal": { "iconPath": "../../icons/file_type_crystal.svg" },
536 "_f_light_crystal": { "iconPath": "../../icons/file_type_light_crystal.svg" },
537 "_f_csharp": { "iconPath": "../../icons/file_type_csharp.svg" },
538 "_f_cspell": { "iconPath": "../../icons/file_type_cspell.svg" },
539 "_f_csproj": { "iconPath": "../../icons/file_type_csproj.svg" },
540 "_f_css": { "iconPath": "../../icons/file_type_css.svg" },
541 "_f_csscomb": { "iconPath": "../../icons/file_type_csscomb.svg" },
542 "_f_csslint": { "iconPath": "../../icons/file_type_csslint.svg" },
543 "_f_cssmap": { "iconPath": "../../icons/file_type_cssmap.svg" },
544 "_f_cucumber": { "iconPath": "../../icons/file_type_cucumber.svg" },
545 "_f_cuda": { "iconPath": "../../icons/file_type_cuda.svg" },
546 "_f_cursorrules": { "iconPath": "../../icons/file_type_cursorrules.svg" },
547 "_f_cvs": { "iconPath": "../../icons/file_type_cvs.svg" },
548 "_f_cypress": { "iconPath": "../../icons/file_type_cypress.svg" },
549 "_f_light_cypress": { "iconPath": "../../icons/file_type_light_cypress.svg" },
550 "_f_cypress_spec": { "iconPath": "../../icons/file_type_cypress_spec.svg" },
551 "_f_light_cypress_spec": { "iconPath": "../../icons/file_type_light_cypress_spec.svg" },
552 "_f_cython": { "iconPath": "../../icons/file_type_cython.svg" },
553 "_f_dal": { "iconPath": "../../icons/file_type_dal.svg" },
554 "_f_darcs": { "iconPath": "../../icons/file_type_darcs.svg" },
555 "_f_dartlang": { "iconPath": "../../icons/file_type_dartlang.svg" },
556 "_f_dartlang_generated": { "iconPath": "../../icons/file_type_dartlang_generated.svg" },
557 "_f_dartlang_ignore": { "iconPath": "../../icons/file_type_dartlang_ignore.svg" },
558 "_f_datadog": { "iconPath": "../../icons/file_type_datadog.svg" },
559 "_f_db": { "iconPath": "../../icons/file_type_db.svg" },
560 "_f_light_db": { "iconPath": "../../icons/file_type_light_db.svg" },
561 "_f_debian": { "iconPath": "../../icons/file_type_debian.svg" },
562 "_f_delphi": { "iconPath": "../../icons/file_type_delphi.svg" },
563 "_f_deno": { "iconPath": "../../icons/file_type_deno.svg" },
564 "_f_denoify": { "iconPath": "../../icons/file_type_denoify.svg" },
565 "_f_dependabot": { "iconPath": "../../icons/file_type_dependabot.svg" },
566 "_f_dependencies": { "iconPath": "../../icons/file_type_dependencies.svg" },
567 "_f_design_tokens": { "iconPath": "../../icons/file_type_design_tokens.svg" },
568 "_f_devcontainer": { "iconPath": "../../icons/file_type_devcontainer.svg" },
569 "_f_devvit": { "iconPath": "../../icons/file_type_devvit.svg" },
570 "_f_dhall": { "iconPath": "../../icons/file_type_dhall.svg" },
571 "_f_light_dhall": { "iconPath": "../../icons/file_type_light_dhall.svg" },
572 "_f_diff": { "iconPath": "../../icons/file_type_diff.svg" },
573 "_f_django": { "iconPath": "../../icons/file_type_django.svg" },
574 "_f_dlang": { "iconPath": "../../icons/file_type_dlang.svg" },
575 "_f_docker": { "iconPath": "../../icons/file_type_docker.svg" },
576 "_f_dockertest": { "iconPath": "../../icons/file_type_dockertest.svg" },
577 "_f_docpad": { "iconPath": "../../icons/file_type_docpad.svg" },
578 "_f_light_docpad": { "iconPath": "../../icons/file_type_light_docpad.svg" },
579 "_f_docusaurus": { "iconPath": "../../icons/file_type_docusaurus.svg" },
580 "_f_docz": { "iconPath": "../../icons/file_type_docz.svg" },
581 "_f_dojo": { "iconPath": "../../icons/file_type_dojo.svg" },
582 "_f_doppler": { "iconPath": "../../icons/file_type_doppler.svg" },
583 "_f_dotenv": { "iconPath": "../../icons/file_type_dotenv.svg" },
584 "_f_dotjs": { "iconPath": "../../icons/file_type_dotjs.svg" },
585 "_f_doxygen": { "iconPath": "../../icons/file_type_doxygen.svg" },
586 "_f_drawio": { "iconPath": "../../icons/file_type_drawio.svg" },
587 "_f_drone": { "iconPath": "../../icons/file_type_drone.svg" },
588 "_f_light_drone": { "iconPath": "../../icons/file_type_light_drone.svg" },
589 "_f_drools": { "iconPath": "../../icons/file_type_drools.svg" },
590 "_f_dtd": { "iconPath": "../../icons/file_type_dtd.svg" },
591 "_f_dune": { "iconPath": "../../icons/file_type_dune.svg" },
592 "_f_dustjs": { "iconPath": "../../icons/file_type_dustjs.svg" },
593 "_f_dvc": { "iconPath": "../../icons/file_type_dvc.svg" },
594 "_f_dylan": { "iconPath": "../../icons/file_type_dylan.svg" },
595 "_f_earthly": { "iconPath": "../../icons/file_type_earthly.svg" },
596 "_f_eas-metadata": { "iconPath": "../../icons/file_type_eas-metadata.svg" },
597 "_f_light_eas-metadata": { "iconPath": "../../icons/file_type_light_eas-metadata.svg" },
598 "_f_edge": { "iconPath": "../../icons/file_type_edge.svg" },
599 "_f_editorconfig": { "iconPath": "../../icons/file_type_editorconfig.svg" },
600 "_f_eex": { "iconPath": "../../icons/file_type_eex.svg" },
601 "_f_ejs": { "iconPath": "../../icons/file_type_ejs.svg" },
602 "_f_elastic": { "iconPath": "../../icons/file_type_elastic.svg" },
603 "_f_elasticbeanstalk": { "iconPath": "../../icons/file_type_elasticbeanstalk.svg" },
604 "_f_electron": { "iconPath": "../../icons/file_type_electron.svg" },
605 "_f_eleventy": { "iconPath": "../../icons/file_type_eleventy.svg" },
606 "_f_light_eleventy": { "iconPath": "../../icons/file_type_light_eleventy.svg" },
607 "_f_elixir": { "iconPath": "../../icons/file_type_elixir.svg" },
608 "_f_elm": { "iconPath": "../../icons/file_type_elm.svg" },
609 "_f_emacs": { "iconPath": "../../icons/file_type_emacs.svg" },
610 "_f_ember": { "iconPath": "../../icons/file_type_ember.svg" },
611 "_f_ensime": { "iconPath": "../../icons/file_type_ensime.svg" },
612 "_f_eps": { "iconPath": "../../icons/file_type_eps.svg" },
613 "_f_epub": { "iconPath": "../../icons/file_type_epub.svg" },
614 "_f_erb": { "iconPath": "../../icons/file_type_erb.svg" },
615 "_f_erlang": { "iconPath": "../../icons/file_type_erlang.svg" },
616 "_f_esbuild": { "iconPath": "../../icons/file_type_esbuild.svg" },
617 "_f_eslint": { "iconPath": "../../icons/file_type_eslint.svg" },
618 "_f_esphome": { "iconPath": "../../icons/file_type_esphome.svg" },
619 "_f_light_esphome": { "iconPath": "../../icons/file_type_light_esphome.svg" },
620 "_f_excalidraw": { "iconPath": "../../icons/file_type_excalidraw.svg" },
621 "_f_excel": { "iconPath": "../../icons/file_type_excel.svg" },
622 "_f_expo": { "iconPath": "../../icons/file_type_expo.svg" },
623 "_f_light_expo": { "iconPath": "../../icons/file_type_light_expo.svg" },
624 "_f_falcon": { "iconPath": "../../icons/file_type_falcon.svg" },
625 "_f_fantasticon": { "iconPath": "../../icons/file_type_fantasticon.svg" },
626 "_f_fastly": { "iconPath": "../../icons/file_type_fastly.svg" },
627 "_f_fauna": { "iconPath": "../../icons/file_type_fauna.svg" },
628 "_f_favicon": { "iconPath": "../../icons/file_type_favicon.svg" },
629 "_f_fbx": { "iconPath": "../../icons/file_type_fbx.svg" },
630 "_f_firebase": { "iconPath": "../../icons/file_type_firebase.svg" },
631 "_f_firebasehosting": { "iconPath": "../../icons/file_type_firebasehosting.svg" },
632 "_f_light_firebasehosting": { "iconPath": "../../icons/file_type_light_firebasehosting.svg" },
633 "_f_firebasestorage": { "iconPath": "../../icons/file_type_firebasestorage.svg" },
634 "_f_firestore": { "iconPath": "../../icons/file_type_firestore.svg" },
635 "_f_fitbit": { "iconPath": "../../icons/file_type_fitbit.svg" },
636 "_f_fla": { "iconPath": "../../icons/file_type_fla.svg" },
637 "_f_light_fla": { "iconPath": "../../icons/file_type_light_fla.svg" },
638 "_f_flareact": { "iconPath": "../../icons/file_type_flareact.svg" },
639 "_f_flash": { "iconPath": "../../icons/file_type_flash.svg" },
640 "_f_floobits": { "iconPath": "../../icons/file_type_floobits.svg" },
641 "_f_flow": { "iconPath": "../../icons/file_type_flow.svg" },
642 "_f_flutter": { "iconPath": "../../icons/file_type_flutter.svg" },
643 "_f_flutter_package": { "iconPath": "../../icons/file_type_flutter_package.svg" },
644 "_f_flyio": { "iconPath": "../../icons/file_type_flyio.svg" },
645 "_f_font": { "iconPath": "../../icons/file_type_font.svg" },
646 "_f_light_font": { "iconPath": "../../icons/file_type_light_font.svg" },
647 "_f_formkit": { "iconPath": "../../icons/file_type_formkit.svg" },
648 "_f_fortran": { "iconPath": "../../icons/file_type_fortran.svg" },
649 "_f_fossa": { "iconPath": "../../icons/file_type_fossa.svg" },
650 "_f_fossil": { "iconPath": "../../icons/file_type_fossil.svg" },
651 "_f_freemarker": { "iconPath": "../../icons/file_type_freemarker.svg" },
652 "_f_frontcommerce": { "iconPath": "../../icons/file_type_frontcommerce.svg" },
653 "_f_fsharp": { "iconPath": "../../icons/file_type_fsharp.svg" },
654 "_f_fsproj": { "iconPath": "../../icons/file_type_fsproj.svg" },
655 "_f_fthtml": { "iconPath": "../../icons/file_type_fthtml.svg" },
656 "_f_funding": { "iconPath": "../../icons/file_type_funding.svg" },
657 "_f_fusebox": { "iconPath": "../../icons/file_type_fusebox.svg" },
658 "_f_galen": { "iconPath": "../../icons/file_type_galen.svg" },
659 "_f_gamemaker": { "iconPath": "../../icons/file_type_gamemaker.svg" },
660 "_f_gamemaker2": { "iconPath": "../../icons/file_type_gamemaker2.svg" },
661 "_f_light_gamemaker2": { "iconPath": "../../icons/file_type_light_gamemaker2.svg" },
662 "_f_gamemaker81": { "iconPath": "../../icons/file_type_gamemaker81.svg" },
663 "_f_gatsby": { "iconPath": "../../icons/file_type_gatsby.svg" },
664 "_f_gcloud": { "iconPath": "../../icons/file_type_gcloud.svg" },
665 "_f_gcode": { "iconPath": "../../icons/file_type_gcode.svg" },
666 "_f_gdscript": { "iconPath": "../../icons/file_type_gdscript.svg" },
667 "_f_gduid": { "iconPath": "../../icons/file_type_gduid.svg" },
668 "_f_gemini": { "iconPath": "../../icons/file_type_gemini.svg" },
669 "_f_genstat": { "iconPath": "../../icons/file_type_genstat.svg" },
670 "_f_geojson": { "iconPath": "../../icons/file_type_geojson.svg" },
671 "_f_gimp": { "iconPath": "../../icons/file_type_gimp.svg" },
672 "_f_git": { "iconPath": "../../icons/file_type_git.svg" },
673 "_f_gitlab": { "iconPath": "../../icons/file_type_gitlab.svg" },
674 "_f_gitpod": { "iconPath": "../../icons/file_type_gitpod.svg" },
675 "_f_gleam": { "iconPath": "../../icons/file_type_gleam.svg" },
676 "_f_gleamconfig": { "iconPath": "../../icons/file_type_gleamconfig.svg" },
677 "_f_glide": { "iconPath": "../../icons/file_type_glide.svg" },
678 "_f_glimmer": { "iconPath": "../../icons/file_type_glimmer.svg" },
679 "_f_glitter": { "iconPath": "../../icons/file_type_glitter.svg" },
680 "_f_glsl": { "iconPath": "../../icons/file_type_glsl.svg" },
681 "_f_gltf": { "iconPath": "../../icons/file_type_gltf.svg" },
682 "_f_glyphs": { "iconPath": "../../icons/file_type_glyphs.svg" },
683 "_f_gnu": { "iconPath": "../../icons/file_type_gnu.svg" },
684 "_f_gnuplot": { "iconPath": "../../icons/file_type_gnuplot.svg" },
685 "_f_go": { "iconPath": "../../icons/file_type_go.svg" },
686 "_f_go_package": { "iconPath": "../../icons/file_type_go_package.svg" },
687 "_f_go_work": { "iconPath": "../../icons/file_type_go_work.svg" },
688 "_f_goctl": { "iconPath": "../../icons/file_type_goctl.svg" },
689 "_f_godot": { "iconPath": "../../icons/file_type_godot.svg" },
690 "_f_gpg": { "iconPath": "../../icons/file_type_gpg.svg" },
691 "_f_gradle": { "iconPath": "../../icons/file_type_gradle.svg" },
692 "_f_light_gradle": { "iconPath": "../../icons/file_type_light_gradle.svg" },
693 "_f_grain": { "iconPath": "../../icons/file_type_grain.svg" },
694 "_f_graphql": { "iconPath": "../../icons/file_type_graphql.svg" },
695 "_f_graphql_config": { "iconPath": "../../icons/file_type_graphql_config.svg" },
696 "_f_graphviz": { "iconPath": "../../icons/file_type_graphviz.svg" },
697 "_f_greenkeeper": { "iconPath": "../../icons/file_type_greenkeeper.svg" },
698 "_f_gridsome": { "iconPath": "../../icons/file_type_gridsome.svg" },
699 "_f_grit": { "iconPath": "../../icons/file_type_grit.svg" },
700 "_f_groovy": { "iconPath": "../../icons/file_type_groovy.svg" },
701 "_f_grunt": { "iconPath": "../../icons/file_type_grunt.svg" },
702 "_f_gulp": { "iconPath": "../../icons/file_type_gulp.svg" },
703 "_f_haml": { "iconPath": "../../icons/file_type_haml.svg" },
704 "_f_handlebars": { "iconPath": "../../icons/file_type_handlebars.svg" },
705 "_f_harbour": { "iconPath": "../../icons/file_type_harbour.svg" },
706 "_f_hardhat": { "iconPath": "../../icons/file_type_hardhat.svg" },
707 "_f_hashicorp": { "iconPath": "../../icons/file_type_hashicorp.svg" },
708 "_f_light_hashicorp": { "iconPath": "../../icons/file_type_light_hashicorp.svg" },
709 "_f_haskell": { "iconPath": "../../icons/file_type_haskell.svg" },
710 "_f_haxe": { "iconPath": "../../icons/file_type_haxe.svg" },
711 "_f_haxecheckstyle": { "iconPath": "../../icons/file_type_haxecheckstyle.svg" },
712 "_f_haxedevelop": { "iconPath": "../../icons/file_type_haxedevelop.svg" },
713 "_f_helix": { "iconPath": "../../icons/file_type_helix.svg" },
714 "_f_helm": { "iconPath": "../../icons/file_type_helm.svg" },
715 "_f_histoire": { "iconPath": "../../icons/file_type_histoire.svg" },
716 "_f_hjson": { "iconPath": "../../icons/file_type_hjson.svg" },
717 "_f_light_hjson": { "iconPath": "../../icons/file_type_light_hjson.svg" },
718 "_f_hlsl": { "iconPath": "../../icons/file_type_hlsl.svg" },
719 "_f_homeassistant": { "iconPath": "../../icons/file_type_homeassistant.svg" },
720 "_f_horusec": { "iconPath": "../../icons/file_type_horusec.svg" },
721 "_f_host": { "iconPath": "../../icons/file_type_host.svg" },
722 "_f_html": { "iconPath": "../../icons/file_type_html.svg" },
723 "_f_htmlhint": { "iconPath": "../../icons/file_type_htmlhint.svg" },
724 "_f_htmlvalidate": { "iconPath": "../../icons/file_type_htmlvalidate.svg" },
725 "_f_http": { "iconPath": "../../icons/file_type_http.svg" },
726 "_f_hugo": { "iconPath": "../../icons/file_type_hugo.svg" },
727 "_f_humanstxt": { "iconPath": "../../icons/file_type_humanstxt.svg" },
728 "_f_hunspell": { "iconPath": "../../icons/file_type_hunspell.svg" },
729 "_f_husky": { "iconPath": "../../icons/file_type_husky.svg" },
730 "_f_hy": { "iconPath": "../../icons/file_type_hy.svg" },
731 "_f_hygen": { "iconPath": "../../icons/file_type_hygen.svg" },
732 "_f_hypr": { "iconPath": "../../icons/file_type_hypr.svg" },
733 "_f_icl": { "iconPath": "../../icons/file_type_icl.svg" },
734 "_f_idris": { "iconPath": "../../icons/file_type_idris.svg" },
735 "_f_idrisbin": { "iconPath": "../../icons/file_type_idrisbin.svg" },
736 "_f_idrispkg": { "iconPath": "../../icons/file_type_idrispkg.svg" },
737 "_f_image": { "iconPath": "../../icons/file_type_image.svg" },
738 "_f_imba": { "iconPath": "../../icons/file_type_imba.svg" },
739 "_f_inc": { "iconPath": "../../icons/file_type_inc.svg" },
740 "_f_infopath": { "iconPath": "../../icons/file_type_infopath.svg" },
741 "_f_informix": { "iconPath": "../../icons/file_type_informix.svg" },
742 "_f_ini": { "iconPath": "../../icons/file_type_ini.svg" },
743 "_f_light_ini": { "iconPath": "../../icons/file_type_light_ini.svg" },
744 "_f_ink": { "iconPath": "../../icons/file_type_ink.svg" },
745 "_f_innosetup": { "iconPath": "../../icons/file_type_innosetup.svg" },
746 "_f_io": { "iconPath": "../../icons/file_type_io.svg" },
747 "_f_light_io": { "iconPath": "../../icons/file_type_light_io.svg" },
748 "_f_iodine": { "iconPath": "../../icons/file_type_iodine.svg" },
749 "_f_ionic": { "iconPath": "../../icons/file_type_ionic.svg" },
750 "_f_jake": { "iconPath": "../../icons/file_type_jake.svg" },
751 "_f_janet": { "iconPath": "../../icons/file_type_janet.svg" },
752 "_f_jar": { "iconPath": "../../icons/file_type_jar.svg" },
753 "_f_jasmine": { "iconPath": "../../icons/file_type_jasmine.svg" },
754 "_f_java": { "iconPath": "../../icons/file_type_java.svg" },
755 "_f_jbuilder": { "iconPath": "../../icons/file_type_jbuilder.svg" },
756 "_f_jekyll": { "iconPath": "../../icons/file_type_jekyll.svg" },
757 "_f_jenkins": { "iconPath": "../../icons/file_type_jenkins.svg" },
758 "_f_jest": { "iconPath": "../../icons/file_type_jest.svg" },
759 "_f_jest_snapshot": { "iconPath": "../../icons/file_type_jest_snapshot.svg" },
760 "_f_jetbrains": { "iconPath": "../../icons/file_type_jetbrains.svg" },
761 "_f_jinja": { "iconPath": "../../icons/file_type_jinja.svg" },
762 "_f_jpm": { "iconPath": "../../icons/file_type_jpm.svg" },
763 "_f_js": { "iconPath": "../../icons/file_type_js.svg" },
764 "_f_light_js": { "iconPath": "../../icons/file_type_light_js.svg" },
765 "_f_jsbeautify": { "iconPath": "../../icons/file_type_jsbeautify.svg" },
766 "_f_jsconfig": { "iconPath": "../../icons/file_type_jsconfig.svg" },
767 "_f_light_jsconfig": { "iconPath": "../../icons/file_type_light_jsconfig.svg" },
768 "_f_jscpd": { "iconPath": "../../icons/file_type_jscpd.svg" },
769 "_f_jshint": { "iconPath": "../../icons/file_type_jshint.svg" },
770 "_f_jsmap": { "iconPath": "../../icons/file_type_jsmap.svg" },
771 "_f_light_jsmap": { "iconPath": "../../icons/file_type_light_jsmap.svg" },
772 "_f_json": { "iconPath": "../../icons/file_type_json.svg" },
773 "_f_light_json": { "iconPath": "../../icons/file_type_light_json.svg" },
774 "_f_json5": { "iconPath": "../../icons/file_type_json5.svg" },
775 "_f_light_json5": { "iconPath": "../../icons/file_type_light_json5.svg" },
776 "_f_json_schema": { "iconPath": "../../icons/file_type_json_schema.svg" },
777 "_f_light_json_schema": { "iconPath": "../../icons/file_type_light_json_schema.svg" },
778 "_f_jsonld": { "iconPath": "../../icons/file_type_jsonld.svg" },
779 "_f_light_jsonld": { "iconPath": "../../icons/file_type_light_jsonld.svg" },
780 "_f_jsonnet": { "iconPath": "../../icons/file_type_jsonnet.svg" },
781 "_f_jsp": { "iconPath": "../../icons/file_type_jsp.svg" },
782 "_f_jsr": { "iconPath": "../../icons/file_type_jsr.svg" },
783 "_f_jss": { "iconPath": "../../icons/file_type_jss.svg" },
784 "_f_juice": { "iconPath": "../../icons/file_type_juice.svg" },
785 "_f_julia": { "iconPath": "../../icons/file_type_julia.svg" },
786 "_f_jupyter": { "iconPath": "../../icons/file_type_jupyter.svg" },
787 "_f_just": { "iconPath": "../../icons/file_type_just.svg" },
788 "_f_k": { "iconPath": "../../icons/file_type_k.svg" },
789 "_f_karma": { "iconPath": "../../icons/file_type_karma.svg" },
790 "_f_key": { "iconPath": "../../icons/file_type_key.svg" },
791 "_f_kitchenci": { "iconPath": "../../icons/file_type_kitchenci.svg" },
792 "_f_kite": { "iconPath": "../../icons/file_type_kite.svg" },
793 "_f_light_kite": { "iconPath": "../../icons/file_type_light_kite.svg" },
794 "_f_kivy": { "iconPath": "../../icons/file_type_kivy.svg" },
795 "_f_knip": { "iconPath": "../../icons/file_type_knip.svg" },
796 "_f_kos": { "iconPath": "../../icons/file_type_kos.svg" },
797 "_f_kotlin": { "iconPath": "../../icons/file_type_kotlin.svg" },
798 "_f_kusto": { "iconPath": "../../icons/file_type_kusto.svg" },
799 "_f_language_configuration": { "iconPath": "../../icons/file_type_language_configuration.svg" },
800 "_f_lark": { "iconPath": "../../icons/file_type_lark.svg" },
801 "_f_light_lark": { "iconPath": "../../icons/file_type_light_lark.svg" },
802 "_f_latino": { "iconPath": "../../icons/file_type_latino.svg" },
803 "_f_layout": { "iconPath": "../../icons/file_type_layout.svg" },
804 "_f_lean": { "iconPath": "../../icons/file_type_lean.svg" },
805 "_f_light_lean": { "iconPath": "../../icons/file_type_light_lean.svg" },
806 "_f_leanconfig": { "iconPath": "../../icons/file_type_leanconfig.svg" },
807 "_f_light_leanconfig": { "iconPath": "../../icons/file_type_light_leanconfig.svg" },
808 "_f_lefthook": { "iconPath": "../../icons/file_type_lefthook.svg" },
809 "_f_lerna": { "iconPath": "../../icons/file_type_lerna.svg" },
810 "_f_light_lerna": { "iconPath": "../../icons/file_type_light_lerna.svg" },
811 "_f_less": { "iconPath": "../../icons/file_type_less.svg" },
812 "_f_lex": { "iconPath": "../../icons/file_type_lex.svg" },
813 "_f_liara": { "iconPath": "../../icons/file_type_liara.svg" },
814 "_f_libreoffice_base": { "iconPath": "../../icons/file_type_libreoffice_base.svg" },
815 "_f_libreoffice_calc": { "iconPath": "../../icons/file_type_libreoffice_calc.svg" },
816 "_f_libreoffice_draw": { "iconPath": "../../icons/file_type_libreoffice_draw.svg" },
817 "_f_libreoffice_impress": { "iconPath": "../../icons/file_type_libreoffice_impress.svg" },
818 "_f_libreoffice_math": { "iconPath": "../../icons/file_type_libreoffice_math.svg" },
819 "_f_libreoffice_writer": { "iconPath": "../../icons/file_type_libreoffice_writer.svg" },
820 "_f_license": { "iconPath": "../../icons/file_type_license.svg" },
821 "_f_licensebat": { "iconPath": "../../icons/file_type_licensebat.svg" },
822 "_f_lighthouse": { "iconPath": "../../icons/file_type_lighthouse.svg" },
823 "_f_lilypond": { "iconPath": "../../icons/file_type_lilypond.svg" },
824 "_f_lime": { "iconPath": "../../icons/file_type_lime.svg" },
825 "_f_lintstagedrc": { "iconPath": "../../icons/file_type_lintstagedrc.svg" },
826 "_f_liquid": { "iconPath": "../../icons/file_type_liquid.svg" },
827 "_f_lisp": { "iconPath": "../../icons/file_type_lisp.svg" },
828 "_f_livescript": { "iconPath": "../../icons/file_type_livescript.svg" },
829 "_f_lnk": { "iconPath": "../../icons/file_type_lnk.svg" },
830 "_f_locale": { "iconPath": "../../icons/file_type_locale.svg" },
831 "_f_log": { "iconPath": "../../icons/file_type_log.svg" },
832 "_f_lolcode": { "iconPath": "../../icons/file_type_lolcode.svg" },
833 "_f_lsl": { "iconPath": "../../icons/file_type_lsl.svg" },
834 "_f_lua": { "iconPath": "../../icons/file_type_lua.svg" },
835 "_f_luau": { "iconPath": "../../icons/file_type_luau.svg" },
836 "_f_lync": { "iconPath": "../../icons/file_type_lync.svg" },
837 "_f_mailing": { "iconPath": "../../icons/file_type_mailing.svg" },
838 "_f_light_mailing": { "iconPath": "../../icons/file_type_light_mailing.svg" },
839 "_f_manifest": { "iconPath": "../../icons/file_type_manifest.svg" },
840 "_f_manifest_bak": { "iconPath": "../../icons/file_type_manifest_bak.svg" },
841 "_f_manifest_skip": { "iconPath": "../../icons/file_type_manifest_skip.svg" },
842 "_f_map": { "iconPath": "../../icons/file_type_map.svg" },
843 "_f_markdown": { "iconPath": "../../icons/file_type_markdown.svg" },
844 "_f_markdownlint": { "iconPath": "../../icons/file_type_markdownlint.svg" },
845 "_f_markdownlint_ignore": { "iconPath": "../../icons/file_type_markdownlint_ignore.svg" },
846 "_f_marko": { "iconPath": "../../icons/file_type_marko.svg" },
847 "_f_markojs": { "iconPath": "../../icons/file_type_markojs.svg" },
848 "_f_markuplint": { "iconPath": "../../icons/file_type_markuplint.svg" },
849 "_f_light_markuplint": { "iconPath": "../../icons/file_type_light_markuplint.svg" },
850 "_f_master-co": { "iconPath": "../../icons/file_type_master-co.svg" },
851 "_f_matlab": { "iconPath": "../../icons/file_type_matlab.svg" },
852 "_f_maven": { "iconPath": "../../icons/file_type_maven.svg" },
853 "_f_maxscript": { "iconPath": "../../icons/file_type_maxscript.svg" },
854 "_f_maya": { "iconPath": "../../icons/file_type_maya.svg" },
855 "_f_mcp": { "iconPath": "../../icons/file_type_mcp.svg" },
856 "_f_light_mcp": { "iconPath": "../../icons/file_type_light_mcp.svg" },
857 "_f_mdx": { "iconPath": "../../icons/file_type_mdx.svg" },
858 "_f_light_mdx": { "iconPath": "../../icons/file_type_light_mdx.svg" },
859 "_f_mdx-components": { "iconPath": "../../icons/file_type_mdx-components.svg" },
860 "_f_light_mdx-components": { "iconPath": "../../icons/file_type_light_mdx-components.svg" },
861 "_f_mdxlint": { "iconPath": "../../icons/file_type_mdxlint.svg" },
862 "_f_mediawiki": { "iconPath": "../../icons/file_type_mediawiki.svg" },
863 "_f_mercurial": { "iconPath": "../../icons/file_type_mercurial.svg" },
864 "_f_mermaid": { "iconPath": "../../icons/file_type_mermaid.svg" },
865 "_f_meson": { "iconPath": "../../icons/file_type_meson.svg" },
866 "_f_metal": { "iconPath": "../../icons/file_type_metal.svg" },
867 "_f_meteor": { "iconPath": "../../icons/file_type_meteor.svg" },
868 "_f_minecraft": { "iconPath": "../../icons/file_type_minecraft.svg" },
869 "_f_mivascript": { "iconPath": "../../icons/file_type_mivascript.svg" },
870 "_f_mjml": { "iconPath": "../../icons/file_type_mjml.svg" },
871 "_f_mlang": { "iconPath": "../../icons/file_type_mlang.svg" },
872 "_f_light_mlang": { "iconPath": "../../icons/file_type_light_mlang.svg" },
873 "_f_mocha": { "iconPath": "../../icons/file_type_mocha.svg" },
874 "_f_modernizr": { "iconPath": "../../icons/file_type_modernizr.svg" },
875 "_f_mojo": { "iconPath": "../../icons/file_type_mojo.svg" },
876 "_f_mojolicious": { "iconPath": "../../icons/file_type_mojolicious.svg" },
877 "_f_moleculer": { "iconPath": "../../icons/file_type_moleculer.svg" },
878 "_f_mondoo": { "iconPath": "../../icons/file_type_mondoo.svg" },
879 "_f_mongo": { "iconPath": "../../icons/file_type_mongo.svg" },
880 "_f_monotone": { "iconPath": "../../icons/file_type_monotone.svg" },
881 "_f_motif": { "iconPath": "../../icons/file_type_motif.svg" },
882 "_f_mson": { "iconPath": "../../icons/file_type_mson.svg" },
883 "_f_mustache": { "iconPath": "../../icons/file_type_mustache.svg" },
884 "_f_light_mustache": { "iconPath": "../../icons/file_type_light_mustache.svg" },
885 "_f_mvt": { "iconPath": "../../icons/file_type_mvt.svg" },
886 "_f_mvtcss": { "iconPath": "../../icons/file_type_mvtcss.svg" },
887 "_f_mvtjs": { "iconPath": "../../icons/file_type_mvtjs.svg" },
888 "_f_mypy": { "iconPath": "../../icons/file_type_mypy.svg" },
889 "_f_light_mypy": { "iconPath": "../../icons/file_type_light_mypy.svg" },
890 "_f_nanostaged": { "iconPath": "../../icons/file_type_nanostaged.svg" },
891 "_f_ndst": { "iconPath": "../../icons/file_type_ndst.svg" },
892 "_f_nearly": { "iconPath": "../../icons/file_type_nearly.svg" },
893 "_f_neo4j": { "iconPath": "../../icons/file_type_neo4j.svg" },
894 "_f_light_neo4j": { "iconPath": "../../icons/file_type_light_neo4j.svg" },
895 "_f_nestjs": { "iconPath": "../../icons/file_type_nestjs.svg" },
896 "_f_netlify": { "iconPath": "../../icons/file_type_netlify.svg" },
897 "_f_light_netlify": { "iconPath": "../../icons/file_type_light_netlify.svg" },
898 "_f_next": { "iconPath": "../../icons/file_type_next.svg" },
899 "_f_light_next": { "iconPath": "../../icons/file_type_light_next.svg" },
900 "_f_nextflow": { "iconPath": "../../icons/file_type_nextflow.svg" },
901 "_f_ng_tailwind": { "iconPath": "../../icons/file_type_ng_tailwind.svg" },
902 "_f_nginx": { "iconPath": "../../icons/file_type_nginx.svg" },
903 "_f_nim": { "iconPath": "../../icons/file_type_nim.svg" },
904 "_f_nimble": { "iconPath": "../../icons/file_type_nimble.svg" },
905 "_f_ninja": { "iconPath": "../../icons/file_type_ninja.svg" },
906 "_f_nix": { "iconPath": "../../icons/file_type_nix.svg" },
907 "_f_njsproj": { "iconPath": "../../icons/file_type_njsproj.svg" },
908 "_f_noc": { "iconPath": "../../icons/file_type_noc.svg" },
909 "_f_node": { "iconPath": "../../icons/file_type_node.svg" },
910 "_f_nodemon": { "iconPath": "../../icons/file_type_nodemon.svg" },
911 "_f_npm": { "iconPath": "../../icons/file_type_npm.svg" },
912 "_f_npmpackagejsonlint": { "iconPath": "../../icons/file_type_npmpackagejsonlint.svg" },
913 "_f_nsi": { "iconPath": "../../icons/file_type_nsi.svg" },
914 "_f_nsri": { "iconPath": "../../icons/file_type_nsri.svg" },
915 "_f_nsri-integrity": { "iconPath": "../../icons/file_type_nsri-integrity.svg" },
916 "_f_nuget": { "iconPath": "../../icons/file_type_nuget.svg" },
917 "_f_numpy": { "iconPath": "../../icons/file_type_numpy.svg" },
918 "_f_nunjucks": { "iconPath": "../../icons/file_type_nunjucks.svg" },
919 "_f_nushell": { "iconPath": "../../icons/file_type_nushell.svg" },
920 "_f_nuxt": { "iconPath": "../../icons/file_type_nuxt.svg" },
921 "_f_nx": { "iconPath": "../../icons/file_type_nx.svg" },
922 "_f_light_nx": { "iconPath": "../../icons/file_type_light_nx.svg" },
923 "_f_nyc": { "iconPath": "../../icons/file_type_nyc.svg" },
924 "_f_objectivec": { "iconPath": "../../icons/file_type_objectivec.svg" },
925 "_f_objectivecpp": { "iconPath": "../../icons/file_type_objectivecpp.svg" },
926 "_f_objidconfig": { "iconPath": "../../icons/file_type_objidconfig.svg" },
927 "_f_light_objidconfig": { "iconPath": "../../icons/file_type_light_objidconfig.svg" },
928 "_f_ocaml": { "iconPath": "../../icons/file_type_ocaml.svg" },
929 "_f_ocaml_intf": { "iconPath": "../../icons/file_type_ocaml_intf.svg" },
930 "_f_ogone": { "iconPath": "../../icons/file_type_ogone.svg" },
931 "_f_onenote": { "iconPath": "../../icons/file_type_onenote.svg" },
932 "_f_opam": { "iconPath": "../../icons/file_type_opam.svg" },
933 "_f_openHAB": { "iconPath": "../../icons/file_type_openHAB.svg" },
934 "_f_light_openHAB": { "iconPath": "../../icons/file_type_light_openHAB.svg" },
935 "_f_opencl": { "iconPath": "../../icons/file_type_opencl.svg" },
936 "_f_openscad": { "iconPath": "../../icons/file_type_openscad.svg" },
937 "_f_opentofu": { "iconPath": "../../icons/file_type_opentofu.svg" },
938 "_f_org": { "iconPath": "../../icons/file_type_org.svg" },
939 "_f_outlook": { "iconPath": "../../icons/file_type_outlook.svg" },
940 "_f_ovpn": { "iconPath": "../../icons/file_type_ovpn.svg" },
941 "_f_oxc": { "iconPath": "../../icons/file_type_oxc.svg" },
942 "_f_light_oxc": { "iconPath": "../../icons/file_type_light_oxc.svg" },
943 "_f_package": { "iconPath": "../../icons/file_type_package.svg" },
944 "_f_packship": { "iconPath": "../../icons/file_type_packship.svg" },
945 "_f_light_packship": { "iconPath": "../../icons/file_type_light_packship.svg" },
946 "_f_paket": { "iconPath": "../../icons/file_type_paket.svg" },
947 "_f_pandacss": { "iconPath": "../../icons/file_type_pandacss.svg" },
948 "_f_patch": { "iconPath": "../../icons/file_type_patch.svg" },
949 "_f_pcl": { "iconPath": "../../icons/file_type_pcl.svg" },
950 "_f_light_pcl": { "iconPath": "../../icons/file_type_light_pcl.svg" },
951 "_f_pddl": { "iconPath": "../../icons/file_type_pddl.svg" },
952 "_f_pddl_happenings": { "iconPath": "../../icons/file_type_pddl_happenings.svg" },
953 "_f_pddl_plan": { "iconPath": "../../icons/file_type_pddl_plan.svg" },
954 "_f_pdf": { "iconPath": "../../icons/file_type_pdf.svg" },
955 "_f_pdm": { "iconPath": "../../icons/file_type_pdm.svg" },
956 "_f_peeky": { "iconPath": "../../icons/file_type_peeky.svg" },
957 "_f_perl": { "iconPath": "../../icons/file_type_perl.svg" },
958 "_f_perl6": { "iconPath": "../../icons/file_type_perl6.svg" },
959 "_f_pgsql": { "iconPath": "../../icons/file_type_pgsql.svg" },
960 "_f_photoshop": { "iconPath": "../../icons/file_type_photoshop.svg" },
961 "_f_php": { "iconPath": "../../icons/file_type_php.svg" },
962 "_f_phpcsfixer": { "iconPath": "../../icons/file_type_phpcsfixer.svg" },
963 "_f_phpstan": { "iconPath": "../../icons/file_type_phpstan.svg" },
964 "_f_phpunit": { "iconPath": "../../icons/file_type_phpunit.svg" },
965 "_f_phraseapp": { "iconPath": "../../icons/file_type_phraseapp.svg" },
966 "_f_pine": { "iconPath": "../../icons/file_type_pine.svg" },
967 "_f_pip": { "iconPath": "../../icons/file_type_pip.svg" },
968 "_f_pipeline": { "iconPath": "../../icons/file_type_pipeline.svg" },
969 "_f_plantuml": { "iconPath": "../../icons/file_type_plantuml.svg" },
970 "_f_platformio": { "iconPath": "../../icons/file_type_platformio.svg" },
971 "_f_playwright": { "iconPath": "../../icons/file_type_playwright.svg" },
972 "_f_plsql": { "iconPath": "../../icons/file_type_plsql.svg" },
973 "_f_plsql_package": { "iconPath": "../../icons/file_type_plsql_package.svg" },
974 "_f_plsql_package_body": { "iconPath": "../../icons/file_type_plsql_package_body.svg" },
975 "_f_plsql_package_header": { "iconPath": "../../icons/file_type_plsql_package_header.svg" },
976 "_f_plsql_package_spec": { "iconPath": "../../icons/file_type_plsql_package_spec.svg" },
977 "_f_pm2": { "iconPath": "../../icons/file_type_pm2.svg" },
978 "_f_pnpm": { "iconPath": "../../icons/file_type_pnpm.svg" },
979 "_f_light_pnpm": { "iconPath": "../../icons/file_type_light_pnpm.svg" },
980 "_f_poedit": { "iconPath": "../../icons/file_type_poedit.svg" },
981 "_f_poetry": { "iconPath": "../../icons/file_type_poetry.svg" },
982 "_f_polyglot": { "iconPath": "../../icons/file_type_polyglot.svg" },
983 "_f_polymer": { "iconPath": "../../icons/file_type_polymer.svg" },
984 "_f_pony": { "iconPath": "../../icons/file_type_pony.svg" },
985 "_f_postcss": { "iconPath": "../../icons/file_type_postcss.svg" },
986 "_f_postcssconfig": { "iconPath": "../../icons/file_type_postcssconfig.svg" },
987 "_f_postman": { "iconPath": "../../icons/file_type_postman.svg" },
988 "_f_postscript": { "iconPath": "../../icons/file_type_postscript.svg" },
989 "_f_powerpoint": { "iconPath": "../../icons/file_type_powerpoint.svg" },
990 "_f_powershell": { "iconPath": "../../icons/file_type_powershell.svg" },
991 "_f_powershell_format": { "iconPath": "../../icons/file_type_powershell_format.svg" },
992 "_f_powershell_psd": { "iconPath": "../../icons/file_type_powershell_psd.svg" },
993 "_f_powershell_psm": { "iconPath": "../../icons/file_type_powershell_psm.svg" },
994 "_f_powershell_types": { "iconPath": "../../icons/file_type_powershell_types.svg" },
995 "_f_preact": { "iconPath": "../../icons/file_type_preact.svg" },
996 "_f_precommit": { "iconPath": "../../icons/file_type_precommit.svg" },
997 "_f_prettier": { "iconPath": "../../icons/file_type_prettier.svg" },
998 "_f_light_prettier": { "iconPath": "../../icons/file_type_light_prettier.svg" },
999 "_f_prisma": { "iconPath": "../../icons/file_type_prisma.svg" },
1000 "_f_light_prisma": { "iconPath": "../../icons/file_type_light_prisma.svg" },
1001 "_f_prismaconfig": { "iconPath": "../../icons/file_type_prismaconfig.svg" },
1002 "_f_processinglang": { "iconPath": "../../icons/file_type_processinglang.svg" },
1003 "_f_procfile": { "iconPath": "../../icons/file_type_procfile.svg" },
1004 "_f_progress": { "iconPath": "../../icons/file_type_progress.svg" },
1005 "_f_prolog": { "iconPath": "../../icons/file_type_prolog.svg" },
1006 "_f_prometheus": { "iconPath": "../../icons/file_type_prometheus.svg" },
1007 "_f_protobuf": { "iconPath": "../../icons/file_type_protobuf.svg" },
1008 "_f_protractor": { "iconPath": "../../icons/file_type_protractor.svg" },
1009 "_f_publiccode": { "iconPath": "../../icons/file_type_publiccode.svg" },
1010 "_f_publisher": { "iconPath": "../../icons/file_type_publisher.svg" },
1011 "_f_pug": { "iconPath": "../../icons/file_type_pug.svg" },
1012 "_f_pulumi": { "iconPath": "../../icons/file_type_pulumi.svg" },
1013 "_f_puppet": { "iconPath": "../../icons/file_type_puppet.svg" },
1014 "_f_purescript": { "iconPath": "../../icons/file_type_purescript.svg" },
1015 "_f_light_purescript": { "iconPath": "../../icons/file_type_light_purescript.svg" },
1016 "_f_purgecss": { "iconPath": "../../icons/file_type_purgecss.svg" },
1017 "_f_pyenv": { "iconPath": "../../icons/file_type_pyenv.svg" },
1018 "_f_pypi": { "iconPath": "../../icons/file_type_pypi.svg" },
1019 "_f_pyret": { "iconPath": "../../icons/file_type_pyret.svg" },
1020 "_f_pyscript": { "iconPath": "../../icons/file_type_pyscript.svg" },
1021 "_f_pytest": { "iconPath": "../../icons/file_type_pytest.svg" },
1022 "_f_python": { "iconPath": "../../icons/file_type_python.svg" },
1023 "_f_pythowo": { "iconPath": "../../icons/file_type_pythowo.svg" },
1024 "_f_pytyped": { "iconPath": "../../icons/file_type_pytyped.svg" },
1025 "_f_pyup": { "iconPath": "../../icons/file_type_pyup.svg" },
1026 "_f_q": { "iconPath": "../../icons/file_type_q.svg" },
1027 "_f_qbs": { "iconPath": "../../icons/file_type_qbs.svg" },
1028 "_f_qlikview": { "iconPath": "../../icons/file_type_qlikview.svg" },
1029 "_f_qml": { "iconPath": "../../icons/file_type_qml.svg" },
1030 "_f_qmldir": { "iconPath": "../../icons/file_type_qmldir.svg" },
1031 "_f_qsharp": { "iconPath": "../../icons/file_type_qsharp.svg" },
1032 "_f_quasar": { "iconPath": "../../icons/file_type_quasar.svg" },
1033 "_f_light_quasar": { "iconPath": "../../icons/file_type_light_quasar.svg" },
1034 "_f_r": { "iconPath": "../../icons/file_type_r.svg" },
1035 "_f_ra_syntax_tree": { "iconPath": "../../icons/file_type_ra_syntax_tree.svg" },
1036 "_f_racket": { "iconPath": "../../icons/file_type_racket.svg" },
1037 "_f_rails": { "iconPath": "../../icons/file_type_rails.svg" },
1038 "_f_rake": { "iconPath": "../../icons/file_type_rake.svg" },
1039 "_f_raku": { "iconPath": "../../icons/file_type_raku.svg" },
1040 "_f_light_raku": { "iconPath": "../../icons/file_type_light_raku.svg" },
1041 "_f_raml": { "iconPath": "../../icons/file_type_raml.svg" },
1042 "_f_razor": { "iconPath": "../../icons/file_type_razor.svg" },
1043 "_f_razzle": { "iconPath": "../../icons/file_type_razzle.svg" },
1044 "_f_light_razzle": { "iconPath": "../../icons/file_type_light_razzle.svg" },
1045 "_f_reactjs": { "iconPath": "../../icons/file_type_reactjs.svg" },
1046 "_f_reactrouter": { "iconPath": "../../icons/file_type_reactrouter.svg" },
1047 "_f_light_reactrouter": { "iconPath": "../../icons/file_type_light_reactrouter.svg" },
1048 "_f_reacttemplate": { "iconPath": "../../icons/file_type_reacttemplate.svg" },
1049 "_f_reactts": { "iconPath": "../../icons/file_type_reactts.svg" },
1050 "_f_reason": { "iconPath": "../../icons/file_type_reason.svg" },
1051 "_f_red": { "iconPath": "../../icons/file_type_red.svg" },
1052 "_f_registry": { "iconPath": "../../icons/file_type_registry.svg" },
1053 "_f_rego": { "iconPath": "../../icons/file_type_rego.svg" },
1054 "_f_rehype": { "iconPath": "../../icons/file_type_rehype.svg" },
1055 "_f_light_rehype": { "iconPath": "../../icons/file_type_light_rehype.svg" },
1056 "_f_remark": { "iconPath": "../../icons/file_type_remark.svg" },
1057 "_f_light_remark": { "iconPath": "../../icons/file_type_light_remark.svg" },
1058 "_f_renovate": { "iconPath": "../../icons/file_type_renovate.svg" },
1059 "_f_replit": { "iconPath": "../../icons/file_type_replit.svg" },
1060 "_f_light_replit": { "iconPath": "../../icons/file_type_light_replit.svg" },
1061 "_f_rescript": { "iconPath": "../../icons/file_type_rescript.svg" },
1062 "_f_rest": { "iconPath": "../../icons/file_type_rest.svg" },
1063 "_f_retext": { "iconPath": "../../icons/file_type_retext.svg" },
1064 "_f_light_retext": { "iconPath": "../../icons/file_type_light_retext.svg" },
1065 "_f_rexx": { "iconPath": "../../icons/file_type_rexx.svg" },
1066 "_f_riot": { "iconPath": "../../icons/file_type_riot.svg" },
1067 "_f_ripple": { "iconPath": "../../icons/file_type_ripple.svg" },
1068 "_f_rmd": { "iconPath": "../../icons/file_type_rmd.svg" },
1069 "_f_rnc": { "iconPath": "../../icons/file_type_rnc.svg" },
1070 "_f_robotframework": { "iconPath": "../../icons/file_type_robotframework.svg" },
1071 "_f_robots": { "iconPath": "../../icons/file_type_robots.svg" },
1072 "_f_rolldown": { "iconPath": "../../icons/file_type_rolldown.svg" },
1073 "_f_light_rolldown": { "iconPath": "../../icons/file_type_light_rolldown.svg" },
1074 "_f_rollup": { "iconPath": "../../icons/file_type_rollup.svg" },
1075 "_f_rome": { "iconPath": "../../icons/file_type_rome.svg" },
1076 "_f_light_rome": { "iconPath": "../../icons/file_type_light_rome.svg" },
1077 "_f_ron": { "iconPath": "../../icons/file_type_ron.svg" },
1078 "_f_rproj": { "iconPath": "../../icons/file_type_rproj.svg" },
1079 "_f_rspec": { "iconPath": "../../icons/file_type_rspec.svg" },
1080 "_f_rss": { "iconPath": "../../icons/file_type_rss.svg" },
1081 "_f_rubocop": { "iconPath": "../../icons/file_type_rubocop.svg" },
1082 "_f_light_rubocop": { "iconPath": "../../icons/file_type_light_rubocop.svg" },
1083 "_f_ruby": { "iconPath": "../../icons/file_type_ruby.svg" },
1084 "_f_rust": { "iconPath": "../../icons/file_type_rust.svg" },
1085 "_f_light_rust": { "iconPath": "../../icons/file_type_light_rust.svg" },
1086 "_f_rust_toolchain": { "iconPath": "../../icons/file_type_rust_toolchain.svg" },
1087 "_f_light_rust_toolchain": { "iconPath": "../../icons/file_type_light_rust_toolchain.svg" },
1088 "_f_s-lang": { "iconPath": "../../icons/file_type_s-lang.svg" },
1089 "_f_safetensors": { "iconPath": "../../icons/file_type_safetensors.svg" },
1090 "_f_light_safetensors": { "iconPath": "../../icons/file_type_light_safetensors.svg" },
1091 "_f_sails": { "iconPath": "../../icons/file_type_sails.svg" },
1092 "_f_saltstack": { "iconPath": "../../icons/file_type_saltstack.svg" },
1093 "_f_san": { "iconPath": "../../icons/file_type_san.svg" },
1094 "_f_sapphire_framework_cli": { "iconPath": "../../icons/file_type_sapphire_framework_cli.svg" },
1095 "_f_sas": { "iconPath": "../../icons/file_type_sas.svg" },
1096 "_f_sass": { "iconPath": "../../icons/file_type_sass.svg" },
1097 "_f_sbt": { "iconPath": "../../icons/file_type_sbt.svg" },
1098 "_f_scala": { "iconPath": "../../icons/file_type_scala.svg" },
1099 "_f_scilab": { "iconPath": "../../icons/file_type_scilab.svg" },
1100 "_f_script": { "iconPath": "../../icons/file_type_script.svg" },
1101 "_f_scss": { "iconPath": "../../icons/file_type_scss.svg" },
1102 "_f_sdlang": { "iconPath": "../../icons/file_type_sdlang.svg" },
1103 "_f_search_result": { "iconPath": "../../icons/file_type_search_result.svg" },
1104 "_f_seedkit": { "iconPath": "../../icons/file_type_seedkit.svg" },
1105 "_f_sentry": { "iconPath": "../../icons/file_type_sentry.svg" },
1106 "_f_sequelize": { "iconPath": "../../icons/file_type_sequelize.svg" },
1107 "_f_serverless": { "iconPath": "../../icons/file_type_serverless.svg" },
1108 "_f_shadcn": { "iconPath": "../../icons/file_type_shadcn.svg" },
1109 "_f_light_shadcn": { "iconPath": "../../icons/file_type_light_shadcn.svg" },
1110 "_f_shaderlab": { "iconPath": "../../icons/file_type_shaderlab.svg" },
1111 "_f_light_shaderlab": { "iconPath": "../../icons/file_type_light_shaderlab.svg" },
1112 "_f_shell": { "iconPath": "../../icons/file_type_shell.svg" },
1113 "_f_shellcheck": { "iconPath": "../../icons/file_type_shellcheck.svg" },
1114 "_f_shuttle": { "iconPath": "../../icons/file_type_shuttle.svg" },
1115 "_f_silverstripe": { "iconPath": "../../icons/file_type_silverstripe.svg" },
1116 "_f_sino": { "iconPath": "../../icons/file_type_sino.svg" },
1117 "_f_sitemap": { "iconPath": "../../icons/file_type_sitemap.svg" },
1118 "_f_siyuan": { "iconPath": "../../icons/file_type_siyuan.svg" },
1119 "_f_sketch": { "iconPath": "../../icons/file_type_sketch.svg" },
1120 "_f_skipper": { "iconPath": "../../icons/file_type_skipper.svg" },
1121 "_f_slang": { "iconPath": "../../icons/file_type_slang.svg" },
1122 "_f_slashup": { "iconPath": "../../icons/file_type_slashup.svg" },
1123 "_f_slice": { "iconPath": "../../icons/file_type_slice.svg" },
1124 "_f_slim": { "iconPath": "../../icons/file_type_slim.svg" },
1125 "_f_slint": { "iconPath": "../../icons/file_type_slint.svg" },
1126 "_f_sln": { "iconPath": "../../icons/file_type_sln.svg" },
1127 "_f_smarty": { "iconPath": "../../icons/file_type_smarty.svg" },
1128 "_f_smithery": { "iconPath": "../../icons/file_type_smithery.svg" },
1129 "_f_snakemake": { "iconPath": "../../icons/file_type_snakemake.svg" },
1130 "_f_snapcraft": { "iconPath": "../../icons/file_type_snapcraft.svg" },
1131 "_f_snaplet": { "iconPath": "../../icons/file_type_snaplet.svg" },
1132 "_f_snort": { "iconPath": "../../icons/file_type_snort.svg" },
1133 "_f_snyk": { "iconPath": "../../icons/file_type_snyk.svg" },
1134 "_f_solidarity": { "iconPath": "../../icons/file_type_solidarity.svg" },
1135 "_f_solidity": { "iconPath": "../../icons/file_type_solidity.svg" },
1136 "_f_light_solidity": { "iconPath": "../../icons/file_type_light_solidity.svg" },
1137 "_f_source": { "iconPath": "../../icons/file_type_source.svg" },
1138 "_f_spacengine": { "iconPath": "../../icons/file_type_spacengine.svg" },
1139 "_f_sparql": { "iconPath": "../../icons/file_type_sparql.svg" },
1140 "_f_spin": { "iconPath": "../../icons/file_type_spin.svg" },
1141 "_f_light_spin": { "iconPath": "../../icons/file_type_light_spin.svg" },
1142 "_f_sqf": { "iconPath": "../../icons/file_type_sqf.svg" },
1143 "_f_sql": { "iconPath": "../../icons/file_type_sql.svg" },
1144 "_f_sqlite": { "iconPath": "../../icons/file_type_sqlite.svg" },
1145 "_f_squirrel": { "iconPath": "../../icons/file_type_squirrel.svg" },
1146 "_f_sss": { "iconPath": "../../icons/file_type_sss.svg" },
1147 "_f_sst": { "iconPath": "../../icons/file_type_sst.svg" },
1148 "_f_stackblitz": { "iconPath": "../../icons/file_type_stackblitz.svg" },
1149 "_f_stan": { "iconPath": "../../icons/file_type_stan.svg" },
1150 "_f_stata": { "iconPath": "../../icons/file_type_stata.svg" },
1151 "_f_stencil": { "iconPath": "../../icons/file_type_stencil.svg" },
1152 "_f_storyboard": { "iconPath": "../../icons/file_type_storyboard.svg" },
1153 "_f_storybook": { "iconPath": "../../icons/file_type_storybook.svg" },
1154 "_f_stryker": { "iconPath": "../../icons/file_type_stryker.svg" },
1155 "_f_stylable": { "iconPath": "../../icons/file_type_stylable.svg" },
1156 "_f_style": { "iconPath": "../../icons/file_type_style.svg" },
1157 "_f_styled": { "iconPath": "../../icons/file_type_styled.svg" },
1158 "_f_stylelint": { "iconPath": "../../icons/file_type_stylelint.svg" },
1159 "_f_light_stylelint": { "iconPath": "../../icons/file_type_light_stylelint.svg" },
1160 "_f_stylish_haskell": { "iconPath": "../../icons/file_type_stylish_haskell.svg" },
1161 "_f_stylus": { "iconPath": "../../icons/file_type_stylus.svg" },
1162 "_f_light_stylus": { "iconPath": "../../icons/file_type_light_stylus.svg" },
1163 "_f_sublime": { "iconPath": "../../icons/file_type_sublime.svg" },
1164 "_f_subversion": { "iconPath": "../../icons/file_type_subversion.svg" },
1165 "_f_svelte": { "iconPath": "../../icons/file_type_svelte.svg" },
1166 "_f_svelteconfig": { "iconPath": "../../icons/file_type_svelteconfig.svg" },
1167 "_f_svg": { "iconPath": "../../icons/file_type_svg.svg" },
1168 "_f_svgo": { "iconPath": "../../icons/file_type_svgo.svg" },
1169 "_f_swagger": { "iconPath": "../../icons/file_type_swagger.svg" },
1170 "_f_swc": { "iconPath": "../../icons/file_type_swc.svg" },
1171 "_f_swift": { "iconPath": "../../icons/file_type_swift.svg" },
1172 "_f_swig": { "iconPath": "../../icons/file_type_swig.svg" },
1173 "_f_symfony": { "iconPath": "../../icons/file_type_symfony.svg" },
1174 "_f_light_symfony": { "iconPath": "../../icons/file_type_light_symfony.svg" },
1175 "_f_syncpack": { "iconPath": "../../icons/file_type_syncpack.svg" },
1176 "_f_systemd": { "iconPath": "../../icons/file_type_systemd.svg" },
1177 "_f_light_systemd": { "iconPath": "../../icons/file_type_light_systemd.svg" },
1178 "_f_systemverilog": { "iconPath": "../../icons/file_type_systemverilog.svg" },
1179 "_f_light_systemverilog": { "iconPath": "../../icons/file_type_light_systemverilog.svg" },
1180 "_f_t4tt": { "iconPath": "../../icons/file_type_t4tt.svg" },
1181 "_f_tailwind": { "iconPath": "../../icons/file_type_tailwind.svg" },
1182 "_f_tamagui": { "iconPath": "../../icons/file_type_tamagui.svg" },
1183 "_f_taplo": { "iconPath": "../../icons/file_type_taplo.svg" },
1184 "_f_taskfile": { "iconPath": "../../icons/file_type_taskfile.svg" },
1185 "_f_tauri": { "iconPath": "../../icons/file_type_tauri.svg" },
1186 "_f_tcl": { "iconPath": "../../icons/file_type_tcl.svg" },
1187 "_f_teal": { "iconPath": "../../icons/file_type_teal.svg" },
1188 "_f_templ": { "iconPath": "../../icons/file_type_templ.svg" },
1189 "_f_tera": { "iconPath": "../../icons/file_type_tera.svg" },
1190 "_f_terraform": { "iconPath": "../../icons/file_type_terraform.svg" },
1191 "_f_test": { "iconPath": "../../icons/file_type_test.svg" },
1192 "_f_testcafe": { "iconPath": "../../icons/file_type_testcafe.svg" },
1193 "_f_light_testcafe": { "iconPath": "../../icons/file_type_light_testcafe.svg" },
1194 "_f_testjs": { "iconPath": "../../icons/file_type_testjs.svg" },
1195 "_f_light_testjs": { "iconPath": "../../icons/file_type_light_testjs.svg" },
1196 "_f_testplane": { "iconPath": "../../icons/file_type_testplane.svg" },
1197 "_f_testts": { "iconPath": "../../icons/file_type_testts.svg" },
1198 "_f_tex": { "iconPath": "../../icons/file_type_tex.svg" },
1199 "_f_light_tex": { "iconPath": "../../icons/file_type_light_tex.svg" },
1200 "_f_text": { "iconPath": "../../icons/file_type_text.svg" },
1201 "_f_textile": { "iconPath": "../../icons/file_type_textile.svg" },
1202 "_f_tfs": { "iconPath": "../../icons/file_type_tfs.svg" },
1203 "_f_tiltfile": { "iconPath": "../../icons/file_type_tiltfile.svg" },
1204 "_f_tm": { "iconPath": "../../icons/file_type_tm.svg" },
1205 "_f_light_tm": { "iconPath": "../../icons/file_type_light_tm.svg" },
1206 "_f_tmux": { "iconPath": "../../icons/file_type_tmux.svg" },
1207 "_f_light_tmux": { "iconPath": "../../icons/file_type_light_tmux.svg" },
1208 "_f_todo": { "iconPath": "../../icons/file_type_todo.svg" },
1209 "_f_light_todo": { "iconPath": "../../icons/file_type_light_todo.svg" },
1210 "_f_toit": { "iconPath": "../../icons/file_type_toit.svg" },
1211 "_f_light_toit": { "iconPath": "../../icons/file_type_light_toit.svg" },
1212 "_f_toml": { "iconPath": "../../icons/file_type_toml.svg" },
1213 "_f_light_toml": { "iconPath": "../../icons/file_type_light_toml.svg" },
1214 "_f_toon": { "iconPath": "../../icons/file_type_toon.svg" },
1215 "_f_light_toon": { "iconPath": "../../icons/file_type_light_toon.svg" },
1216 "_f_tox": { "iconPath": "../../icons/file_type_tox.svg" },
1217 "_f_travis": { "iconPath": "../../icons/file_type_travis.svg" },
1218 "_f_tree": { "iconPath": "../../icons/file_type_tree.svg" },
1219 "_f_light_tree": { "iconPath": "../../icons/file_type_light_tree.svg" },
1220 "_f_tres": { "iconPath": "../../icons/file_type_tres.svg" },
1221 "_f_truffle": { "iconPath": "../../icons/file_type_truffle.svg" },
1222 "_f_trunk": { "iconPath": "../../icons/file_type_trunk.svg" },
1223 "_f_tsbuildinfo": { "iconPath": "../../icons/file_type_tsbuildinfo.svg" },
1224 "_f_tscn": { "iconPath": "../../icons/file_type_tscn.svg" },
1225 "_f_tsconfig": { "iconPath": "../../icons/file_type_tsconfig.svg" },
1226 "_f_tsdoc": { "iconPath": "../../icons/file_type_tsdoc.svg" },
1227 "_f_tsdown": { "iconPath": "../../icons/file_type_tsdown.svg" },
1228 "_f_tslint": { "iconPath": "../../icons/file_type_tslint.svg" },
1229 "_f_tt": { "iconPath": "../../icons/file_type_tt.svg" },
1230 "_f_ttcn": { "iconPath": "../../icons/file_type_ttcn.svg" },
1231 "_f_tuc": { "iconPath": "../../icons/file_type_tuc.svg" },
1232 "_f_turbo": { "iconPath": "../../icons/file_type_turbo.svg" },
1233 "_f_light_turbo": { "iconPath": "../../icons/file_type_light_turbo.svg" },
1234 "_f_twig": { "iconPath": "../../icons/file_type_twig.svg" },
1235 "_f_typedoc": { "iconPath": "../../icons/file_type_typedoc.svg" },
1236 "_f_typescript": { "iconPath": "../../icons/file_type_typescript.svg" },
1237 "_f_typescriptdef": { "iconPath": "../../icons/file_type_typescriptdef.svg" },
1238 "_f_typo3": { "iconPath": "../../icons/file_type_typo3.svg" },
1239 "_f_uiua": { "iconPath": "../../icons/file_type_uiua.svg" },
1240 "_f_unibeautify": { "iconPath": "../../icons/file_type_unibeautify.svg" },
1241 "_f_light_unibeautify": { "iconPath": "../../icons/file_type_light_unibeautify.svg" },
1242 "_f_unison": { "iconPath": "../../icons/file_type_unison.svg" },
1243 "_f_unlicense": { "iconPath": "../../icons/file_type_unlicense.svg" },
1244 "_f_unocss": { "iconPath": "../../icons/file_type_unocss.svg" },
1245 "_f_uv": { "iconPath": "../../icons/file_type_uv.svg" },
1246 "_f_vagrant": { "iconPath": "../../icons/file_type_vagrant.svg" },
1247 "_f_vala": { "iconPath": "../../icons/file_type_vala.svg" },
1248 "_f_vanilla_extract": { "iconPath": "../../icons/file_type_vanilla_extract.svg" },
1249 "_f_vapi": { "iconPath": "../../icons/file_type_vapi.svg" },
1250 "_f_vapor": { "iconPath": "../../icons/file_type_vapor.svg" },
1251 "_f_vash": { "iconPath": "../../icons/file_type_vash.svg" },
1252 "_f_light_vash": { "iconPath": "../../icons/file_type_light_vash.svg" },
1253 "_f_vb": { "iconPath": "../../icons/file_type_vb.svg" },
1254 "_f_vba": { "iconPath": "../../icons/file_type_vba.svg" },
1255 "_f_vbhtml": { "iconPath": "../../icons/file_type_vbhtml.svg" },
1256 "_f_vbproj": { "iconPath": "../../icons/file_type_vbproj.svg" },
1257 "_f_vcxproj": { "iconPath": "../../icons/file_type_vcxproj.svg" },
1258 "_f_velocity": { "iconPath": "../../icons/file_type_velocity.svg" },
1259 "_f_vento": { "iconPath": "../../icons/file_type_vento.svg" },
1260 "_f_vercel": { "iconPath": "../../icons/file_type_vercel.svg" },
1261 "_f_light_vercel": { "iconPath": "../../icons/file_type_light_vercel.svg" },
1262 "_f_verilog": { "iconPath": "../../icons/file_type_verilog.svg" },
1263 "_f_vhdl": { "iconPath": "../../icons/file_type_vhdl.svg" },
1264 "_f_video": { "iconPath": "../../icons/file_type_video.svg" },
1265 "_f_view": { "iconPath": "../../icons/file_type_view.svg" },
1266 "_f_vim": { "iconPath": "../../icons/file_type_vim.svg" },
1267 "_f_vite": { "iconPath": "../../icons/file_type_vite.svg" },
1268 "_f_light_vite": { "iconPath": "../../icons/file_type_light_vite.svg" },
1269 "_f_vitest": { "iconPath": "../../icons/file_type_vitest.svg" },
1270 "_f_light_vitest": { "iconPath": "../../icons/file_type_light_vitest.svg" },
1271 "_f_vlang": { "iconPath": "../../icons/file_type_vlang.svg" },
1272 "_f_volt": { "iconPath": "../../icons/file_type_volt.svg" },
1273 "_f_vroom": { "iconPath": "../../icons/file_type_vroom.svg" },
1274 "_f_vscode": { "iconPath": "../../icons/file_type_vscode.svg" },
1275 "_f_vscode_test": { "iconPath": "../../icons/file_type_vscode_test.svg" },
1276 "_f_vsix": { "iconPath": "../../icons/file_type_vsix.svg" },
1277 "_f_light_vsix": { "iconPath": "../../icons/file_type_light_vsix.svg" },
1278 "_f_vsixmanifest": { "iconPath": "../../icons/file_type_vsixmanifest.svg" },
1279 "_f_light_vsixmanifest": { "iconPath": "../../icons/file_type_light_vsixmanifest.svg" },
1280 "_f_vue": { "iconPath": "../../icons/file_type_vue.svg" },
1281 "_f_vueconfig": { "iconPath": "../../icons/file_type_vueconfig.svg" },
1282 "_f_vvvvvv": { "iconPath": "../../icons/file_type_vvvvvv.svg" },
1283 "_f_vyper": { "iconPath": "../../icons/file_type_vyper.svg" },
1284 "_f_wallaby": { "iconPath": "../../icons/file_type_wallaby.svg" },
1285 "_f_wally": { "iconPath": "../../icons/file_type_wally.svg" },
1286 "_f_wasm": { "iconPath": "../../icons/file_type_wasm.svg" },
1287 "_f_watchmanconfig": { "iconPath": "../../icons/file_type_watchmanconfig.svg" },
1288 "_f_wdio": { "iconPath": "../../icons/file_type_wdio.svg" },
1289 "_f_weblate": { "iconPath": "../../icons/file_type_weblate.svg" },
1290 "_f_webpack": { "iconPath": "../../icons/file_type_webpack.svg" },
1291 "_f_wenyan": { "iconPath": "../../icons/file_type_wenyan.svg" },
1292 "_f_wercker": { "iconPath": "../../icons/file_type_wercker.svg" },
1293 "_f_wgsl": { "iconPath": "../../icons/file_type_wgsl.svg" },
1294 "_f_wikitext": { "iconPath": "../../icons/file_type_wikitext.svg" },
1295 "_f_windi": { "iconPath": "../../icons/file_type_windi.svg" },
1296 "_f_wit": { "iconPath": "../../icons/file_type_wit.svg" },
1297 "_f_wolfram": { "iconPath": "../../icons/file_type_wolfram.svg" },
1298 "_f_word": { "iconPath": "../../icons/file_type_word.svg" },
1299 "_f_wpml": { "iconPath": "../../icons/file_type_wpml.svg" },
1300 "_f_wurst": { "iconPath": "../../icons/file_type_wurst.svg" },
1301 "_f_wxml": { "iconPath": "../../icons/file_type_wxml.svg" },
1302 "_f_wxss": { "iconPath": "../../icons/file_type_wxss.svg" },
1303 "_f_wxt": { "iconPath": "../../icons/file_type_wxt.svg" },
1304 "_f_xaml": { "iconPath": "../../icons/file_type_xaml.svg" },
1305 "_f_xcode": { "iconPath": "../../icons/file_type_xcode.svg" },
1306 "_f_xfl": { "iconPath": "../../icons/file_type_xfl.svg" },
1307 "_f_light_xfl": { "iconPath": "../../icons/file_type_light_xfl.svg" },
1308 "_f_xib": { "iconPath": "../../icons/file_type_xib.svg" },
1309 "_f_xliff": { "iconPath": "../../icons/file_type_xliff.svg" },
1310 "_f_xmake": { "iconPath": "../../icons/file_type_xmake.svg" },
1311 "_f_xml": { "iconPath": "../../icons/file_type_xml.svg" },
1312 "_f_xo": { "iconPath": "../../icons/file_type_xo.svg" },
1313 "_f_xorg": { "iconPath": "../../icons/file_type_xorg.svg" },
1314 "_f_light_xorg": { "iconPath": "../../icons/file_type_light_xorg.svg" },
1315 "_f_xquery": { "iconPath": "../../icons/file_type_xquery.svg" },
1316 "_f_xsl": { "iconPath": "../../icons/file_type_xsl.svg" },
1317 "_f_yacc": { "iconPath": "../../icons/file_type_yacc.svg" },
1318 "_f_yaml": { "iconPath": "../../icons/file_type_yaml.svg" },
1319 "_f_light_yaml": { "iconPath": "../../icons/file_type_light_yaml.svg" },
1320 "_f_yamllint": { "iconPath": "../../icons/file_type_yamllint.svg" },
1321 "_f_yandex": { "iconPath": "../../icons/file_type_yandex.svg" },
1322 "_f_yang": { "iconPath": "../../icons/file_type_yang.svg" },
1323 "_f_yarn": { "iconPath": "../../icons/file_type_yarn.svg" },
1324 "_f_yeoman": { "iconPath": "../../icons/file_type_yeoman.svg" },
1325 "_f_zig": { "iconPath": "../../icons/file_type_zig.svg" },
1326 "_f_zip": { "iconPath": "../../icons/file_type_zip.svg" }
1327 },
1328 "file": "_file",
1329 "folder": "_folder",
1330 "folderExpanded": "_folder_open",
1331 "rootFolder": "_root_folder",
1332 "rootFolderExpanded": "_root_folder_open",
1333 "folderNames": {
1334 "android": "_fd_android",
1335 ".angular": "_fd_angular",
1336 "apache": "_fd_apache",
1337 "apache2": "_fd_apache",
1338 "httpd": "_fd_apache",
1339 "api": "_fd_api",
1340 ".api": "_fd_api",
1341 "apis": "_fd_api",
1342 ".apis": "_fd_api",
1343 "app": "_fd_app",
1344 "apps": "_fd_app",
1345 ".app": "_fd_app",
1346 "application": "_fd_app",
1347 "applications": "_fd_app",
1348 "arangodb": "_fd_arangodb",
1349 "arango": "_fd_arangodb",
1350 "assets": "_fd_asset",
1351 ".assets": "_fd_asset",
1352 ".astro": "_fd_astro",
1353 "audio": "_fd_audio",
1354 ".audio": "_fd_audio",
1355 "audios": "_fd_audio",
1356 ".audios": "_fd_audio",
1357 "sound": "_fd_audio",
1358 ".sound": "_fd_audio",
1359 "sounds": "_fd_audio",
1360 ".sounds": "_fd_audio",
1361 "aurelia_project": "_fd_aurelia",
1362 "aws": "_fd_aws",
1363 ".aws": "_fd_aws",
1364 "azure": "_fd_azure",
1365 ".azure": "_fd_azure",
1366 "azure-pipelines": "_fd_azurepipelines",
1367 ".azure-pipelines": "_fd_azurepipelines",
1368 "bin": "_fd_binary",
1369 ".bin": "_fd_binary",
1370 "blocs": "_fd_bloc",
1371 "bloc": "_fd_bloc",
1372 "blueprint": "_fd_blueprint",
1373 ".blueprint": "_fd_blueprint",
1374 "blueprints": "_fd_blueprint",
1375 ".blueprints": "_fd_blueprint",
1376 "bot": "_fd_bot",
1377 ".bot": "_fd_bot",
1378 "bower_components": "_fd_bower",
1379 ".buildkite": "_fd_buildkite",
1380 "cake": "_fd_cake",
1381 ".cake": "_fd_cake",
1382 "cargo": "_fd_cargo",
1383 ".cargo": "_fd_cargo",
1384 "crates": "_fd_cargo",
1385 "certificates": "_fd_certificate",
1386 ".certificates": "_fd_certificate",
1387 "certs": "_fd_certificate",
1388 "certs.": "_fd_certificate",
1389 ".changeset": "_fd_changesets",
1390 "chef": "_fd_chef",
1391 ".chef": "_fd_chef",
1392 ".circleci": "_fd_circleci",
1393 ".claude": "_fd_claude",
1394 "cli": "_fd_cli",
1395 "cmd": "_fd_cli",
1396 "command": "_fd_cli",
1397 "commands": "_fd_cli",
1398 "commandline": "_fd_cli",
1399 "console": "_fd_cli",
1400 "client": "_fd_client",
1401 "clients": "_fd_client",
1402 ".cloudflare": "_fd_cloudflare",
1403 "cloudflare": "_fd_cloudflare",
1404 ".cmake": "_fd_cmake",
1405 "cmake": "_fd_cmake",
1406 "common": "_fd_common",
1407 "component": "_fd_component",
1408 "components": "_fd_component",
1409 ".components": "_fd_component",
1410 "gui": "_fd_component",
1411 "src-ui": "_fd_component",
1412 "ui": "_fd_component",
1413 "widgets": "_fd_component",
1414 "composer": "_fd_composer",
1415 ".composer": "_fd_composer",
1416 "conf": "_fd_config",
1417 ".conf": "_fd_config",
1418 "config": "_fd_config",
1419 ".config": "_fd_config",
1420 "configs": "_fd_config",
1421 ".configs": "_fd_config",
1422 "configuration": "_fd_config",
1423 ".configuration": "_fd_config",
1424 "configurations": "_fd_config",
1425 ".configurations": "_fd_config",
1426 "setting": "_fd_config",
1427 ".setting": "_fd_config",
1428 "settings": "_fd_config",
1429 ".settings": "_fd_config",
1430 "ini": "_fd_config",
1431 ".ini": "_fd_config",
1432 "initializers": "_fd_config",
1433 ".initializers": "_fd_config",
1434 "__controller__": "_fd_controller",
1435 "__controllers__": "_fd_controller",
1436 "controller": "_fd_controller",
1437 "controllers": "_fd_controller",
1438 ".controllers": "_fd_controller",
1439 "__handler__": "_fd_controller",
1440 "__handlers__": "_fd_controller",
1441 "handler": "_fd_controller",
1442 ".handler": "_fd_controller",
1443 "handlers": "_fd_controller",
1444 ".handlers": "_fd_controller",
1445 "coverage": "_fd_coverage",
1446 "css": "_fd_css",
1447 "_css": "_fd_css",
1448 "cubits": "_fd_cubit",
1449 "cubit": "_fd_cubit",
1450 ".cursor": "_fd_cursor",
1451 "cypress": "_fd_cypress",
1452 ".dapr": "_fd_dapr",
1453 "dapr": "_fd_dapr",
1454 "dart": "_fd_dart",
1455 ".dart_tool": "_fd_dart",
1456 "datadog": "_fd_datadog",
1457 ".datadog": "_fd_datadog",
1458 "db": "_fd_db",
1459 "database": "_fd_db",
1460 "sql": "_fd_db",
1461 "data": "_fd_db",
1462 "repo": "_fd_db",
1463 "repository": "_fd_db",
1464 "repositories": "_fd_db",
1465 "store": "_fd_db",
1466 "stores": "_fd_db",
1467 "debian": "_fd_debian",
1468 "deb": "_fd_debian",
1469 ".dependabot": "_fd_dependabot",
1470 ".devcontainer": "_fd_devcontainer",
1471 "dist": "_fd_dist",
1472 ".dist": "_fd_dist",
1473 "dists": "_fd_dist",
1474 "out": "_fd_dist",
1475 "outs": "_fd_dist",
1476 "export": "_fd_dist",
1477 "exports": "_fd_dist",
1478 "build": "_fd_dist",
1479 ".build": "_fd_dist",
1480 "builds": "_fd_dist",
1481 "release": "_fd_dist",
1482 "releases": "_fd_dist",
1483 "target": "_fd_dist",
1484 "targets": "_fd_dist",
1485 "docker": "_fd_docker",
1486 ".docker": "_fd_docker",
1487 "docs": "_fd_docs",
1488 "doc": "_fd_docs",
1489 "document": "_fd_docs",
1490 "documents": "_fd_docs",
1491 "e2e": "_fd_e2e",
1492 ".elasticbeanstalk": "_fd_elasticbeanstalk",
1493 ".ebextensions": "_fd_elasticbeanstalk",
1494 "electron": "_fd_electron",
1495 ".expo": "_fd_expo",
1496 ".expo-shared": "_fd_expo",
1497 "favicon": "_fd_favicon",
1498 "favicons": "_fd_favicon",
1499 "flow": "_fd_flow",
1500 "flow-typed": "_fd_flow",
1501 "flutter": "_fd_flutter",
1502 "Flutter": "_fd_flutter",
1503 "fonts": "_fd_fonts",
1504 "font": "_fd_fonts",
1505 "fnt": "_fd_fonts",
1506 "webfonts": "_fd_fonts",
1507 ".front-commerce": "_fd_frontcommerce",
1508 "gcp": "_fd_gcp",
1509 ".gcp": "_fd_gcp",
1510 ".gemini": "_fd_gemini",
1511 ".git": "_fd_git",
1512 "submodules": "_fd_git",
1513 ".submodules": "_fd_git",
1514 ".github": "_fd_github",
1515 ".gitlab": "_fd_gitlab",
1516 "godot": "_fd_godot",
1517 ".godot": "_fd_godot",
1518 "gradle": "_fd_gradle",
1519 ".gradle": "_fd_gradle",
1520 "graphql": "_fd_graphql",
1521 "grunt": "_fd_grunt",
1522 "gulp": "_fd_gulp",
1523 "gulpfile.js": "_fd_gulp",
1524 "gulpfile.coffee": "_fd_gulp",
1525 "gulpfile.ts": "_fd_gulp",
1526 "gulpfile.babel.js": "_fd_gulp",
1527 "gulpfile.babel.coffee": "_fd_gulp",
1528 "gulpfile.babel.ts": "_fd_gulp",
1529 ".haxelib": "_fd_haxelib",
1530 "haxe_libraries": "_fd_haxelib",
1531 "helper": "_fd_helper",
1532 ".helper": "_fd_helper",
1533 "helpers": "_fd_helper",
1534 ".helpers": "_fd_helper",
1535 ".histoire": "_fd_histoire",
1536 "hook": "_fd_hook",
1537 ".hook": "_fd_hook",
1538 "hooks": "_fd_hook",
1539 ".hooks": "_fd_hook",
1540 ".husky": "_fd_husky",
1541 ".idea": "_fd_idea",
1542 ".fig": "_fd_images",
1543 ".figs": "_fd_images",
1544 "images": "_fd_images",
1545 "image": "_fd_images",
1546 ".img": "_fd_images",
1547 "img": "_fd_images",
1548 ".imgs": "_fd_images",
1549 "imgs": "_fd_images",
1550 "icons": "_fd_images",
1551 "icon": "_fd_images",
1552 "ico": "_fd_images",
1553 "screenshot": "_fd_images",
1554 "screenshots": "_fd_images",
1555 "svg": "_fd_images",
1556 "texture": "_fd_images",
1557 "textures": "_fd_images",
1558 "include": "_fd_include",
1559 "includes": "_fd_include",
1560 "incl": "_fd_include",
1561 "inc": "_fd_include",
1562 ".include": "_fd_include",
1563 ".includes": "_fd_include",
1564 ".incl": "_fd_include",
1565 ".inc": "_fd_include",
1566 "_include": "_fd_include",
1567 "_includes": "_fd_include",
1568 "_incl": "_fd_include",
1569 "_inc": "_fd_include",
1570 "interface": "_fd_interfaces",
1571 "interfaces": "_fd_interfaces",
1572 "ios": "_fd_ios",
1573 "js": "_fd_js",
1574 "json": "_fd_json",
1575 ".junie": "_fd_junie",
1576 "kotlin": "_fd_kotlin",
1577 "Kotlin": "_fd_kotlin",
1578 ".kotlin": "_fd_kotlin",
1579 "kubernetes": "_fd_kubernetes",
1580 "k8s": "_fd_kubernetes",
1581 "kube": "_fd_kubernetes",
1582 "kuber": "_fd_kubernetes",
1583 ".kubernetes": "_fd_kubernetes",
1584 ".k8s": "_fd_kubernetes",
1585 ".kube": "_fd_kubernetes",
1586 ".kuber": "_fd_kubernetes",
1587 "less": "_fd_less",
1588 "_less": "_fd_less",
1589 "lib": "_fd_library",
1590 "libs": "_fd_library",
1591 ".lib": "_fd_library",
1592 ".libs": "_fd_library",
1593 "__lib__": "_fd_library",
1594 "__libs__": "_fd_library",
1595 "library": "_fd_library",
1596 "libraries": "_fd_library",
1597 "linux": "_fd_linux",
1598 "lang": "_fd_locale",
1599 "language": "_fd_locale",
1600 "languages": "_fd_locale",
1601 "locale": "_fd_locale",
1602 "locales": "_fd_locale",
1603 "_locale": "_fd_locale",
1604 "_locales": "_fd_locale",
1605 "internationalization": "_fd_locale",
1606 "globalization": "_fd_locale",
1607 "localization": "_fd_locale",
1608 "i18n": "_fd_locale",
1609 "g11n": "_fd_locale",
1610 "l10n": "_fd_locale",
1611 "log": "_fd_log",
1612 "logs": "_fd_log",
1613 "macos": "_fd_macos",
1614 "darwin": "_fd_macos",
1615 "mariadb": "_fd_mariadb",
1616 "maria": "_fd_mariadb",
1617 ".mvn": "_fd_maven",
1618 "mediawiki": "_fd_mediawiki",
1619 "memcached": "_fd_memcached",
1620 ".memcached": "_fd_memcached",
1621 ".meteor": "_fd_meteor",
1622 "middleware": "_fd_middleware",
1623 "middlewares": "_fd_middleware",
1624 ".minecraft": "_fd_minecraft",
1625 "minikube": "_fd_minikube",
1626 "minik8s": "_fd_minikube",
1627 "minikuber": "_fd_minikube",
1628 "mjml": "_fd_mjml",
1629 ".mjml": "_fd_mjml",
1630 "mocks": "_fd_mock",
1631 ".mocks": "_fd_mock",
1632 "__mocks__": "_fd_mock",
1633 "model": "_fd_model",
1634 ".model": "_fd_model",
1635 "models": "_fd_model",
1636 ".models": "_fd_model",
1637 "entities": "_fd_model",
1638 ".entities": "_fd_model",
1639 "modules": "_fd_module",
1640 "mojo": "_fd_mojo",
1641 "mongodb": "_fd_mongodb",
1642 "mongo": "_fd_mongodb",
1643 ".mypy_cache": "_fd_mypy",
1644 "mysqldb": "_fd_mysql",
1645 "mysql": "_fd_mysql",
1646 "netlify": "_fd_netlify",
1647 ".next": "_fd_next",
1648 "nginx": "_fd_nginx",
1649 "conf.d": "_fd_nginx",
1650 ".niv": "_fd_nix",
1651 ".nix": "_fd_nix",
1652 "nix": "_fd_nix",
1653 "niv": "_fd_nix",
1654 "node_modules": "_fd_node",
1655 "notebooks": "_fd_notebooks",
1656 "notebook": "_fd_notebooks",
1657 "notification": "_fd_notification",
1658 "notifications": "_fd_notification",
1659 "event": "_fd_notification",
1660 "events": "_fd_notification",
1661 ".nuget": "_fd_nuget",
1662 "nuxt": "_fd_nuxt",
1663 ".nuxt": "_fd_nuxt",
1664 "package": "_fd_package",
1665 "packages": "_fd_package",
1666 ".package": "_fd_package",
1667 ".packages": "_fd_package",
1668 "pkg": "_fd_package",
1669 ".paket": "_fd_paket",
1670 "php": "_fd_php",
1671 ".pio": "_fd_platformio",
1672 ".pioenvs": "_fd_platformio",
1673 "plugin": "_fd_plugin",
1674 ".plugin": "_fd_plugin",
1675 "plugins": "_fd_plugin",
1676 ".plugins": "_fd_plugin",
1677 "extension": "_fd_plugin",
1678 ".extension": "_fd_plugin",
1679 "extensions": "_fd_plugin",
1680 ".extensions": "_fd_plugin",
1681 "prisma": "_fd_prisma",
1682 "private": "_fd_private",
1683 ".private": "_fd_private",
1684 "public": "_fd_public",
1685 ".public": "_fd_public",
1686 ".pytest_cache": "_fd_pytest",
1687 ".venv": "_fd_python",
1688 ".virtualenv": "_fd_python",
1689 "__pycache__": "_fd_python",
1690 "ravendb": "_fd_ravendb",
1691 "redis": "_fd_redis",
1692 "redux": "_fd_redux",
1693 "route": "_fd_route",
1694 "routes": "_fd_route",
1695 "_route": "_fd_route",
1696 "_routes": "_fd_route",
1697 "router": "_fd_route",
1698 "routers": "_fd_route",
1699 "sass": "_fd_sass",
1700 "scss": "_fd_sass",
1701 "_sass": "_fd_sass",
1702 "_scss": "_fd_sass",
1703 "script": "_fd_script",
1704 "scripts": "_fd_script",
1705 "seedkit": "_fd_seedkit",
1706 ".seedkit": "_fd_seedkit",
1707 "server": "_fd_server",
1708 "service": "_fd_services",
1709 "services": "_fd_services",
1710 "share": "_fd_shared",
1711 "shared": "_fd_shared",
1712 ".share": "_fd_shared",
1713 ".shared": "_fd_shared",
1714 "__shared__": "_fd_shared",
1715 "__share__": "_fd_shared",
1716 ".snaplet": "_fd_snaplet",
1717 ".spin": "_fd_spin",
1718 "src": "_fd_src",
1719 "source": "_fd_src",
1720 "sources": "_fd_src",
1721 "__src__": "_fd_src",
1722 "sso": "_fd_sso",
1723 "story": "_fd_story",
1724 "stories": "_fd_story",
1725 "__stories__": "_fd_story",
1726 ".storybook": "_fd_story",
1727 "style": "_fd_style",
1728 "styles": "_fd_style",
1729 "supabase": "_fd_supabase",
1730 "svelte": "_fd_svelte",
1731 ".svelte-kit": "_fd_svelte",
1732 "src-tauri": "_fd_tauri",
1733 "temp": "_fd_temp",
1734 ".temp": "_fd_temp",
1735 "tmp": "_fd_temp",
1736 ".tmp": "_fd_temp",
1737 "template": "_fd_template",
1738 ".template": "_fd_template",
1739 "templates": "_fd_template",
1740 ".templates": "_fd_template",
1741 "tests": "_fd_test",
1742 ".tests": "_fd_test",
1743 "test": "_fd_test",
1744 ".test": "_fd_test",
1745 "__tests__": "_fd_test",
1746 "__test__": "_fd_test",
1747 "spec": "_fd_test",
1748 ".spec": "_fd_test",
1749 "specs": "_fd_test",
1750 ".specs": "_fd_test",
1751 "integration": "_fd_test",
1752 "theme": "_fd_theme",
1753 "themes": "_fd_theme",
1754 "tool": "_fd_tools",
1755 "tools": "_fd_tools",
1756 ".tools": "_fd_tools",
1757 "util": "_fd_tools",
1758 "utils": "_fd_tools",
1759 "utilities": "_fd_tools",
1760 "tooling": "_fd_tools",
1761 ".travis": "_fd_travis",
1762 ".trunk": "_fd_trunk",
1763 ".turbo": "_fd_turbo",
1764 "typescript": "_fd_typescript",
1765 "ts": "_fd_typescript",
1766 "typings": "_fd_typings",
1767 "types": "_fd_typings",
1768 "@types": "_fd_typings",
1769 "vagrant": "_fd_vagrant",
1770 ".vagrant": "_fd_vagrant",
1771 ".vercel": "_fd_vercel",
1772 "video": "_fd_video",
1773 ".video": "_fd_video",
1774 "videos": "_fd_video",
1775 ".videos": "_fd_video",
1776 "html": "_fd_view",
1777 "view": "_fd_view",
1778 "views": "_fd_view",
1779 "layout": "_fd_view",
1780 "layouts": "_fd_view",
1781 "page": "_fd_view",
1782 "pages": "_fd_view",
1783 "_view": "_fd_view",
1784 "_views": "_fd_view",
1785 "_layout": "_fd_view",
1786 "_layouts": "_fd_view",
1787 "_page": "_fd_view",
1788 "_pages": "_fd_view",
1789 ".vitepress": "_fd_vitepress",
1790 ".vs": "_fd_vs",
1791 ".vscode": "_fd_vscode",
1792 "vscode": "_fd_vscode",
1793 ".vscode-test": "_fd_vscode_test",
1794 "wasm": "_fd_wasm",
1795 "WASM": "_fd_wasm",
1796 "webassembly": "_fd_wasm",
1797 "WebAssembly": "_fd_wasm",
1798 "webpack": "_fd_webpack",
1799 ".webpack": "_fd_webpack",
1800 "windows": "_fd_windows",
1801 "win32": "_fd_windows",
1802 ".windsurf": "_fd_windsurf",
1803 "wit": "_fd_wit",
1804 ".web": "_fd_www",
1805 "www": "_fd_www",
1806 "wwwroot": "_fd_www",
1807 "web": "_fd_www",
1808 ".yarn": "_fd_yarn",
1809 ".zed": "_fd_zed"
1810 },
1811 "folderNamesExpanded": {
1812 "android": "_fd_android_open",
1813 ".angular": "_fd_angular_open",
1814 "apache": "_fd_apache_open",
1815 "apache2": "_fd_apache_open",
1816 "httpd": "_fd_apache_open",
1817 "api": "_fd_api_open",
1818 ".api": "_fd_api_open",
1819 "apis": "_fd_api_open",
1820 ".apis": "_fd_api_open",
1821 "app": "_fd_app_open",
1822 "apps": "_fd_app_open",
1823 ".app": "_fd_app_open",
1824 "application": "_fd_app_open",
1825 "applications": "_fd_app_open",
1826 "arangodb": "_fd_arangodb_open",
1827 "arango": "_fd_arangodb_open",
1828 "assets": "_fd_asset_open",
1829 ".assets": "_fd_asset_open",
1830 ".astro": "_fd_astro_open",
1831 "audio": "_fd_audio_open",
1832 ".audio": "_fd_audio_open",
1833 "audios": "_fd_audio_open",
1834 ".audios": "_fd_audio_open",
1835 "sound": "_fd_audio_open",
1836 ".sound": "_fd_audio_open",
1837 "sounds": "_fd_audio_open",
1838 ".sounds": "_fd_audio_open",
1839 "aurelia_project": "_fd_aurelia_open",
1840 "aws": "_fd_aws_open",
1841 ".aws": "_fd_aws_open",
1842 "azure": "_fd_azure_open",
1843 ".azure": "_fd_azure_open",
1844 "azure-pipelines": "_fd_azurepipelines_open",
1845 ".azure-pipelines": "_fd_azurepipelines_open",
1846 "bin": "_fd_binary_open",
1847 ".bin": "_fd_binary_open",
1848 "blocs": "_fd_bloc_open",
1849 "bloc": "_fd_bloc_open",
1850 "blueprint": "_fd_blueprint_open",
1851 ".blueprint": "_fd_blueprint_open",
1852 "blueprints": "_fd_blueprint_open",
1853 ".blueprints": "_fd_blueprint_open",
1854 "bot": "_fd_bot_open",
1855 ".bot": "_fd_bot_open",
1856 "bower_components": "_fd_bower_open",
1857 ".buildkite": "_fd_buildkite_open",
1858 "cake": "_fd_cake_open",
1859 ".cake": "_fd_cake_open",
1860 "cargo": "_fd_cargo_open",
1861 ".cargo": "_fd_cargo_open",
1862 "crates": "_fd_cargo_open",
1863 "certificates": "_fd_certificate_open",
1864 ".certificates": "_fd_certificate_open",
1865 "certs": "_fd_certificate_open",
1866 "certs.": "_fd_certificate_open",
1867 ".changeset": "_fd_changesets_open",
1868 "chef": "_fd_chef_open",
1869 ".chef": "_fd_chef_open",
1870 ".circleci": "_fd_circleci_open",
1871 ".claude": "_fd_claude_open",
1872 "cli": "_fd_cli_open",
1873 "cmd": "_fd_cli_open",
1874 "command": "_fd_cli_open",
1875 "commands": "_fd_cli_open",
1876 "commandline": "_fd_cli_open",
1877 "console": "_fd_cli_open",
1878 "client": "_fd_client_open",
1879 "clients": "_fd_client_open",
1880 ".cloudflare": "_fd_cloudflare_open",
1881 "cloudflare": "_fd_cloudflare_open",
1882 ".cmake": "_fd_cmake_open",
1883 "cmake": "_fd_cmake_open",
1884 "common": "_fd_common_open",
1885 "component": "_fd_component_open",
1886 "components": "_fd_component_open",
1887 ".components": "_fd_component_open",
1888 "gui": "_fd_component_open",
1889 "src-ui": "_fd_component_open",
1890 "ui": "_fd_component_open",
1891 "widgets": "_fd_component_open",
1892 "composer": "_fd_composer_open",
1893 ".composer": "_fd_composer_open",
1894 "conf": "_fd_config_open",
1895 ".conf": "_fd_config_open",
1896 "config": "_fd_config_open",
1897 ".config": "_fd_config_open",
1898 "configs": "_fd_config_open",
1899 ".configs": "_fd_config_open",
1900 "configuration": "_fd_config_open",
1901 ".configuration": "_fd_config_open",
1902 "configurations": "_fd_config_open",
1903 ".configurations": "_fd_config_open",
1904 "setting": "_fd_config_open",
1905 ".setting": "_fd_config_open",
1906 "settings": "_fd_config_open",
1907 ".settings": "_fd_config_open",
1908 "ini": "_fd_config_open",
1909 ".ini": "_fd_config_open",
1910 "initializers": "_fd_config_open",
1911 ".initializers": "_fd_config_open",
1912 "__controller__": "_fd_controller_open",
1913 "__controllers__": "_fd_controller_open",
1914 "controller": "_fd_controller_open",
1915 "controllers": "_fd_controller_open",
1916 ".controllers": "_fd_controller_open",
1917 "__handler__": "_fd_controller_open",
1918 "__handlers__": "_fd_controller_open",
1919 "handler": "_fd_controller_open",
1920 ".handler": "_fd_controller_open",
1921 "handlers": "_fd_controller_open",
1922 ".handlers": "_fd_controller_open",
1923 "coverage": "_fd_coverage_open",
1924 "css": "_fd_css_open",
1925 "_css": "_fd_css_open",
1926 "cubits": "_fd_cubit_open",
1927 "cubit": "_fd_cubit_open",
1928 ".cursor": "_fd_cursor_open",
1929 "cypress": "_fd_cypress_open",
1930 ".dapr": "_fd_dapr_open",
1931 "dapr": "_fd_dapr_open",
1932 "dart": "_fd_dart_open",
1933 ".dart_tool": "_fd_dart_open",
1934 "datadog": "_fd_datadog_open",
1935 ".datadog": "_fd_datadog_open",
1936 "db": "_fd_db_open",
1937 "database": "_fd_db_open",
1938 "sql": "_fd_db_open",
1939 "data": "_fd_db_open",
1940 "repo": "_fd_db_open",
1941 "repository": "_fd_db_open",
1942 "repositories": "_fd_db_open",
1943 "store": "_fd_db_open",
1944 "stores": "_fd_db_open",
1945 "debian": "_fd_debian_open",
1946 "deb": "_fd_debian_open",
1947 ".dependabot": "_fd_dependabot_open",
1948 ".devcontainer": "_fd_devcontainer_open",
1949 "dist": "_fd_dist_open",
1950 ".dist": "_fd_dist_open",
1951 "dists": "_fd_dist_open",
1952 "out": "_fd_dist_open",
1953 "outs": "_fd_dist_open",
1954 "export": "_fd_dist_open",
1955 "exports": "_fd_dist_open",
1956 "build": "_fd_dist_open",
1957 ".build": "_fd_dist_open",
1958 "builds": "_fd_dist_open",
1959 "release": "_fd_dist_open",
1960 "releases": "_fd_dist_open",
1961 "target": "_fd_dist_open",
1962 "targets": "_fd_dist_open",
1963 "docker": "_fd_docker_open",
1964 ".docker": "_fd_docker_open",
1965 "docs": "_fd_docs_open",
1966 "doc": "_fd_docs_open",
1967 "document": "_fd_docs_open",
1968 "documents": "_fd_docs_open",
1969 "e2e": "_fd_e2e_open",
1970 ".elasticbeanstalk": "_fd_elasticbeanstalk_open",
1971 ".ebextensions": "_fd_elasticbeanstalk_open",
1972 "electron": "_fd_electron_open",
1973 ".expo": "_fd_expo_open",
1974 ".expo-shared": "_fd_expo_open",
1975 "favicon": "_fd_favicon_open",
1976 "favicons": "_fd_favicon_open",
1977 "flow": "_fd_flow_open",
1978 "flow-typed": "_fd_flow_open",
1979 "flutter": "_fd_flutter_open",
1980 "Flutter": "_fd_flutter_open",
1981 "fonts": "_fd_fonts_open",
1982 "font": "_fd_fonts_open",
1983 "fnt": "_fd_fonts_open",
1984 "webfonts": "_fd_fonts_open",
1985 ".front-commerce": "_fd_frontcommerce_open",
1986 "gcp": "_fd_gcp_open",
1987 ".gcp": "_fd_gcp_open",
1988 ".gemini": "_fd_gemini_open",
1989 ".git": "_fd_git_open",
1990 "submodules": "_fd_git_open",
1991 ".submodules": "_fd_git_open",
1992 ".github": "_fd_github_open",
1993 ".gitlab": "_fd_gitlab_open",
1994 "godot": "_fd_godot_open",
1995 ".godot": "_fd_godot_open",
1996 "gradle": "_fd_gradle_open",
1997 ".gradle": "_fd_gradle_open",
1998 "graphql": "_fd_graphql_open",
1999 "grunt": "_fd_grunt_open",
2000 "gulp": "_fd_gulp_open",
2001 "gulpfile.js": "_fd_gulp_open",
2002 "gulpfile.coffee": "_fd_gulp_open",
2003 "gulpfile.ts": "_fd_gulp_open",
2004 "gulpfile.babel.js": "_fd_gulp_open",
2005 "gulpfile.babel.coffee": "_fd_gulp_open",
2006 "gulpfile.babel.ts": "_fd_gulp_open",
2007 ".haxelib": "_fd_haxelib_open",
2008 "haxe_libraries": "_fd_haxelib_open",
2009 "helper": "_fd_helper_open",
2010 ".helper": "_fd_helper_open",
2011 "helpers": "_fd_helper_open",
2012 ".helpers": "_fd_helper_open",
2013 ".histoire": "_fd_histoire_open",
2014 "hook": "_fd_hook_open",
2015 ".hook": "_fd_hook_open",
2016 "hooks": "_fd_hook_open",
2017 ".hooks": "_fd_hook_open",
2018 ".husky": "_fd_husky_open",
2019 ".idea": "_fd_idea_open",
2020 ".fig": "_fd_images_open",
2021 ".figs": "_fd_images_open",
2022 "images": "_fd_images_open",
2023 "image": "_fd_images_open",
2024 ".img": "_fd_images_open",
2025 "img": "_fd_images_open",
2026 ".imgs": "_fd_images_open",
2027 "imgs": "_fd_images_open",
2028 "icons": "_fd_images_open",
2029 "icon": "_fd_images_open",
2030 "ico": "_fd_images_open",
2031 "screenshot": "_fd_images_open",
2032 "screenshots": "_fd_images_open",
2033 "svg": "_fd_images_open",
2034 "texture": "_fd_images_open",
2035 "textures": "_fd_images_open",
2036 "include": "_fd_include_open",
2037 "includes": "_fd_include_open",
2038 "incl": "_fd_include_open",
2039 "inc": "_fd_include_open",
2040 ".include": "_fd_include_open",
2041 ".includes": "_fd_include_open",
2042 ".incl": "_fd_include_open",
2043 ".inc": "_fd_include_open",
2044 "_include": "_fd_include_open",
2045 "_includes": "_fd_include_open",
2046 "_incl": "_fd_include_open",
2047 "_inc": "_fd_include_open",
2048 "interface": "_fd_interfaces_open",
2049 "interfaces": "_fd_interfaces_open",
2050 "ios": "_fd_ios_open",
2051 "js": "_fd_js_open",
2052 "json": "_fd_json_open",
2053 ".junie": "_fd_junie_open",
2054 "kotlin": "_fd_kotlin_open",
2055 "Kotlin": "_fd_kotlin_open",
2056 ".kotlin": "_fd_kotlin_open",
2057 "kubernetes": "_fd_kubernetes_open",
2058 "k8s": "_fd_kubernetes_open",
2059 "kube": "_fd_kubernetes_open",
2060 "kuber": "_fd_kubernetes_open",
2061 ".kubernetes": "_fd_kubernetes_open",
2062 ".k8s": "_fd_kubernetes_open",
2063 ".kube": "_fd_kubernetes_open",
2064 ".kuber": "_fd_kubernetes_open",
2065 "less": "_fd_less_open",
2066 "_less": "_fd_less_open",
2067 "lib": "_fd_library_open",
2068 "libs": "_fd_library_open",
2069 ".lib": "_fd_library_open",
2070 ".libs": "_fd_library_open",
2071 "__lib__": "_fd_library_open",
2072 "__libs__": "_fd_library_open",
2073 "library": "_fd_library_open",
2074 "libraries": "_fd_library_open",
2075 "linux": "_fd_linux_open",
2076 "lang": "_fd_locale_open",
2077 "language": "_fd_locale_open",
2078 "languages": "_fd_locale_open",
2079 "locale": "_fd_locale_open",
2080 "locales": "_fd_locale_open",
2081 "_locale": "_fd_locale_open",
2082 "_locales": "_fd_locale_open",
2083 "internationalization": "_fd_locale_open",
2084 "globalization": "_fd_locale_open",
2085 "localization": "_fd_locale_open",
2086 "i18n": "_fd_locale_open",
2087 "g11n": "_fd_locale_open",
2088 "l10n": "_fd_locale_open",
2089 "log": "_fd_log_open",
2090 "logs": "_fd_log_open",
2091 "macos": "_fd_macos_open",
2092 "darwin": "_fd_macos_open",
2093 "mariadb": "_fd_mariadb_open",
2094 "maria": "_fd_mariadb_open",
2095 ".mvn": "_fd_maven_open",
2096 "mediawiki": "_fd_mediawiki_open",
2097 "memcached": "_fd_memcached_open",
2098 ".memcached": "_fd_memcached_open",
2099 ".meteor": "_fd_meteor_open",
2100 "middleware": "_fd_middleware_open",
2101 "middlewares": "_fd_middleware_open",
2102 ".minecraft": "_fd_minecraft_open",
2103 "minikube": "_fd_minikube_open",
2104 "minik8s": "_fd_minikube_open",
2105 "minikuber": "_fd_minikube_open",
2106 "mjml": "_fd_mjml_open",
2107 ".mjml": "_fd_mjml_open",
2108 "mocks": "_fd_mock_open",
2109 ".mocks": "_fd_mock_open",
2110 "__mocks__": "_fd_mock_open",
2111 "model": "_fd_model_open",
2112 ".model": "_fd_model_open",
2113 "models": "_fd_model_open",
2114 ".models": "_fd_model_open",
2115 "entities": "_fd_model_open",
2116 ".entities": "_fd_model_open",
2117 "modules": "_fd_module_open",
2118 "mojo": "_fd_mojo_open",
2119 "mongodb": "_fd_mongodb_open",
2120 "mongo": "_fd_mongodb_open",
2121 ".mypy_cache": "_fd_mypy_open",
2122 "mysqldb": "_fd_mysql_open",
2123 "mysql": "_fd_mysql_open",
2124 "netlify": "_fd_netlify_open",
2125 ".next": "_fd_next_open",
2126 "nginx": "_fd_nginx_open",
2127 "conf.d": "_fd_nginx_open",
2128 ".niv": "_fd_nix_open",
2129 ".nix": "_fd_nix_open",
2130 "nix": "_fd_nix_open",
2131 "niv": "_fd_nix_open",
2132 "node_modules": "_fd_node_open",
2133 "notebooks": "_fd_notebooks_open",
2134 "notebook": "_fd_notebooks_open",
2135 "notification": "_fd_notification_open",
2136 "notifications": "_fd_notification_open",
2137 "event": "_fd_notification_open",
2138 "events": "_fd_notification_open",
2139 ".nuget": "_fd_nuget_open",
2140 "nuxt": "_fd_nuxt_open",
2141 ".nuxt": "_fd_nuxt_open",
2142 "package": "_fd_package_open",
2143 "packages": "_fd_package_open",
2144 ".package": "_fd_package_open",
2145 ".packages": "_fd_package_open",
2146 "pkg": "_fd_package_open",
2147 ".paket": "_fd_paket_open",
2148 "php": "_fd_php_open",
2149 ".pio": "_fd_platformio_open",
2150 ".pioenvs": "_fd_platformio_open",
2151 "plugin": "_fd_plugin_open",
2152 ".plugin": "_fd_plugin_open",
2153 "plugins": "_fd_plugin_open",
2154 ".plugins": "_fd_plugin_open",
2155 "extension": "_fd_plugin_open",
2156 ".extension": "_fd_plugin_open",
2157 "extensions": "_fd_plugin_open",
2158 ".extensions": "_fd_plugin_open",
2159 "prisma": "_fd_prisma_open",
2160 "private": "_fd_private_open",
2161 ".private": "_fd_private_open",
2162 "public": "_fd_public_open",
2163 ".public": "_fd_public_open",
2164 ".pytest_cache": "_fd_pytest_open",
2165 ".venv": "_fd_python_open",
2166 ".virtualenv": "_fd_python_open",
2167 "__pycache__": "_fd_python_open",
2168 "ravendb": "_fd_ravendb_open",
2169 "redis": "_fd_redis_open",
2170 "redux": "_fd_redux_open",
2171 "route": "_fd_route_open",
2172 "routes": "_fd_route_open",
2173 "_route": "_fd_route_open",
2174 "_routes": "_fd_route_open",
2175 "router": "_fd_route_open",
2176 "routers": "_fd_route_open",
2177 "sass": "_fd_sass_open",
2178 "scss": "_fd_sass_open",
2179 "_sass": "_fd_sass_open",
2180 "_scss": "_fd_sass_open",
2181 "script": "_fd_script_open",
2182 "scripts": "_fd_script_open",
2183 "seedkit": "_fd_seedkit_open",
2184 ".seedkit": "_fd_seedkit_open",
2185 "server": "_fd_server_open",
2186 "service": "_fd_services_open",
2187 "services": "_fd_services_open",
2188 "share": "_fd_shared_open",
2189 "shared": "_fd_shared_open",
2190 ".share": "_fd_shared_open",
2191 ".shared": "_fd_shared_open",
2192 "__shared__": "_fd_shared_open",
2193 "__share__": "_fd_shared_open",
2194 ".snaplet": "_fd_snaplet_open",
2195 ".spin": "_fd_spin_open",
2196 "src": "_fd_src_open",
2197 "source": "_fd_src_open",
2198 "sources": "_fd_src_open",
2199 "__src__": "_fd_src_open",
2200 "sso": "_fd_sso_open",
2201 "story": "_fd_story_open",
2202 "stories": "_fd_story_open",
2203 "__stories__": "_fd_story_open",
2204 ".storybook": "_fd_story_open",
2205 "style": "_fd_style_open",
2206 "styles": "_fd_style_open",
2207 "supabase": "_fd_supabase_open",
2208 "svelte": "_fd_svelte_open",
2209 ".svelte-kit": "_fd_svelte_open",
2210 "src-tauri": "_fd_tauri_open",
2211 "temp": "_fd_temp_open",
2212 ".temp": "_fd_temp_open",
2213 "tmp": "_fd_temp_open",
2214 ".tmp": "_fd_temp_open",
2215 "template": "_fd_template_open",
2216 ".template": "_fd_template_open",
2217 "templates": "_fd_template_open",
2218 ".templates": "_fd_template_open",
2219 "tests": "_fd_test_open",
2220 ".tests": "_fd_test_open",
2221 "test": "_fd_test_open",
2222 ".test": "_fd_test_open",
2223 "__tests__": "_fd_test_open",
2224 "__test__": "_fd_test_open",
2225 "spec": "_fd_test_open",
2226 ".spec": "_fd_test_open",
2227 "specs": "_fd_test_open",
2228 ".specs": "_fd_test_open",
2229 "integration": "_fd_test_open",
2230 "theme": "_fd_theme_open",
2231 "themes": "_fd_theme_open",
2232 "tool": "_fd_tools_open",
2233 "tools": "_fd_tools_open",
2234 ".tools": "_fd_tools_open",
2235 "util": "_fd_tools_open",
2236 "utils": "_fd_tools_open",
2237 "utilities": "_fd_tools_open",
2238 "tooling": "_fd_tools_open",
2239 ".travis": "_fd_travis_open",
2240 ".trunk": "_fd_trunk_open",
2241 ".turbo": "_fd_turbo_open",
2242 "typescript": "_fd_typescript_open",
2243 "ts": "_fd_typescript_open",
2244 "typings": "_fd_typings_open",
2245 "types": "_fd_typings_open",
2246 "@types": "_fd_typings_open",
2247 "vagrant": "_fd_vagrant_open",
2248 ".vagrant": "_fd_vagrant_open",
2249 ".vercel": "_fd_vercel_open",
2250 "video": "_fd_video_open",
2251 ".video": "_fd_video_open",
2252 "videos": "_fd_video_open",
2253 ".videos": "_fd_video_open",
2254 "html": "_fd_view_open",
2255 "view": "_fd_view_open",
2256 "views": "_fd_view_open",
2257 "layout": "_fd_view_open",
2258 "layouts": "_fd_view_open",
2259 "page": "_fd_view_open",
2260 "pages": "_fd_view_open",
2261 "_view": "_fd_view_open",
2262 "_views": "_fd_view_open",
2263 "_layout": "_fd_view_open",
2264 "_layouts": "_fd_view_open",
2265 "_page": "_fd_view_open",
2266 "_pages": "_fd_view_open",
2267 ".vitepress": "_fd_vitepress_open",
2268 ".vs": "_fd_vs_open",
2269 ".vscode": "_fd_vscode_open",
2270 "vscode": "_fd_vscode_open",
2271 ".vscode-test": "_fd_vscode_test_open",
2272 "wasm": "_fd_wasm_open",
2273 "WASM": "_fd_wasm_open",
2274 "webassembly": "_fd_wasm_open",
2275 "WebAssembly": "_fd_wasm_open",
2276 "webpack": "_fd_webpack_open",
2277 ".webpack": "_fd_webpack_open",
2278 "windows": "_fd_windows_open",
2279 "win32": "_fd_windows_open",
2280 ".windsurf": "_fd_windsurf_open",
2281 "wit": "_fd_wit_open",
2282 ".web": "_fd_www_open",
2283 "www": "_fd_www_open",
2284 "wwwroot": "_fd_www_open",
2285 "web": "_fd_www_open",
2286 ".yarn": "_fd_yarn_open",
2287 ".zed": "_fd_zed_open"
2288 },
2289 "fileExtensions": {
2290 "accdb": "_f_access",
2291 "accdt": "_f_access",
2292 "mdb": "_f_access",
2293 "accda": "_f_access",
2294 "accdc": "_f_access",
2295 "accde": "_f_access",
2296 "accdp": "_f_access",
2297 "accdr": "_f_access",
2298 "accdu": "_f_access",
2299 "ade": "_f_access",
2300 "adp": "_f_access",
2301 "laccdb": "_f_access",
2302 "ldb": "_f_access",
2303 "mam": "_f_access",
2304 "maq": "_f_access",
2305 "mdw": "_f_access",
2306 "af": "_f_affinity",
2307 "aftemplate": "_f_affinity",
2308 "afdesign": "_f_affinitydesigner",
2309 "affinitydesigner": "_f_affinitydesigner",
2310 "afphoto": "_f_affinityphoto",
2311 "affinityphoto": "_f_affinityphoto",
2312 "afpub": "_f_affinitypublisher",
2313 "affinitypublisher": "_f_affinitypublisher",
2314 "ai": "_f_ai",
2315 "dal": "_f_al_dal",
2316 "gs": "_f_appscript",
2317 "ino": "_f_arduino",
2318 "pde": "_f_arduino",
2319 "ase": "_f_aseprite",
2320 "aseprite": "_f_aseprite",
2321 "aspx": "_f_aspx",
2322 "ascx": "_f_aspx",
2323 "asm": "_f_assembly",
2324 "s": "_f_assembly",
2325 "astro": "_f_astro",
2326 "atom": "_f_atom",
2327 "aac": "_f_audio",
2328 "act": "_f_audio",
2329 "aiff": "_f_audio",
2330 "amr": "_f_audio",
2331 "ape": "_f_audio",
2332 "au": "_f_audio",
2333 "dct": "_f_audio",
2334 "dss": "_f_audio",
2335 "dvf": "_f_audio",
2336 "flac": "_f_audio",
2337 "gsm": "_f_audio",
2338 "iklax": "_f_audio",
2339 "ivs": "_f_audio",
2340 "m4a": "_f_audio",
2341 "m4b": "_f_audio",
2342 "m4p": "_f_audio",
2343 "mmf": "_f_audio",
2344 "mogg": "_f_audio",
2345 "mp3": "_f_audio",
2346 "mpc": "_f_audio",
2347 "msv": "_f_audio",
2348 "oga": "_f_audio",
2349 "ogg": "_f_audio",
2350 "opus": "_f_audio",
2351 "ra": "_f_audio",
2352 "raw": "_f_audio",
2353 "tta": "_f_audio",
2354 "vox": "_f_audio",
2355 "wav": "_f_audio",
2356 "wma": "_f_audio",
2357 "avif": "_f_avif",
2358 "awk": "_f_awk",
2359 "back": "_f_bak",
2360 "backup": "_f_bak",
2361 "bak": "_f_bak",
2362 "a": "_f_binary",
2363 "app": "_f_binary",
2364 "bin": "_f_binary",
2365 "cmo": "_f_binary",
2366 "cmx": "_f_binary",
2367 "cma": "_f_binary",
2368 "cmxa": "_f_binary",
2369 "cmi": "_f_binary",
2370 "dll": "_f_binary",
2371 "exe": "_f_binary",
2372 "hl": "_f_binary",
2373 "ilk": "_f_binary",
2374 "lib": "_f_binary",
2375 "n": "_f_binary",
2376 "ndll": "_f_binary",
2377 "o": "_f_binary",
2378 "obj": "_f_binary",
2379 "pyc": "_f_binary",
2380 "pyd": "_f_binary",
2381 "pyo": "_f_binary",
2382 "pdb": "_f_binary",
2383 "scpt": "_f_binary",
2384 "scptd": "_f_binary",
2385 "so": "_f_binary",
2386 "blend": "_f_blender",
2387 "bb": "_f_blitzbasic",
2388 "buf.yaml": "_f_buf",
2389 "buf.yml": "_f_buf",
2390 "buf.gen.yml": "_f_buf",
2391 "buf.gen.yaml": "_f_buf",
2392 "buf.work.yaml": "_f_buf",
2393 "buf.work.yml": "_f_buf",
2394 "gemfile": "_f_bundler",
2395 "gemfile.lock": "_f_bundler",
2396 "cake": "_f_cake",
2397 "csr": "_f_cert",
2398 "crt": "_f_cert",
2399 "cer": "_f_cert",
2400 "der": "_f_cert",
2401 "pfx": "_f_cert",
2402 "p12": "_f_cert",
2403 "p7b": "_f_cert",
2404 "p7r": "_f_cert",
2405 "src": "_f_cert",
2406 "crl": "_f_cert",
2407 "sst": "_f_cert",
2408 "stl": "_f_cert",
2409 "lucee": "_f_cf",
2410 "h": "_f_cheader",
2411 "class": "_f_class",
2412 "cjm": "_f_clojure",
2413 "cljc": "_f_clojure",
2414 "cljs": "_f_clojurescript",
2415 "kit": "_f_codekit",
2416 "plist": "_f_config",
2417 "lcov": "_f_coverage",
2418 "hpp": "_f_cppheader",
2419 "hh": "_f_cppheader",
2420 "hxx": "_f_cppheader",
2421 "h++": "_f_cppheader",
2422 "csx": "_f_csharp",
2423 "csproj": "_f_csproj",
2424 "css.map": "_f_cssmap",
2425 "cy.js": "_f_cypress_spec",
2426 "cy.mjs": "_f_cypress_spec",
2427 "cy.cjs": "_f_cypress_spec",
2428 "cy.coffee": "_f_cypress_spec",
2429 "cy.ts": "_f_cypress_spec",
2430 "cy.tsx": "_f_cypress_spec",
2431 "cy.jsx": "_f_cypress_spec",
2432 "g.dart": "_f_dartlang_generated",
2433 "freezed.dart": "_f_dartlang_generated",
2434 "db": "_f_db",
2435 "deb": "_f_debian",
2436 "resolver.json": "_f_design_tokens",
2437 "resolver.json5": "_f_design_tokens",
2438 "resolver.jsonc": "_f_design_tokens",
2439 "tokens.json": "_f_design_tokens",
2440 "tokens.json5": "_f_design_tokens",
2441 "tokens.jsonc": "_f_design_tokens",
2442 "djt": "_f_django",
2443 "eco": "_f_docpad",
2444 "drawio": "_f_drawio",
2445 "dio": "_f_drawio",
2446 "drawio.png": "_f_drawio",
2447 "drawio.svg": "_f_drawio",
2448 "dio.png": "_f_drawio",
2449 "dio.svg": "_f_drawio",
2450 "dtd": "_f_dtd",
2451 "eex": "_f_eex",
2452 "heex": "_f_eex",
2453 "phoenix-heex": "_f_eex",
2454 "html-heex": "_f_eex",
2455 "ejs": "_f_ejs",
2456 "el": "_f_emacs",
2457 "elc": "_f_emacs",
2458 "ensime": "_f_ensime",
2459 "eps": "_f_eps",
2460 "epub": "_f_epub",
2461 "excalidraw": "_f_excalidraw",
2462 "excalidraw.json": "_f_excalidraw",
2463 "excalidraw.svg": "_f_excalidraw",
2464 "excalidraw.png": "_f_excalidraw",
2465 "xls": "_f_excel",
2466 "xlsx": "_f_excel",
2467 "xlsm": "_f_excel",
2468 "ods": "_f_libreoffice_calc",
2469 "fods": "_f_libreoffice_calc",
2470 "xlsb": "_f_excel",
2471 "fbx": "_f_fbx",
2472 "fba": "_f_fitbit",
2473 "fla": "_f_fla",
2474 "swf": "_f_flash",
2475 "swc": "_f_flash",
2476 "js.flow": "_f_flow",
2477 "woff": "_f_font",
2478 "woff2": "_f_font",
2479 "ttf": "_f_font",
2480 "otf": "_f_libreoffice_math",
2481 "eot": "_f_font",
2482 "pfa": "_f_font",
2483 "pfb": "_f_font",
2484 "sfd": "_f_font",
2485 "fsproj": "_f_fsproj",
2486 "gmx": "_f_gamemaker",
2487 "yy": "_f_gamemaker2",
2488 "yyp": "_f_gamemaker2",
2489 "gd.uid": "_f_gduid",
2490 "geojson": "_f_geojson",
2491 "xcf": "_f_gimp",
2492 "gleam": "_f_gleam",
2493 "glb": "_f_gltf",
2494 "gltf": "_f_gltf",
2495 "am": "_f_gnu",
2496 "ld": "_f_gnu",
2497 "m4": "_f_gnu",
2498 "makefile": "_f_gnu",
2499 "godot": "_f_godot",
2500 "gpg": "_f_gpg",
2501 "pgp": "_f_gpg",
2502 "gradle": "_f_gradle",
2503 "gr": "_f_grain",
2504 "hcl2": "_f_hashicorp",
2505 "sentinel": "_f_hashicorp",
2506 "hxproj": "_f_haxedevelop",
2507 "ejs.t": "_f_hygen",
2508 "idr": "_f_idris",
2509 "lidr": "_f_idris",
2510 "ibc": "_f_idrisbin",
2511 "ipkg": "_f_idrispkg",
2512 "jpeg": "_f_image",
2513 "jpg": "_f_image",
2514 "gif": "_f_image",
2515 "png": "_f_image",
2516 "bmp": "_f_image",
2517 "tiff": "_f_image",
2518 "heic": "_f_image",
2519 "ico": "_f_image",
2520 "icns": "_f_image",
2521 "webp": "_f_image",
2522 "imba": "_f_imba",
2523 "imba2": "_f_imba",
2524 "inc": "_f_inc",
2525 "include": "_f_inc",
2526 "infopathxml": "_f_infopath",
2527 "xsn": "_f_infopath",
2528 "xsf": "_f_infopath",
2529 "xtp2": "_f_infopath",
2530 "jar": "_f_jar",
2531 "jbuilder": "_f_jbuilder",
2532 "iml": "_f_jetbrains",
2533 "js.map": "_f_jsmap",
2534 "cjs.map": "_f_jsmap",
2535 "mjs.map": "_f_jsmap",
2536 "jsonl": "_f_json",
2537 "ndjson": "_f_json",
2538 "json5": "_f_json5",
2539 "schema.json": "_f_json_schema",
2540 "jsonld": "_f_jsonld",
2541 "json-ld": "_f_jsonld",
2542 "jsp": "_f_jsp",
2543 "jss": "_f_jss",
2544 "juice": "_f_juice",
2545 "ipynb": "_f_jupyter",
2546 "key": "_f_key",
2547 "pem": "_f_key",
2548 "ktm": "_f_kotlin",
2549 "language-configuration.json": "_f_language_configuration",
2550 "master": "_f_layout",
2551 "layout.html": "_f_layout",
2552 "layout.htm": "_f_layout",
2553 "odb": "_f_libreoffice_base",
2554 "otb": "_f_libreoffice_base",
2555 "ots": "_f_libreoffice_calc",
2556 "odg": "_f_libreoffice_draw",
2557 "otg": "_f_libreoffice_draw",
2558 "odp": "_f_libreoffice_impress",
2559 "otp": "_f_libreoffice_impress",
2560 "odf": "_f_libreoffice_math",
2561 "odt": "_f_libreoffice_writer",
2562 "ott": "_f_libreoffice_writer",
2563 "enc": "_f_license",
2564 "license": "_f_license",
2565 "lic": "_f_license",
2566 "hxp": "_f_lime",
2567 "liquid": "_f_liquid",
2568 "ls": "_f_livescript",
2569 "lnk": "_f_lnk",
2570 "log": "_f_log",
2571 "tlg": "_f_log",
2572 "luau": "_f_luau",
2573 "crec": "_f_lync",
2574 "ocrec": "_f_lync",
2575 "map": "_f_map",
2576 "mdown": "_f_markdown",
2577 "markdown": "_f_markdown",
2578 "marko.js": "_f_markojs",
2579 "fig": "_f_matlab",
2580 "mex": "_f_matlab",
2581 "mexn": "_f_matlab",
2582 "mexrs6": "_f_matlab",
2583 "mn": "_f_matlab",
2584 "mum": "_f_matlab",
2585 "mx": "_f_matlab",
2586 "mx3": "_f_matlab",
2587 "rwd": "_f_matlab",
2588 "slx": "_f_matlab",
2589 "slddc": "_f_matlab",
2590 "smv": "_f_matlab",
2591 "xvc": "_f_matlab",
2592 "mca": "_f_minecraft",
2593 "mcmeta": "_f_minecraft",
2594 "mojo": "_f_mojo",
2595 "🔥": "_f_mojo",
2596 "mql.yaml": "_f_mondoo",
2597 "mql.yml": "_f_mondoo",
2598 "mustache": "_f_mustache",
2599 "mst": "_f_mustache",
2600 "ndst.yaml": "_f_ndst",
2601 "ndst.yml": "_f_ndst",
2602 "ndst.json": "_f_ndst",
2603 "njsproj": "_f_njsproj",
2604 "noc": "_f_noc",
2605 "nupkg": "_f_nuget",
2606 "snupkg": "_f_nuget",
2607 "nuspec": "_f_nuget",
2608 "psmdcp": "_f_nuget",
2609 "npy": "_f_numpy",
2610 "npz": "_f_numpy",
2611 "nunj": "_f_nunjucks",
2612 "njs": "_f_nunjucks",
2613 "mli": "_f_ocaml_intf",
2614 "one": "_f_onenote",
2615 "onepkg": "_f_onenote",
2616 "onetoc": "_f_onenote",
2617 "onetoc2": "_f_onenote",
2618 "sig": "_f_onenote",
2619 "opam": "_f_opam",
2620 "cl": "_f_opencl",
2621 "opencl": "_f_opencl",
2622 "tofu": "_f_opentofu",
2623 "tofu.json": "_f_opentofu",
2624 "tofutest.hcl": "_f_opentofu",
2625 "tofutest.json": "_f_opentofu",
2626 "org": "_f_org",
2627 "pst": "_f_outlook",
2628 "bcmx": "_f_outlook",
2629 "otm": "_f_outlook",
2630 "msg": "_f_outlook",
2631 "oft": "_f_outlook",
2632 "ovpn": "_f_ovpn",
2633 "pkg": "_f_package",
2634 "paket.references": "_f_paket",
2635 "paket.template": "_f_paket",
2636 "patch": "_f_patch",
2637 "pcd": "_f_pcl",
2638 "pdf": "_f_pdf",
2639 "psd": "_f_photoshop",
2640 "php1": "_f_php",
2641 "php2": "_f_php",
2642 "php3": "_f_php",
2643 "php4": "_f_php",
2644 "php5": "_f_php",
2645 "php6": "_f_php",
2646 "phps": "_f_php",
2647 "phpsa": "_f_php",
2648 "phpt": "_f_php",
2649 "phtml": "_f_php",
2650 "phar": "_f_php",
2651 "pipeline": "_f_pipeline",
2652 "pu": "_f_plantuml",
2653 "plantuml": "_f_plantuml",
2654 "iuml": "_f_plantuml",
2655 "puml": "_f_plantuml",
2656 "pck": "_f_plsql_package",
2657 "pkb": "_f_plsql_package_body",
2658 "pkh": "_f_plsql_package_header",
2659 "pks": "_f_plsql_package_spec",
2660 "po": "_f_poedit",
2661 "mo": "_f_poedit",
2662 "postman_collection.json": "_f_postman",
2663 "postman_environment.json": "_f_postman",
2664 "postman_globals.json": "_f_postman",
2665 "pot": "_f_powerpoint",
2666 "potx": "_f_powerpoint",
2667 "potm": "_f_powerpoint",
2668 "pps": "_f_powerpoint",
2669 "ppsx": "_f_powerpoint",
2670 "ppsm": "_f_powerpoint",
2671 "ppt": "_f_powerpoint",
2672 "pptx": "_f_powerpoint",
2673 "pptm": "_f_powerpoint",
2674 "pa": "_f_powerpoint",
2675 "ppa": "_f_powerpoint",
2676 "ppam": "_f_powerpoint",
2677 "sldm": "_f_powerpoint",
2678 "sldx": "_f_powerpoint",
2679 "format.ps1xml": "_f_powershell_format",
2680 "psd1": "_f_powershell_psd",
2681 "psm1": "_f_powershell_psm",
2682 "types.ps1xml": "_f_powershell_types",
2683 "pro": "_f_prolog",
2684 "pub": "_f_publisher",
2685 "puz": "_f_publisher",
2686 "pyowo": "_f_pythowo",
2687 "q": "_f_q",
2688 "qbs": "_f_qbs",
2689 "qvd": "_f_qlikview",
2690 "qvw": "_f_qlikview",
2691 "rast": "_f_ra_syntax_tree",
2692 "rake": "_f_rake",
2693 "rt": "_f_reacttemplate",
2694 "reg": "_f_registry",
2695 "rego": "_f_rego",
2696 "ripple": "_f_ripple",
2697 "rnc": "_f_rnc",
2698 "ron": "_f_ron",
2699 "rproj": "_f_rproj",
2700 "rss": "_f_rss",
2701 "rbs": "_f_ruby",
2702 "safetensors": "_f_safetensors",
2703 "sls": "_f_saltstack",
2704 "sass": "_f_sass",
2705 "scssm": "_f_scss",
2706 "unity": "_f_shaderlab",
2707 "fish": "_f_shell",
2708 "sy": "_f_siyuan",
2709 "sketch": "_f_sketch",
2710 "eskip": "_f_skipper",
2711 "sln": "_f_sln",
2712 "slnx": "_f_sln",
2713 "slnf": "_f_sln",
2714 "spe": "_f_spacengine",
2715 "sqlite": "_f_sqlite",
2716 "sqlite3": "_f_sqlite",
2717 "db3": "_f_sqlite",
2718 "sss": "_f_sss",
2719 "dta": "_f_stata",
2720 "storyboard": "_f_storyboard",
2721 "story.js": "_f_storybook",
2722 "story.jsx": "_f_storybook",
2723 "story.ts": "_f_storybook",
2724 "story.tsx": "_f_storybook",
2725 "story.mdx": "_f_storybook",
2726 "story.svelte": "_f_storybook",
2727 "stories.js": "_f_storybook",
2728 "stories.jsx": "_f_storybook",
2729 "stories.ts": "_f_storybook",
2730 "stories.tsx": "_f_storybook",
2731 "stories.mdx": "_f_storybook",
2732 "stories.svelte": "_f_storybook",
2733 "sublime-project": "_f_sublime",
2734 "svg": "_f_svg",
2735 "tcl": "_f_tcl",
2736 "exp": "_f_tcl",
2737 "templ": "_f_templ",
2738 "tfstate": "_f_terraform",
2739 "tfvars": "_f_terraform",
2740 "tf": "_f_terraform",
2741 "tf.json": "_f_terraform",
2742 "tst": "_f_test",
2743 "test.js": "_f_testjs",
2744 "test.jsx": "_f_testjs",
2745 "test.mjs": "_f_testjs",
2746 "spec.js": "_f_testjs",
2747 "spec.jsx": "_f_testjs",
2748 "spec.mjs": "_f_testjs",
2749 "testplane.ts": "_f_testplane",
2750 "testplane.js": "_f_testplane",
2751 "testplane.cts": "_f_testplane",
2752 "testplane.cjs": "_f_testplane",
2753 "testplane.tsx": "_f_testplane",
2754 "testplane.jsx": "_f_testplane",
2755 "test.ts": "_f_testts",
2756 "test.tsx": "_f_testts",
2757 "test.mts": "_f_testts",
2758 "spec.ts": "_f_testts",
2759 "spec.tsx": "_f_testts",
2760 "spec.mts": "_f_testts",
2761 "e2e-test.ts": "_f_testts",
2762 "e2e-test.tsx": "_f_testts",
2763 "e2e-test.mts": "_f_testts",
2764 "e2e-spec.ts": "_f_testts",
2765 "e2e-spec.tsx": "_f_testts",
2766 "e2e-spec.mts": "_f_testts",
2767 "test-d.ts": "_f_testts",
2768 "test-d.tsx": "_f_testts",
2769 "test-d.mts": "_f_testts",
2770 "texi": "_f_tex",
2771 "tikz": "_f_tex",
2772 "csv": "_f_text",
2773 "tsv": "_f_text",
2774 "tm": "_f_tm",
2775 "tm.hcl": "_f_tm",
2776 "todo": "_f_todo",
2777 "tree": "_f_tree",
2778 "tres": "_f_tres",
2779 "tsbuildinfo": "_f_tsbuildinfo",
2780 "tscn": "_f_tscn",
2781 "tt2": "_f_tt",
2782 "d.ts": "_f_typescriptdef",
2783 "d.cts": "_f_typescriptdef",
2784 "d.mts": "_f_typescriptdef",
2785 "ua": "_f_uiua",
2786 "u": "_f_unison",
2787 "vala": "_f_vala",
2788 "css.ts": "_f_vanilla_extract",
2789 "vapi": "_f_vapi",
2790 "vash": "_f_vash",
2791 "vbhtml": "_f_vbhtml",
2792 "vbproj": "_f_vbproj",
2793 "vcxproj": "_f_vcxproj",
2794 "vto": "_f_vento",
2795 "vento": "_f_vento",
2796 "3g2": "_f_video",
2797 "3gp": "_f_video",
2798 "asf": "_f_video",
2799 "amv": "_f_video",
2800 "avi": "_f_video",
2801 "divx": "_f_video",
2802 "qt": "_f_video",
2803 "f4a": "_f_video",
2804 "f4b": "_f_video",
2805 "f4p": "_f_video",
2806 "f4v": "_f_video",
2807 "flv": "_f_video",
2808 "m2v": "_f_video",
2809 "m4v": "_f_video",
2810 "mkv": "_f_video",
2811 "mk3d": "_f_video",
2812 "mov": "_f_video",
2813 "mp2": "_f_video",
2814 "mp4": "_f_video",
2815 "mpe": "_f_video",
2816 "mpeg": "_f_video",
2817 "mpeg2": "_f_video",
2818 "mpg": "_f_video",
2819 "mpv": "_f_video",
2820 "nsv": "_f_video",
2821 "ogv": "_f_video",
2822 "rm": "_f_video",
2823 "rmvb": "_f_video",
2824 "svi": "_f_video",
2825 "vob": "_f_video",
2826 "webm": "_f_video",
2827 "wmv": "_f_video",
2828 "code-snippets": "_f_vscode",
2829 "code-workspace": "_f_vscode",
2830 "vsix": "_f_vsix",
2831 "vsixmanifest": "_f_vsixmanifest",
2832 "vvvvvv": "_f_vvvvvv",
2833 "vyi": "_f_vyper",
2834 "wasm": "_f_wasm",
2835 "wikitext": "_f_wikitext",
2836 "wit": "_f_wit",
2837 "doc": "_f_word",
2838 "docx": "_f_word",
2839 "docm": "_f_word",
2840 "dot": "_f_word",
2841 "dotx": "_f_word",
2842 "dotm": "_f_word",
2843 "wll": "_f_word",
2844 "wxml": "_f_wxml",
2845 "wxss": "_f_wxss",
2846 "xaml": "_f_xaml",
2847 "axaml": "_f_xaml",
2848 "xcodeproj": "_f_xcode",
2849 "xfl": "_f_xfl",
2850 "xib": "_f_xib",
2851 "xliff": "_f_xliff",
2852 "xlf": "_f_xliff",
2853 "pex": "_f_xml",
2854 "tmlanguage": "_f_xml",
2855 "zip": "_f_zip",
2856 "rar": "_f_zip",
2857 "7z": "_f_zip",
2858 "tar": "_f_zip",
2859 "tgz": "_f_zip",
2860 "bz": "_f_zip",
2861 "gz": "_f_zip",
2862 "bzip2": "_f_zip",
2863 "xz": "_f_zip",
2864 "bz2": "_f_zip",
2865 "whl": "_f_zip",
2866 "zipx": "_f_zip",
2867 "br": "_f_zip"
2868 },
2869 "fileNames": {
2870 "agents.md": "_f_agents",
2871 "alchemy.run.ts": "_f_alchemy",
2872 ".all-contributorsrc": "_f_allcontributors",
2873 "allurerc.js": "_f_allure",
2874 "allurerc.mjs": "_f_allure",
2875 "allurerc.cjs": "_f_allure",
2876 "allurerc.json": "_f_allure",
2877 "allurerc.yaml": "_f_allure",
2878 "allurerc.yml": "_f_allure",
2879 ".angular-cli.json": "_f_angular",
2880 "angular-cli.json": "_f_angular",
2881 "angular.json": "_f_angular",
2882 ".angular.json": "_f_angular",
2883 "antlers.html": "_f_antlers_html",
2884 "api-extractor.json": "_f_api_extractor",
2885 "api-extractor-base.json": "_f_api_extractor",
2886 ".appsemblerc.yaml": "_f_appsemble",
2887 "app-definition.yaml": "_f_appsemble",
2888 "appveyor.yml": "_f_appveyor",
2889 ".appveyor.yml": "_f_appveyor",
2890 "asconfig.json": "_f_assembly_script",
2891 "astro.config.js": "_f_astroconfig",
2892 "astro.config.cjs": "_f_astroconfig",
2893 "astro.config.mjs": "_f_astroconfig",
2894 "astro.config.ts": "_f_astroconfig",
2895 "astro.config.cts": "_f_astroconfig",
2896 "astro.config.mts": "_f_astroconfig",
2897 ".attw.json": "_f_attw",
2898 "aurelia.json": "_f_aurelia",
2899 "azure-pipelines.yml": "_f_azurepipelines",
2900 ".vsts-ci.yml": "_f_azurepipelines",
2901 ".babelrc": "_f_babel",
2902 ".babelignore": "_f_babel",
2903 ".babelrc.js": "_f_babel",
2904 ".babelrc.cjs": "_f_babel",
2905 ".babelrc.mjs": "_f_babel",
2906 ".babelrc.json": "_f_babel",
2907 "babel.config.js": "_f_babel",
2908 "babel.config.cjs": "_f_babel",
2909 "babel.config.mjs": "_f_babel",
2910 "babel.config.json": "_f_babel",
2911 ".bzrignore": "_f_bazaar",
2912 "BUILD.bazel": "_f_bazel",
2913 ".bazelrc": "_f_bazel",
2914 "bazel.rc": "_f_bazel",
2915 "bazel.bazelrc": "_f_bazel",
2916 ".bazelignore": "_f_bazel_ignore",
2917 ".bazelversion": "_f_bazel_version",
2918 "biome.json": "_f_biome",
2919 "biome.jsonc": "_f_biome",
2920 "bitbucket-pipelines.yml": "_f_bitbucketpipeline",
2921 ".bithoundrc": "_f_bithound",
2922 ".bowerrc": "_f_bower",
2923 "bower.json": "_f_bower",
2924 ".browserslistrc": "_f_browserslist",
2925 "browserslist": "_f_browserslist",
2926 ".buckconfig": "_f_buckbuild",
2927 ".bunfig.toml": "_f_bun",
2928 "bunfig.toml": "_f_bunfig",
2929 "bun.lockb": "_f_bun",
2930 "bun.lock": "_f_bun",
2931 ".bundlemonrc": "_f_bundlemon",
2932 "bundlemon.config.js": "_f_bundlemon",
2933 "bundlemon.config.mjs": "_f_bundlemon",
2934 "bundlemon.config.cjs": "_f_bundlemon",
2935 ".bundlemonrc.js": "_f_bundlemon",
2936 ".bundlemonrc.mjs": "_f_bundlemon",
2937 ".bundlemonrc.cjs": "_f_bundlemon",
2938 ".bundlemonrc.json": "_f_bundlemon",
2939 ".bundlemonrc.yaml": "_f_bundlemon",
2940 ".bundlemonrc.yml": "_f_bundlemon",
2941 "gemfile": "_f_bundler",
2942 "gemfile.lock": "_f_bundler",
2943 "capacitor.config.json": "_f_capacitor",
2944 "capacitor.config.ts": "_f_capacitor",
2945 "Cargo.toml": "_f_cargo",
2946 "Cargo.lock": "_f_cargo",
2947 ".changie.yaml": "_f_changie",
2948 ".changie.yml": "_f_changie",
2949 "chefignore": "_f_chef",
2950 "berksfile": "_f_chef",
2951 "berksfile.lock": "_f_chef",
2952 "policyfile.rb": "_f_chef",
2953 "policyfile.lock.json": "_f_chef",
2954 "circle.yml": "_f_circleci",
2955 "claude.md": "_f_claude",
2956 "wrangler.toml": "_f_cloudflare",
2957 ".cfignore": "_f_cloudfoundry",
2958 ".codacy.yml": "_f_codacy",
2959 ".codacy.yaml": "_f_codacy",
2960 ".codeclimate.yml": "_f_codeclimate",
2961 "codecov.yml": "_f_codecov",
2962 ".codecov.yml": "_f_codecov",
2963 "config.codekit": "_f_codekit",
2964 "config.codekit2": "_f_codekit",
2965 "config.codekit3": "_f_codekit",
2966 ".config.codekit": "_f_codekit",
2967 ".config.codekit2": "_f_codekit",
2968 ".config.codekit3": "_f_codekit",
2969 "codemagic.yaml": "_f_codemagic",
2970 "codeowners": "_f_codeowners",
2971 ".coderabbit.yaml": "_f_coderabbit",
2972 "coffeelint.json": "_f_coffeelint",
2973 ".coffeelintignore": "_f_coffeelint",
2974 ".czrc": "_f_commitizen",
2975 ".cz.json": "_f_commitizen",
2976 ".commitlintrc": "_f_commitlint",
2977 "commitlint.config.js": "_f_commitlint",
2978 "commitlint.config.cjs": "_f_commitlint",
2979 "commitlint.config.mjs": "_f_commitlint",
2980 "commitlint.config.ts": "_f_commitlint",
2981 "commitlint.config.cts": "_f_commitlint",
2982 ".commitlintrc.json": "_f_commitlint",
2983 ".commitlintrc.yaml": "_f_commitlint",
2984 ".commitlintrc.yml": "_f_commitlint",
2985 ".commitlintrc.js": "_f_commitlint",
2986 ".commitlintrc.cjs": "_f_commitlint",
2987 ".commitlintrc.mjs": "_f_commitlint",
2988 ".commitlintrc.ts": "_f_commitlint",
2989 ".commitlintrc.cts": "_f_commitlint",
2990 "composer.json": "_f_composer",
2991 "composer.lock": "_f_composer",
2992 "conanfile.txt": "_f_conan",
2993 "conanfile.py": "_f_conan",
2994 ".condarc": "_f_conda",
2995 ".tool-versions": "_f_config",
2996 "copilot-instructions.md": "_f_copilot",
2997 "github-copilot.xml": "_f_copilot",
2998 ".coverage": "_f_coverage",
2999 "lcov.info": "_f_coverage",
3000 ".coveralls.yml": "_f_coveralls",
3001 "craco.config.ts": "_f_craco",
3002 "craco.config.js": "_f_craco",
3003 "craco.config.cjs": "_f_craco",
3004 ".cracorc.ts": "_f_craco",
3005 ".cracorc.js": "_f_craco",
3006 ".cracorc": "_f_craco",
3007 "crowdin.yml": "_f_crowdin",
3008 "cspell.config.json": "_f_cspell",
3009 "cspell.config.js": "_f_cspell",
3010 "cspell.config.mjs": "_f_cspell",
3011 "cspell.config.cjs": "_f_cspell",
3012 "cspell.config.yaml": "_f_cspell",
3013 "cspell.config.yml": "_f_cspell",
3014 "cspell.json": "_f_cspell",
3015 "cspell.js": "_f_cspell",
3016 "cspell.mjs": "_f_cspell",
3017 "cspell.cjs": "_f_cspell",
3018 "cspell.yaml": "_f_cspell",
3019 "cspell.yml": "_f_cspell",
3020 ".cspell.json": "_f_cspell",
3021 ".cspell.js": "_f_cspell",
3022 ".cspell.mjs": "_f_cspell",
3023 ".cspell.cjs": "_f_cspell",
3024 ".cspell.yaml": "_f_cspell",
3025 ".cspell.yml": "_f_cspell",
3026 ".csscomb.json": "_f_csscomb",
3027 ".csslintrc": "_f_csslint",
3028 ".cursorignore": "_f_cursorrules",
3029 ".cursorindexingignore": "_f_cursorrules",
3030 ".cursorrules": "_f_cursorrules",
3031 ".cvsignore": "_f_cvs",
3032 "cypress.json": "_f_cypress",
3033 "cypress.env.json": "_f_cypress",
3034 "cypress.config.js": "_f_cypress",
3035 "cypress.config.ts": "_f_cypress",
3036 "cypress.config.cjs": "_f_cypress",
3037 "cypress.config.mjs": "_f_cypress",
3038 ".boringignore": "_f_darcs",
3039 ".pubignore": "_f_dartlang_ignore",
3040 "service.datadog.yaml": "_f_datadog",
3041 "datadog-ci.json": "_f_datadog",
3042 "static-analysis.datadog.yml": "_f_datadog",
3043 "static-analysis.datadog.yaml": "_f_datadog",
3044 "aptfile": "_f_debian",
3045 "deno.json": "_f_deno",
3046 "deno.jsonc": "_f_deno",
3047 "deno.lock": "_f_deno",
3048 ".denoifyrc": "_f_denoify",
3049 "denoify.config.json": "_f_denoify",
3050 "denoify.config.js": "_f_denoify",
3051 "dependabot.yml": "_f_dependabot",
3052 "dependabot.yaml": "_f_dependabot",
3053 "dependencies.yml": "_f_dependencies",
3054 "devcontainer.json": "_f_devcontainer",
3055 ".devcontainer.json": "_f_devcontainer",
3056 "devvit.json": "_f_devvit",
3057 "devvit.dev.json": "_f_devvit",
3058 "devvit.prod.json": "_f_devvit",
3059 "devvit.test.json": "_f_devvit",
3060 "devvit.config.js": "_f_devvit",
3061 "devvit.config.cjs": "_f_devvit",
3062 "devvit.config.mjs": "_f_devvit",
3063 "devvit.config.ts": "_f_devvit",
3064 "devvit.config.mts": "_f_devvit",
3065 "devvit.config.dev.js": "_f_devvit",
3066 "devvit.config.dev.cjs": "_f_devvit",
3067 "devvit.config.dev.mjs": "_f_devvit",
3068 "devvit.config.dev.ts": "_f_devvit",
3069 "devvit.config.dev.mts": "_f_devvit",
3070 "devvit.config.prod.js": "_f_devvit",
3071 "devvit.config.prod.cjs": "_f_devvit",
3072 "devvit.config.prod.mjs": "_f_devvit",
3073 "devvit.config.prod.ts": "_f_devvit",
3074 "devvit.config.prod.mts": "_f_devvit",
3075 "devvit.config.test.js": "_f_devvit",
3076 "devvit.config.test.cjs": "_f_devvit",
3077 "devvit.config.test.mjs": "_f_devvit",
3078 "devvit.config.test.ts": "_f_devvit",
3079 "devvit.config.test.mts": "_f_devvit",
3080 ".dockerignore": "_f_docker",
3081 "compose.yaml": "_f_docker",
3082 "compose.yml": "_f_docker",
3083 "docker-compose.yaml": "_f_docker",
3084 "docker-compose.yml": "_f_docker",
3085 "docker-compose.debug.yaml": "_f_docker",
3086 "docker-compose.debug.yml": "_f_docker",
3087 "docker-compose.ci-build.yaml": "_f_docker",
3088 "docker-compose.ci-build.yml": "_f_docker",
3089 "docker-compose.override.yaml": "_f_docker",
3090 "docker-compose.override.yml": "_f_docker",
3091 "docker-compose.vs.debug.yaml": "_f_docker",
3092 "docker-compose.vs.debug.yml": "_f_docker",
3093 "docker-compose.vs.release.yaml": "_f_docker",
3094 "docker-compose.vs.release.yml": "_f_docker",
3095 "docker-cloud.yaml": "_f_docker",
3096 "docker-cloud.yml": "_f_docker",
3097 "docker-compose.test.yml": "_f_dockertest",
3098 "docusaurus.config.ts": "_f_docusaurus",
3099 "docusaurus.config.mts": "_f_docusaurus",
3100 "docusaurus.config.cts": "_f_docusaurus",
3101 "docusaurus.config.js": "_f_docusaurus",
3102 "docusaurus.config.mjs": "_f_docusaurus",
3103 "docusaurus.config.cjs": "_f_docusaurus",
3104 ".doczrc": "_f_docz",
3105 "docz.js": "_f_docz",
3106 "docz.json": "_f_docz",
3107 ".docz.js": "_f_docz",
3108 ".docz.json": "_f_docz",
3109 "doczrc.js": "_f_docz",
3110 "doczrc.json": "_f_docz",
3111 "docz.config.js": "_f_docz",
3112 "docz.config.json": "_f_docz",
3113 ".dojorc": "_f_dojo",
3114 "doppler.yaml": "_f_doppler",
3115 "doppler-template.yaml": "_f_doppler",
3116 ".drone.yml": "_f_drone",
3117 ".drone.yml.sig": "_f_drone",
3118 "dune": "_f_dune",
3119 "dune-project": "_f_dune",
3120 "dune-workspace": "_f_dune",
3121 ".dvc": "_f_dvc",
3122 ".earthlyignore": "_f_earthly",
3123 "Earthfile": "_f_earthly",
3124 "store.config.json": "_f_eas-metadata",
3125 ".editorconfig": "_f_editorconfig",
3126 "forge.config.js": "_f_electron",
3127 "forge.config.ts": "_f_electron",
3128 ".eleventy.js": "_f_eleventy",
3129 "eleventy.config.js": "_f_eleventy",
3130 "eleventy.config.cjs": "_f_eleventy",
3131 "eleventy.config.mjs": "_f_eleventy",
3132 "elm-package.json": "_f_elm",
3133 ".ember-cli": "_f_ember",
3134 "emakefile": "_f_erlang",
3135 ".emakerfile": "_f_erlang",
3136 "esbuild.js": "_f_esbuild",
3137 "esbuild.mjs": "_f_esbuild",
3138 "esbuild.cjs": "_f_esbuild",
3139 "esbuild.ts": "_f_esbuild",
3140 "esbuild.config.js": "_f_esbuild",
3141 "esbuild.config.mjs": "_f_esbuild",
3142 "esbuild.config.cjs": "_f_esbuild",
3143 "esbuild.config.ts": "_f_esbuild",
3144 ".eslintrc": "_f_eslint",
3145 ".eslintignore": "_f_eslint",
3146 ".eslintcache": "_f_eslint",
3147 "eslint.config.js": "_f_eslint",
3148 "eslint.config.mjs": "_f_eslint",
3149 "eslint.config.cjs": "_f_eslint",
3150 "eslint.config.ts": "_f_eslint",
3151 "eslint.config.mts": "_f_eslint",
3152 "eslint.config.cts": "_f_eslint",
3153 ".eslintrc.js": "_f_eslint",
3154 ".eslintrc.mjs": "_f_eslint",
3155 ".eslintrc.cjs": "_f_eslint",
3156 ".eslintrc.ts": "_f_eslint",
3157 ".eslintrc.mts": "_f_eslint",
3158 ".eslintrc.cts": "_f_eslint",
3159 ".eslintrc.json": "_f_eslint",
3160 ".eslintrc.yaml": "_f_eslint",
3161 ".eslintrc.yml": "_f_eslint",
3162 "app.json": "_f_expo",
3163 "app.config.js": "_f_expo",
3164 "app.config.json": "_f_expo",
3165 "app.config.json5": "_f_expo",
3166 ".fantasticonrc": "_f_fantasticon",
3167 "fantasticonrc": "_f_fantasticon",
3168 ".fantasticonrc.json": "_f_fantasticon",
3169 "fantasticonrc.json": "_f_fantasticon",
3170 ".fantasticonrc.js": "_f_fantasticon",
3171 "fantasticonrc.js": "_f_fantasticon",
3172 "fastly.toml": "_f_fastly",
3173 ".faunarc": "_f_fauna",
3174 "favicon.ico": "_f_favicon",
3175 ".firebaserc": "_f_firebase",
3176 "firebase.json": "_f_firebasehosting",
3177 "storage.rules": "_f_firebasestorage",
3178 "firestore.rules": "_f_firestore",
3179 "firestore.indexes.json": "_f_firestore",
3180 "flareact.config.js": "_f_flareact",
3181 ".flooignore": "_f_floobits",
3182 ".flowconfig": "_f_flow",
3183 ".flutter-plugins": "_f_flutter",
3184 ".metadata": "_f_flutter",
3185 ".flutter-plugins-dependencies": "_f_flutter",
3186 "pubspec.lock": "_f_flutter_package",
3187 "pubspec.yaml": "_f_flutter_package",
3188 ".packages": "_f_flutter_package",
3189 "fly.toml": "_f_flyio",
3190 "formkit.config.js": "_f_formkit",
3191 "formkit.config.mjs": "_f_formkit",
3192 "formkit.config.cjs": "_f_formkit",
3193 "formkit.config.ts": "_f_formkit",
3194 ".fossaignore": "_f_fossa",
3195 "ignore-glob": "_f_fossil",
3196 ".front-commerce.js": "_f_frontcommerce",
3197 "front-commerce.config.ts": "_f_frontcommerce",
3198 "funding.yml": "_f_funding",
3199 "fuse.js": "_f_fusebox",
3200 "gatsby-browser.js": "_f_gatsby",
3201 "gatsby-browser.ts": "_f_gatsby",
3202 "gatsby-browser.tsx": "_f_gatsby",
3203 "gatsby-ssr.js": "_f_gatsby",
3204 "gatsby-ssr.ts": "_f_gatsby",
3205 "gatsby-ssr.tsx": "_f_gatsby",
3206 "gatsby-config.js": "_f_gatsby",
3207 "gatsby-config.ts": "_f_gatsby",
3208 "gatsby-config.mjs": "_f_gatsby",
3209 "gatsby-node.js": "_f_gatsby",
3210 "gatsby-node.ts": "_f_gatsby",
3211 "gatsby-node.mjs": "_f_gatsby",
3212 ".gcloudignore": "_f_gcloud",
3213 "gemini.md": "_f_gemini",
3214 "GEMINI.md": "_f_gemini",
3215 ".git": "_f_git",
3216 ".gitattributes": "_f_git",
3217 ".gitconfig": "_f_git",
3218 ".gitignore": "_f_git",
3219 ".gitmodules": "_f_git",
3220 ".gitkeep": "_f_git",
3221 ".git-blame-ignore-revs": "_f_git",
3222 ".mailmap": "_f_git",
3223 ".issuetracker": "_f_git",
3224 ".gitlab-ci.yml": "_f_gitlab",
3225 ".gitpod.yaml": "_f_gitpod",
3226 ".gitpod.yml": "_f_gitpod",
3227 "gitpod.yaml": "_f_gitpod",
3228 "gitpod.yml": "_f_gitpod",
3229 "gleam.toml": "_f_gleamconfig",
3230 "glide.yml": "_f_glide",
3231 ".glitterrc": "_f_glitter",
3232 "go.sum": "_f_go_package",
3233 "go.mod": "_f_go_package",
3234 "go.work": "_f_go_work",
3235 "go.work.sum": "_f_go_work",
3236 ".gqlconfig": "_f_graphql",
3237 ".graphqlconfig": "_f_graphql_config",
3238 ".graphqlconfig.yml": "_f_graphql_config",
3239 ".graphqlconfig.yaml": "_f_graphql_config",
3240 "greenkeeper.json": "_f_greenkeeper",
3241 "gridsome.config.js": "_f_gridsome",
3242 "gridsome.config.ts": "_f_gridsome",
3243 "gridsome.server.js": "_f_gridsome",
3244 "gridsome.server.ts": "_f_gridsome",
3245 "gridsome.client.js": "_f_gridsome",
3246 "gridsome.client.ts": "_f_gridsome",
3247 "gruntfile.js": "_f_grunt",
3248 "gruntfile.cjs": "_f_grunt",
3249 "gruntfile.coffee": "_f_grunt",
3250 "gruntfile.ts": "_f_grunt",
3251 "gruntfile.babel.js": "_f_grunt",
3252 "gruntfile.babel.cjs": "_f_grunt",
3253 "gruntfile.babel.coffee": "_f_grunt",
3254 "gruntfile.babel.ts": "_f_grunt",
3255 "gulpfile.js": "_f_gulp",
3256 "gulpfile.coffee": "_f_gulp",
3257 "gulpfile.ts": "_f_gulp",
3258 "gulpfile.mjs": "_f_gulp",
3259 "gulpfile.esm.js": "_f_gulp",
3260 "gulpfile.esm.coffee": "_f_gulp",
3261 "gulpfile.esm.ts": "_f_gulp",
3262 "gulpfile.esm.mjs": "_f_gulp",
3263 "gulpfile.babel.js": "_f_gulp",
3264 "gulpfile.babel.coffee": "_f_gulp",
3265 "gulpfile.babel.ts": "_f_gulp",
3266 "gulpfile.babel.mjs": "_f_gulp",
3267 "hardhat.config.cjs": "_f_hardhat",
3268 "hardhat.config.js": "_f_hardhat",
3269 "hardhat.config.ts": "_f_hardhat",
3270 "haxelib.json": "_f_haxe",
3271 "checkstyle.json": "_f_haxecheckstyle",
3272 ".p4ignore": "_f_helix",
3273 "chart.lock": "_f_helm",
3274 "chart.yaml": "_f_helm",
3275 "histoire.config.ts": "_f_histoire",
3276 "histoire.config.js": "_f_histoire",
3277 ".histoire.ts": "_f_histoire",
3278 ".histoire.js": "_f_histoire",
3279 "horusec-config.json": "_f_horusec",
3280 ".htmlhintrc": "_f_htmlhint",
3281 ".htmlvalidateignore": "_f_htmlvalidate",
3282 ".htmlvalidate.js": "_f_htmlvalidate",
3283 ".htmlvalidate.cjs": "_f_htmlvalidate",
3284 ".htmlvalidate.json": "_f_htmlvalidate",
3285 "hugo.json": "_f_hugo",
3286 "hugo.toml": "_f_hugo",
3287 "hugo.yaml": "_f_hugo",
3288 ".hugo_build.lock": "_f_hugo",
3289 "humans.txt": "_f_humanstxt",
3290 ".huskyrc": "_f_husky",
3291 "husky.config.js": "_f_husky",
3292 ".huskyrc.js": "_f_husky",
3293 ".huskyrc.json": "_f_husky",
3294 ".huskyrc.yaml": "_f_husky",
3295 ".huskyrc.yml": "_f_husky",
3296 "ionic.project": "_f_ionic",
3297 "ionic.config.json": "_f_ionic",
3298 "jakefile": "_f_jake",
3299 "jakefile.js": "_f_jake",
3300 "jasmine.json": "_f_jasmine",
3301 ".nojekyll": "_f_jekyll",
3302 "jest.json": "_f_jest",
3303 "jest.config.babel.js": "_f_jest",
3304 ".jestrc": "_f_jest",
3305 ".jestrc.js": "_f_jest",
3306 ".jestrc.json": "_f_jest",
3307 "jest.config.js": "_f_jest",
3308 "jest.config.cjs": "_f_jest",
3309 "jest.config.mjs": "_f_jest",
3310 "jest.config.json": "_f_jest",
3311 "jest.config.ts": "_f_jest",
3312 "jest.config.base.js": "_f_jest",
3313 "jest.config.base.cjs": "_f_jest",
3314 "jest.config.base.mjs": "_f_jest",
3315 "jest.config.base.json": "_f_jest",
3316 "jest.config.base.ts": "_f_jest",
3317 "jest.config.common.js": "_f_jest",
3318 "jest.config.common.cjs": "_f_jest",
3319 "jest.config.common.mjs": "_f_jest",
3320 "jest.config.common.json": "_f_jest",
3321 "jest.config.common.ts": "_f_jest",
3322 "jest.config.e2e.js": "_f_jest",
3323 "jest.config.e2e.cjs": "_f_jest",
3324 "jest.config.e2e.mjs": "_f_jest",
3325 "jest.config.e2e.json": "_f_jest",
3326 "jest.config.e2e.ts": "_f_jest",
3327 "jest.config.int.js": "_f_jest",
3328 "jest.config.int.cjs": "_f_jest",
3329 "jest.config.int.mjs": "_f_jest",
3330 "jest.config.int.json": "_f_jest",
3331 "jest.config.int.ts": "_f_jest",
3332 "jest.config.integration.js": "_f_jest",
3333 "jest.config.integration.cjs": "_f_jest",
3334 "jest.config.integration.mjs": "_f_jest",
3335 "jest.config.integration.json": "_f_jest",
3336 "jest.config.integration.ts": "_f_jest",
3337 "jest.config.unit.js": "_f_jest",
3338 "jest.config.unit.cjs": "_f_jest",
3339 "jest.config.unit.mjs": "_f_jest",
3340 "jest.config.unit.json": "_f_jest",
3341 "jest.config.unit.ts": "_f_jest",
3342 ".jpmignore": "_f_jpm",
3343 ".jsbeautifyrc": "_f_jsbeautify",
3344 "jsbeautifyrc": "_f_jsbeautify",
3345 ".jsbeautify": "_f_jsbeautify",
3346 "jsbeautify": "_f_jsbeautify",
3347 "jsconfig.json": "_f_jsconfig",
3348 ".jscpd.json": "_f_jscpd",
3349 "jscpd-report.xml": "_f_jscpd",
3350 "jscpd-report.json": "_f_jscpd",
3351 "jscpd-report.html": "_f_jscpd",
3352 ".jshintrc": "_f_jshint",
3353 ".jshintignore": "_f_jshint",
3354 "jsr.json": "_f_jsr",
3355 "karma.conf.js": "_f_karma",
3356 "karma.conf.coffee": "_f_karma",
3357 "karma.conf.ts": "_f_karma",
3358 ".kitchen.yml": "_f_kitchenci",
3359 "kitchen.yml": "_f_kitchenci",
3360 ".kiteignore": "_f_kite",
3361 "knip.json": "_f_knip",
3362 "knip.jsonc": "_f_knip",
3363 ".knip.json": "_f_knip",
3364 ".knip.jsonc": "_f_knip",
3365 "knip.ts": "_f_knip",
3366 "knip.js": "_f_knip",
3367 "knip.config.ts": "_f_knip",
3368 "knip.config.js": "_f_knip",
3369 "language-configuration.json": "_f_language_configuration",
3370 "layout.html": "_f_layout",
3371 "layout.htm": "_f_layout",
3372 "lakefile.lean": "_f_leanconfig",
3373 "lakefile.toml": "_f_leanconfig",
3374 "lake-manifest.json": "_f_leanconfig",
3375 "lean-toolchain": "_f_leanconfig",
3376 "lefthook.json": "_f_lefthook",
3377 "lefthook.toml": "_f_lefthook",
3378 "lefthook.yaml": "_f_lefthook",
3379 "lefthook.yml": "_f_lefthook",
3380 "lefthook-local.json": "_f_lefthook",
3381 "lefthook-local.toml": "_f_lefthook",
3382 "lefthook-local.yaml": "_f_lefthook",
3383 "lefthook-local.yml": "_f_lefthook",
3384 ".lefthook.json": "_f_lefthook",
3385 ".lefthook.toml": "_f_lefthook",
3386 ".lefthook.yaml": "_f_lefthook",
3387 ".lefthook.yml": "_f_lefthook",
3388 ".lefthook-local.json": "_f_lefthook",
3389 ".lefthook-local.toml": "_f_lefthook",
3390 ".lefthook-local.yaml": "_f_lefthook",
3391 ".lefthook-local.yml": "_f_lefthook",
3392 "lerna.json": "_f_lerna",
3393 "liara.json": "_f_liara",
3394 ".liaraignore": "_f_liara",
3395 "licence": "_f_license",
3396 "LICENCE": "_f_license",
3397 "license": "_f_license",
3398 "LICENSE": "_f_license",
3399 "copying": "_f_license",
3400 "copying.lesser": "_f_license",
3401 "LICENSE-MIT": "_f_license",
3402 "license-mit": "_f_license",
3403 "LICENSE-APACHE": "_f_license",
3404 "license-apache": "_f_license",
3405 "LICENSE-GPL": "_f_license",
3406 "license-gpl": "_f_license",
3407 "LICENSE-AGPL": "_f_license",
3408 "license-agpl": "_f_license",
3409 "LICENCE.md": "_f_license",
3410 "LICENCE.txt": "_f_license",
3411 "licence.md": "_f_license",
3412 "licence.txt": "_f_license",
3413 "LICENSE.md": "_f_license",
3414 "LICENSE.txt": "_f_license",
3415 "license.md": "_f_license",
3416 "license.txt": "_f_license",
3417 "copying.md": "_f_license",
3418 "copying.txt": "_f_license",
3419 "copying.lesser.md": "_f_license",
3420 "copying.lesser.txt": "_f_license",
3421 "LICENSE-MIT.md": "_f_license",
3422 "LICENSE-MIT.txt": "_f_license",
3423 "license-mit.md": "_f_license",
3424 "license-mit.txt": "_f_license",
3425 "LICENSE-APACHE.md": "_f_license",
3426 "LICENSE-APACHE.txt": "_f_license",
3427 "license-apache.md": "_f_license",
3428 "license-apache.txt": "_f_license",
3429 "LICENSE-GPL.md": "_f_license",
3430 "LICENSE-GPL.txt": "_f_license",
3431 "license-gpl.md": "_f_license",
3432 "license-gpl.txt": "_f_license",
3433 "LICENSE-AGPL.md": "_f_license",
3434 "LICENSE-AGPL.txt": "_f_license",
3435 "license-agpl.md": "_f_license",
3436 "license-agpl.txt": "_f_license",
3437 ".licrc": "_f_licensebat",
3438 "budget.json": "_f_lighthouse",
3439 "budgets.json": "_f_lighthouse",
3440 ".lighthouserc.cjs": "_f_lighthouse",
3441 ".lighthouserc.js": "_f_lighthouse",
3442 ".lighthouserc.json": "_f_lighthouse",
3443 ".lighthouserc.yaml": "_f_lighthouse",
3444 ".lighthouserc.yml": "_f_lighthouse",
3445 "include.xml": "_f_lime",
3446 ".lintstagedrc": "_f_lintstagedrc",
3447 ".lintstagedrc.json": "_f_lintstagedrc",
3448 ".lintstagedrc.yaml": "_f_lintstagedrc",
3449 ".lintstagedrc.yml": "_f_lintstagedrc",
3450 ".lintstagedrc.mjs": "_f_lintstagedrc",
3451 ".lintstagedrc.mts": "_f_lintstagedrc",
3452 ".lintstagedrc.js": "_f_lintstagedrc",
3453 ".lintstagedrc.ts": "_f_lintstagedrc",
3454 ".lintstagedrc.cjs": "_f_lintstagedrc",
3455 ".lintstagedrc.cts": "_f_lintstagedrc",
3456 "lint-staged.config.mjs": "_f_lintstagedrc",
3457 "lint-staged.config.mts": "_f_lintstagedrc",
3458 "lint-staged.config.js": "_f_lintstagedrc",
3459 "lint-staged.config.ts": "_f_lintstagedrc",
3460 "lint-staged.config.cjs": "_f_lintstagedrc",
3461 "lint-staged.config.cts": "_f_lintstagedrc",
3462 "mailing.config.json": "_f_mailing",
3463 "manifest": "_f_manifest",
3464 "manifest.bak": "_f_manifest_bak",
3465 "manifest.skip": "_f_manifest_skip",
3466 ".markdownlintrc": "_f_markdownlint",
3467 ".markdownlint.json": "_f_markdownlint",
3468 ".markdownlint.jsonc": "_f_markdownlint",
3469 ".markdownlint.yml": "_f_markdownlint",
3470 ".markdownlint.yaml": "_f_markdownlint",
3471 ".markdownlint.cjs": "_f_markdownlint",
3472 ".markdownlint-cli2.json": "_f_markdownlint",
3473 ".markdownlint-cli2.jsonc": "_f_markdownlint",
3474 ".markdownlint-cli2.yml": "_f_markdownlint",
3475 ".markdownlint-cli2.yaml": "_f_markdownlint",
3476 ".markdownlint-cli2.cjs": "_f_markdownlint",
3477 ".markdownlintignore": "_f_markdownlint_ignore",
3478 ".markuplintrc": "_f_markuplint",
3479 ".markuplintrc.json": "_f_markuplint",
3480 ".markuplintrc.yaml": "_f_markuplint",
3481 ".markuplintrc.yml": "_f_markuplint",
3482 ".markuplintrc.js": "_f_markuplint",
3483 ".markuplintrc.cjs": "_f_markuplint",
3484 ".markuplintrc.mjs": "_f_markuplint",
3485 ".markuplintrc.ts": "_f_markuplint",
3486 "markuplint.config.json": "_f_markuplint",
3487 "markuplint.config.yaml": "_f_markuplint",
3488 "markuplint.config.yml": "_f_markuplint",
3489 "markuplint.config.js": "_f_markuplint",
3490 "markuplint.config.cjs": "_f_markuplint",
3491 "markuplint.config.mjs": "_f_markuplint",
3492 "markuplint.config.ts": "_f_markuplint",
3493 "master.js": "_f_master-co",
3494 "master.ts": "_f_master-co",
3495 "master.mjs": "_f_master-co",
3496 "master.json": "_f_master-co",
3497 "master.css.js": "_f_master-co",
3498 "master.css.ts": "_f_master-co",
3499 "master.css.mjs": "_f_master-co",
3500 "master.css.json": "_f_master-co",
3501 "maven.config": "_f_maven",
3502 "pom.xml": "_f_maven",
3503 "extensions.xml": "_f_maven",
3504 "settings.xml": "_f_maven",
3505 "mcp.json": "_f_mcp",
3506 "mdx-components.cjs": "_f_mdx-components",
3507 "mdx-components.cts": "_f_mdx-components",
3508 "mdx-components.js": "_f_mdx-components",
3509 "mdx-components.jsx": "_f_mdx-components",
3510 "mdx-components.mjs": "_f_mdx-components",
3511 "mdx-components.mts": "_f_mdx-components",
3512 "mdx-components.ts": "_f_mdx-components",
3513 "mdx-components.tsx": "_f_mdx-components",
3514 ".mdxlintrc": "_f_mdxlint",
3515 ".mdxlintignore": "_f_mdxlint",
3516 ".mdxlintrc.cjs": "_f_mdxlint",
3517 ".mdxlintrc.js": "_f_mdxlint",
3518 ".mdxlintrc.json": "_f_mdxlint",
3519 ".mdxlintrc.mjs": "_f_mdxlint",
3520 ".mdxlintrc.yml": "_f_mdxlint",
3521 ".mdxlintrc.yaml": "_f_mdxlint",
3522 ".hgignore": "_f_mercurial",
3523 "mocha.opts": "_f_mocha",
3524 ".mocharc.cjs": "_f_mocha",
3525 ".mocharc.js": "_f_mocha",
3526 ".mocharc.json": "_f_mocha",
3527 ".mocharc.jsonc": "_f_mocha",
3528 ".mocharc.mjs": "_f_mocha",
3529 ".mocharc.yaml": "_f_mocha",
3530 ".mocharc.yml": "_f_mocha",
3531 "modernizr": "_f_modernizr",
3532 "modernizr.js": "_f_modernizr",
3533 "modernizrrc.js": "_f_modernizr",
3534 ".modernizr.js": "_f_modernizr",
3535 ".modernizrrc.js": "_f_modernizr",
3536 "moleculer.config.js": "_f_moleculer",
3537 "moleculer.config.json": "_f_moleculer",
3538 "moleculer.config.ts": "_f_moleculer",
3539 ".mtn-ignore": "_f_monotone",
3540 "motif.json": "_f_motif",
3541 "mypy.ini": "_f_mypy",
3542 ".mypy.ini": "_f_mypy",
3543 ".nano-staged.js": "_f_nanostaged",
3544 "nano-staged.js": "_f_nanostaged",
3545 ".nano-staged.cjs": "_f_nanostaged",
3546 "nano-staged.cjs": "_f_nanostaged",
3547 ".nano-staged.mjs": "_f_nanostaged",
3548 "nano-staged.mjs": "_f_nanostaged",
3549 ".nano-staged.json": "_f_nanostaged",
3550 "nano-staged.json": "_f_nanostaged",
3551 ".nanostagedrc": "_f_nanostaged",
3552 ".nest-cli.json": "_f_nestjs",
3553 "nest-cli.json": "_f_nestjs",
3554 "nestconfig.json": "_f_nestjs",
3555 ".nestconfig.json": "_f_nestjs",
3556 "netlify.toml": "_f_netlify",
3557 "next.config.js": "_f_next",
3558 "next.config.cjs": "_f_next",
3559 "next.config.mjs": "_f_next",
3560 "next.config.ts": "_f_next",
3561 ".nf": "_f_nextflow",
3562 "ng-tailwind.js": "_f_ng_tailwind",
3563 "nginx.conf": "_f_nginx",
3564 "build.ninja": "_f_ninja",
3565 "flake.lock": "_f_nix",
3566 ".node-version": "_f_node",
3567 ".nvmrc": "_f_node",
3568 "node.config.json": "_f_node",
3569 "nodemon.json": "_f_nodemon",
3570 ".npmignore": "_f_npm",
3571 ".npmrc": "_f_npm",
3572 ".package-lock.json": "_f_npm",
3573 "package.json": "_f_npm",
3574 "package-lock.json": "_f_npm",
3575 "npm-shrinkwrap.json": "_f_npm",
3576 ".npmpackagejsonlintrc": "_f_npmpackagejsonlint",
3577 ".npmpackagejsonlintrc.cjs": "_f_npmpackagejsonlint",
3578 ".npmpackagejsonlintrc.js": "_f_npmpackagejsonlint",
3579 ".npmpackagejsonlintrc.json": "_f_npmpackagejsonlint",
3580 ".npmpackagejsonlintrc.yaml": "_f_npmpackagejsonlint",
3581 ".npmpackagejsonlintrc.yml": "_f_npmpackagejsonlint",
3582 ".npmpackagejsonlintrc.mjs": "_f_npmpackagejsonlint",
3583 ".npmpackagejsonlintignore": "_f_npmpackagejsonlint",
3584 "npmpackagejsonlint.config.cjs": "_f_npmpackagejsonlint",
3585 "npmpackagejsonlint.config.js": "_f_npmpackagejsonlint",
3586 "npmpackagejsonlint.config.mjs": "_f_npmpackagejsonlint",
3587 ".nsrirc": "_f_nsri",
3588 ".nsriignore": "_f_nsri",
3589 "nsri.config.js": "_f_nsri",
3590 ".nsrirc.js": "_f_nsri",
3591 ".nsrirc.json": "_f_nsri",
3592 ".nsrirc.yaml": "_f_nsri",
3593 ".nsrirc.yml": "_f_nsri",
3594 ".integrity.json": "_f_nsri-integrity",
3595 ".nuxtignore": "_f_nuxt",
3596 ".nuxtrc": "_f_nuxt",
3597 "nuxt.config.js": "_f_nuxt",
3598 "nuxt.config.ts": "_f_nuxt",
3599 "nuxt.config.mjs": "_f_nuxt",
3600 "nuxt.js": "_f_nuxt",
3601 "nuxt.ts": "_f_nuxt",
3602 "nuxt.mjs": "_f_nuxt",
3603 "nx.json": "_f_nx",
3604 ".nycrc": "_f_nyc",
3605 ".nycrc.json": "_f_nyc",
3606 ".objidconfig": "_f_objidconfig",
3607 ".merlin": "_f_ocaml",
3608 ".oxlintignore": "_f_oxc",
3609 ".oxlintrc.json": "_f_oxc",
3610 ".oxlintrc.jsonc": "_f_oxc",
3611 ".oxfmtrc.json": "_f_oxc",
3612 ".oxfmtrc.jsonc": "_f_oxc",
3613 ".packshiprc": "_f_packship",
3614 ".packshiprc.json": "_f_packship",
3615 ".packshiprc.js": "_f_packship",
3616 ".packshiprc.ts": "_f_packship",
3617 "packship.config.js": "_f_packship",
3618 "packship.config.ts": "_f_packship",
3619 "packship.config.mjs": "_f_packship",
3620 "packship.config.mts": "_f_packship",
3621 "packship.config.json": "_f_packship",
3622 "paket.dependencies": "_f_paket",
3623 "paket.lock": "_f_paket",
3624 "paket.references": "_f_paket",
3625 "paket.template": "_f_paket",
3626 "paket.local": "_f_paket",
3627 "panda.config.js": "_f_pandacss",
3628 "panda.config.mjs": "_f_pandacss",
3629 "panda.config.ts": "_f_pandacss",
3630 ".pdm-python": "_f_pdm",
3631 "pdm.lock": "_f_pdm",
3632 "pdm.toml": "_f_pdm",
3633 "peeky.config.ts": "_f_peeky",
3634 "peeky.config.js": "_f_peeky",
3635 "peeky.config.mjs": "_f_peeky",
3636 ".php-cs-fixer.php": "_f_phpcsfixer",
3637 ".php-cs-fixer.dist.php": "_f_phpcsfixer",
3638 ".php_cs": "_f_phpcsfixer",
3639 ".php_cs.dist": "_f_phpcsfixer",
3640 "phpstan.neon": "_f_phpstan",
3641 "phpstan.dist.neon": "_f_phpstan",
3642 "phpunit": "_f_phpunit",
3643 "phpunit.xml": "_f_phpunit",
3644 "phpunit.xml.dist": "_f_phpunit",
3645 ".phraseapp.yml": "_f_phraseapp",
3646 "pipfile": "_f_pip",
3647 "pipfile.lock": "_f_pip",
3648 "platformio.ini": "_f_platformio",
3649 "playwright.config.js": "_f_playwright",
3650 "playwright.config.ts": "_f_playwright",
3651 "playwright.config.mts": "_f_playwright",
3652 "playwright.config.mjs": "_f_playwright",
3653 "playwright.config.cts": "_f_playwright",
3654 "playwright.config.cjs": "_f_playwright",
3655 "ecosystem.config.js": "_f_pm2",
3656 "ecosystem.config.cjs": "_f_pm2",
3657 "ecosystem.config.json": "_f_pm2",
3658 "ecosystem.config.yaml": "_f_pm2",
3659 "ecosystem.config.yml": "_f_pm2",
3660 ".pnpmfile.cjs": "_f_pnpm",
3661 "pnpmfile.js": "_f_pnpm",
3662 "pnpm-lock.yaml": "_f_pnpm",
3663 "pnpm-workspace.yaml": "_f_pnpm",
3664 "poetry.lock": "_f_poetry",
3665 ".postcssrc": "_f_postcssconfig",
3666 ".postcssrc.json": "_f_postcssconfig",
3667 ".postcssrc.yaml": "_f_postcssconfig",
3668 ".postcssrc.yml": "_f_postcssconfig",
3669 ".postcssrc.ts": "_f_postcssconfig",
3670 ".postcssrc.cts": "_f_postcssconfig",
3671 ".postcssrc.mts": "_f_postcssconfig",
3672 ".postcssrc.js": "_f_postcssconfig",
3673 ".postcssrc.cjs": "_f_postcssconfig",
3674 ".postcssrc.mjs": "_f_postcssconfig",
3675 "postcss.config.ts": "_f_postcssconfig",
3676 "postcss.config.cts": "_f_postcssconfig",
3677 "postcss.config.mts": "_f_postcssconfig",
3678 "postcss.config.js": "_f_postcssconfig",
3679 "postcss.config.cjs": "_f_postcssconfig",
3680 "postcss.config.mjs": "_f_postcssconfig",
3681 "preact.config.js": "_f_preact",
3682 ".pre-commit-config.yaml": "_f_precommit",
3683 ".prettierrc": "_f_prettier",
3684 ".prettierignore": "_f_prettier",
3685 "prettier.config.js": "_f_prettier",
3686 "prettier.config.cjs": "_f_prettier",
3687 "prettier.config.mjs": "_f_prettier",
3688 "prettier.config.ts": "_f_prettier",
3689 "prettier.config.coffee": "_f_prettier",
3690 ".prettierrc.js": "_f_prettier",
3691 ".prettierrc.cjs": "_f_prettier",
3692 ".prettierrc.mjs": "_f_prettier",
3693 ".prettierrc.json": "_f_prettier",
3694 ".prettierrc.json5": "_f_prettier",
3695 ".prettierrc.yml": "_f_prettier",
3696 ".prettierrc.yaml": "_f_prettier",
3697 ".prettierrc.toml": "_f_prettier",
3698 "prisma.config.js": "_f_prismaconfig",
3699 "prisma.config.ts": "_f_prismaconfig",
3700 "procfile": "_f_procfile",
3701 "protractor.conf.js": "_f_protractor",
3702 "protractor.conf.coffee": "_f_protractor",
3703 "protractor.conf.ts": "_f_protractor",
3704 "publiccode.yml": "_f_publiccode",
3705 ".jade-lintrc": "_f_pug",
3706 ".pug-lintrc": "_f_pug",
3707 ".jade-lint.json": "_f_pug",
3708 ".pug-lintrc.js": "_f_pug",
3709 ".pug-lintrc.json": "_f_pug",
3710 "Pulumi.yaml": "_f_pulumi",
3711 "Pulumi.yml": "_f_pulumi",
3712 "Pulumi.dev.yaml": "_f_pulumi",
3713 "Pulumi.dev.yml": "_f_pulumi",
3714 "Pulumi.prod.yaml": "_f_pulumi",
3715 "Pulumi.prod.yml": "_f_pulumi",
3716 "purgecss.config.js": "_f_purgecss",
3717 ".python-version": "_f_pyenv",
3718 "pyproject.toml": "_f_pypi",
3719 "pytest.ini": "_f_pytest",
3720 ".pytest.ini": "_f_pytest",
3721 "pytest.toml": "_f_pytest",
3722 ".pytest.toml": "_f_pytest",
3723 "py.typed": "_f_pytyped",
3724 ".pyup": "_f_pyup",
3725 ".pyup.yml": "_f_pyup",
3726 "qmldir": "_f_qmldir",
3727 "quasar.conf.js": "_f_quasar",
3728 "quasar.config.js": "_f_quasar",
3729 "quasar.config.mjs": "_f_quasar",
3730 "quasar.config.cjs": "_f_quasar",
3731 "quasar.config.ts": "_f_quasar",
3732 "Rakefile": "_f_rake",
3733 "razzle.config.js": "_f_razzle",
3734 "react-router.config.js": "_f_reactrouter",
3735 "react-router.config.ts": "_f_reactrouter",
3736 ".rehyperc": "_f_rehype",
3737 ".rehypeignore": "_f_rehype",
3738 ".rehyperc.cjs": "_f_rehype",
3739 ".rehyperc.js": "_f_rehype",
3740 ".rehyperc.json": "_f_rehype",
3741 ".rehyperc.mjs": "_f_rehype",
3742 ".rehyperc.yml": "_f_rehype",
3743 ".rehyperc.yaml": "_f_rehype",
3744 ".remarkrc": "_f_remark",
3745 ".remarkignore": "_f_remark",
3746 ".remarkrc.cjs": "_f_remark",
3747 ".remarkrc.js": "_f_remark",
3748 ".remarkrc.json": "_f_remark",
3749 ".remarkrc.mjs": "_f_remark",
3750 ".remarkrc.yml": "_f_remark",
3751 ".remarkrc.yaml": "_f_remark",
3752 ".renovaterc": "_f_renovate",
3753 ".renovaterc.json": "_f_renovate",
3754 "renovate.json": "_f_renovate",
3755 "renovate.json5": "_f_renovate",
3756 ".replit": "_f_replit",
3757 "replit.nix": "_f_replit",
3758 ".retextrc": "_f_retext",
3759 ".retextignore": "_f_retext",
3760 ".retextrc.cjs": "_f_retext",
3761 ".retextrc.js": "_f_retext",
3762 ".retextrc.json": "_f_retext",
3763 ".retextrc.mjs": "_f_retext",
3764 ".retextrc.yml": "_f_retext",
3765 ".retextrc.yaml": "_f_retext",
3766 "robots.txt": "_f_robots",
3767 "rolldown.config.js": "_f_rolldown",
3768 "rolldown.config.cjs": "_f_rolldown",
3769 "rolldown.config.mjs": "_f_rolldown",
3770 "rolldown.config.coffee": "_f_rolldown",
3771 "rolldown.config.ts": "_f_rolldown",
3772 "rolldown.config.mts": "_f_rolldown",
3773 "rolldown.config.cts": "_f_rolldown",
3774 "rolldown.config.common.js": "_f_rolldown",
3775 "rolldown.config.common.cjs": "_f_rolldown",
3776 "rolldown.config.common.mjs": "_f_rolldown",
3777 "rolldown.config.common.coffee": "_f_rolldown",
3778 "rolldown.config.common.ts": "_f_rolldown",
3779 "rolldown.config.common.mts": "_f_rolldown",
3780 "rolldown.config.common.cts": "_f_rolldown",
3781 "rolldown.config.dev.js": "_f_rolldown",
3782 "rolldown.config.dev.cjs": "_f_rolldown",
3783 "rolldown.config.dev.mjs": "_f_rolldown",
3784 "rolldown.config.dev.coffee": "_f_rolldown",
3785 "rolldown.config.dev.ts": "_f_rolldown",
3786 "rolldown.config.dev.mts": "_f_rolldown",
3787 "rolldown.config.dev.cts": "_f_rolldown",
3788 "rolldown.config.prod.js": "_f_rolldown",
3789 "rolldown.config.prod.cjs": "_f_rolldown",
3790 "rolldown.config.prod.mjs": "_f_rolldown",
3791 "rolldown.config.prod.coffee": "_f_rolldown",
3792 "rolldown.config.prod.ts": "_f_rolldown",
3793 "rolldown.config.prod.mts": "_f_rolldown",
3794 "rolldown.config.prod.cts": "_f_rolldown",
3795 "rollup.config.js": "_f_rollup",
3796 "rollup.config.cjs": "_f_rollup",
3797 "rollup.config.mjs": "_f_rollup",
3798 "rollup.config.coffee": "_f_rollup",
3799 "rollup.config.ts": "_f_rollup",
3800 "rollup.config.common.js": "_f_rollup",
3801 "rollup.config.common.cjs": "_f_rollup",
3802 "rollup.config.common.mjs": "_f_rollup",
3803 "rollup.config.common.coffee": "_f_rollup",
3804 "rollup.config.common.ts": "_f_rollup",
3805 "rollup.config.dev.js": "_f_rollup",
3806 "rollup.config.dev.cjs": "_f_rollup",
3807 "rollup.config.dev.mjs": "_f_rollup",
3808 "rollup.config.dev.coffee": "_f_rollup",
3809 "rollup.config.dev.ts": "_f_rollup",
3810 "rollup.config.prod.js": "_f_rollup",
3811 "rollup.config.prod.cjs": "_f_rollup",
3812 "rollup.config.prod.mjs": "_f_rollup",
3813 "rollup.config.prod.coffee": "_f_rollup",
3814 "rollup.config.prod.ts": "_f_rollup",
3815 "rome.json": "_f_rome",
3816 ".rspec": "_f_rspec",
3817 ".rubocop.yml": "_f_rubocop",
3818 ".rubocop_todo.yml": "_f_rubocop",
3819 ".ruby-version": "_f_ruby",
3820 "rust-toolchain": "_f_rust_toolchain",
3821 "rust-toolchain.toml": "_f_rust_toolchain",
3822 ".slshrc": "_f_s-lang",
3823 ".isisrc": "_f_s-lang",
3824 ".jedrc": "_f_s-lang",
3825 ".sailsrc": "_f_sails",
3826 ".sapphirerc.json": "_f_sapphire_framework_cli",
3827 ".sapphirerc.yml": "_f_sapphire_framework_cli",
3828 ".sapphirerc": "_f_sapphire_framework_cli",
3829 "seedkit.config.js": "_f_seedkit",
3830 "seedkit.config.ts": "_f_seedkit",
3831 "seedkit.config.mjs": "_f_seedkit",
3832 ".sentryclirc": "_f_sentry",
3833 ".sequelizerc": "_f_sequelize",
3834 ".sequelizerc.js": "_f_sequelize",
3835 ".sequelizerc.json": "_f_sequelize",
3836 "serverless.yml": "_f_serverless",
3837 "serverless.json": "_f_serverless",
3838 "serverless.js": "_f_serverless",
3839 "serverless.ts": "_f_serverless",
3840 "components.json": "_f_shadcn",
3841 ".shellcheckrc": "_f_shellcheck",
3842 "Shuttle.toml": "_f_shuttle",
3843 "sitemap.xml": "_f_sitemap",
3844 "sitemap.rss": "_f_sitemap",
3845 "sitemap.txt": "_f_sitemap",
3846 "sitemap.atom": "_f_sitemap",
3847 "slash-up.config.js": "_f_slashup",
3848 "smithery.yaml": "_f_smithery",
3849 "snapcraft.yaml": "_f_snapcraft",
3850 "snaplet.config.js": "_f_snaplet",
3851 "snaplet.config.ts": "_f_snaplet",
3852 ".snyk": "_f_snyk",
3853 ".solidarity": "_f_solidarity",
3854 ".solidarity.json": "_f_solidarity",
3855 "spin.toml": "_f_spin",
3856 "sst-env.d.ts": "_f_sst",
3857 "sst-env.ts": "_f_sst",
3858 "sst-env.js": "_f_sst",
3859 "sst.config.d.ts": "_f_sst",
3860 "sst.config.ts": "_f_sst",
3861 "sst.config.js": "_f_sst",
3862 ".stackblitzrc": "_f_stackblitz",
3863 "stryker.conf.mjs": "_f_stryker",
3864 "stryker.conf.cjs": "_f_stryker",
3865 "stryker.conf.js": "_f_stryker",
3866 "stryker.conf.conf": "_f_stryker",
3867 "stryker.conf.json": "_f_stryker",
3868 ".stryker.conf.mjs": "_f_stryker",
3869 ".stryker.conf.cjs": "_f_stryker",
3870 ".stryker.conf.js": "_f_stryker",
3871 ".stryker.conf.conf": "_f_stryker",
3872 ".stryker.conf.json": "_f_stryker",
3873 "stryker.config.mjs": "_f_stryker",
3874 "stryker.config.cjs": "_f_stryker",
3875 "stryker.config.js": "_f_stryker",
3876 "stryker.config.conf": "_f_stryker",
3877 "stryker.config.json": "_f_stryker",
3878 ".stryker.config.mjs": "_f_stryker",
3879 ".stryker.config.cjs": "_f_stryker",
3880 ".stryker.config.js": "_f_stryker",
3881 ".stryker.config.conf": "_f_stryker",
3882 ".stryker.config.json": "_f_stryker",
3883 "stryker-config.mjs": "_f_stryker",
3884 "stryker-config.cjs": "_f_stryker",
3885 "stryker-config.js": "_f_stryker",
3886 "stryker-config.conf": "_f_stryker",
3887 "stryker-config.json": "_f_stryker",
3888 "stryker4s.mjs": "_f_stryker",
3889 "stryker4s.cjs": "_f_stryker",
3890 "stryker4s.js": "_f_stryker",
3891 "stryker4s.conf": "_f_stryker",
3892 "stryker4s.json": "_f_stryker",
3893 ".stylelintrc": "_f_stylelint",
3894 ".stylelintignore": "_f_stylelint",
3895 ".stylelintcache": "_f_stylelint",
3896 "stylelint.config.js": "_f_stylelint",
3897 "stylelint.config.json": "_f_stylelint",
3898 "stylelint.config.yaml": "_f_stylelint",
3899 "stylelint.config.yml": "_f_stylelint",
3900 "stylelint.config.ts": "_f_stylelint",
3901 "stylelint.config.cjs": "_f_stylelint",
3902 "stylelint.config.mjs": "_f_stylelint",
3903 ".stylelintrc.js": "_f_stylelint",
3904 ".stylelintrc.json": "_f_stylelint",
3905 ".stylelintrc.yaml": "_f_stylelint",
3906 ".stylelintrc.yml": "_f_stylelint",
3907 ".stylelintrc.ts": "_f_stylelint",
3908 ".stylelintrc.cjs": "_f_stylelint",
3909 ".stylelintrc.mjs": "_f_stylelint",
3910 ".stylish-haskell.yaml": "_f_stylish_haskell",
3911 ".svnignore": "_f_subversion",
3912 "svelte.config.js": "_f_svelteconfig",
3913 "svelte.config.ts": "_f_svelteconfig",
3914 "svgo.config.cjs": "_f_svgo",
3915 "svgo.config.js": "_f_svgo",
3916 "svgo.config.mjs": "_f_svgo",
3917 ".swcrc": "_f_swc",
3918 "package.pins": "_f_swift",
3919 "symfony.lock": "_f_symfony",
3920 ".syncpackrc": "_f_syncpack",
3921 ".syncpackrc.json": "_f_syncpack",
3922 ".syncpackrc.yaml": "_f_syncpack",
3923 ".syncpackrc.yml": "_f_syncpack",
3924 ".syncpackrc.js": "_f_syncpack",
3925 ".syncpackrc.cjs": "_f_syncpack",
3926 "syncpack.config.js": "_f_syncpack",
3927 "syncpack.config.cjs": "_f_syncpack",
3928 "tailwind.js": "_f_tailwind",
3929 "tailwind.cjs": "_f_tailwind",
3930 "tailwind.coffee": "_f_tailwind",
3931 "tailwind.ts": "_f_tailwind",
3932 "tailwind.json": "_f_tailwind",
3933 "tailwind.mjs": "_f_tailwind",
3934 "tailwind.config.js": "_f_tailwind",
3935 "tailwind.config.cjs": "_f_tailwind",
3936 "tailwind.config.coffee": "_f_tailwind",
3937 "tailwind.config.ts": "_f_tailwind",
3938 "tailwind.config.json": "_f_tailwind",
3939 "tailwind.config.mjs": "_f_tailwind",
3940 ".tailwind.js": "_f_tailwind",
3941 ".tailwind.cjs": "_f_tailwind",
3942 ".tailwind.coffee": "_f_tailwind",
3943 ".tailwind.ts": "_f_tailwind",
3944 ".tailwind.json": "_f_tailwind",
3945 ".tailwind.mjs": "_f_tailwind",
3946 ".tailwindrc.js": "_f_tailwind",
3947 ".tailwindrc.cjs": "_f_tailwind",
3948 ".tailwindrc.coffee": "_f_tailwind",
3949 ".tailwindrc.ts": "_f_tailwind",
3950 ".tailwindrc.json": "_f_tailwind",
3951 ".tailwindrc.mjs": "_f_tailwind",
3952 "tamagui.js": "_f_tamagui",
3953 "tamagui.mjs": "_f_tamagui",
3954 "tamagui.cjs": "_f_tamagui",
3955 "tamagui.ts": "_f_tamagui",
3956 "tamagui.mts": "_f_tamagui",
3957 "tamagui.cts": "_f_tamagui",
3958 "tamagui.config.js": "_f_tamagui",
3959 "tamagui.config.mjs": "_f_tamagui",
3960 "tamagui.config.cjs": "_f_tamagui",
3961 "tamagui.config.ts": "_f_tamagui",
3962 "tamagui.config.mts": "_f_tamagui",
3963 "tamagui.config.cts": "_f_tamagui",
3964 ".taplo.toml": "_f_taplo",
3965 "taplo.toml": "_f_taplo",
3966 "taskfile.yml": "_f_taskfile",
3967 "taskfile.yaml": "_f_taskfile",
3968 "taskfile.dist.yml": "_f_taskfile",
3969 "taskfile.dist.yaml": "_f_taskfile",
3970 "tauri.conf.json": "_f_tauri",
3971 ".taurignore": "_f_tauri",
3972 ".testcaferc.json": "_f_testcafe",
3973 "testplane.conf.ts": "_f_testplane",
3974 "testplane.conf.js": "_f_testplane",
3975 "testplane.conf.cts": "_f_testplane",
3976 "testplane.conf.cjs": "_f_testplane",
3977 "testplane.config.ts": "_f_testplane",
3978 "testplane.config.js": "_f_testplane",
3979 "testplane.config.cts": "_f_testplane",
3980 "testplane.config.cjs": "_f_testplane",
3981 ".tfignore": "_f_tfs",
3982 ".tiltignore": "_f_tiltfile",
3983 ".tmux.conf": "_f_tmux",
3984 "tox.ini": "_f_tox",
3985 ".travis.yml": "_f_travis",
3986 "truffle-config.js": "_f_truffle",
3987 "truffle-config.ts": "_f_truffle",
3988 "trunk.yaml": "_f_trunk",
3989 "tsconfig.json": "_f_tsconfig",
3990 "tsconfig.app.json": "_f_tsconfig",
3991 "tsconfig.base.json": "_f_tsconfig",
3992 "tsconfig.build.json": "_f_tsconfig",
3993 "tsconfig.cjs.json": "_f_tsconfig",
3994 "tsconfig.common.json": "_f_tsconfig",
3995 "tsconfig.debug.json": "_f_tsconfig",
3996 "tsconfig.dev.json": "_f_tsconfig",
3997 "tsconfig.development.json": "_f_tsconfig",
3998 "tsconfig.e2e.json": "_f_tsconfig",
3999 "tsconfig.eslint.json": "_f_tsconfig",
4000 "tsconfig.esm.json": "_f_tsconfig",
4001 "tsconfig.mjs.json": "_f_tsconfig",
4002 "tsconfig.node.json": "_f_tsconfig",
4003 "tsconfig.prod.json": "_f_tsconfig",
4004 "tsconfig.production.json": "_f_tsconfig",
4005 "tsconfig.release.json": "_f_tsconfig",
4006 "tsconfig.server.json": "_f_tsconfig",
4007 "tsconfig.spec.json": "_f_tsconfig",
4008 "tsconfig.staging.json": "_f_tsconfig",
4009 "tsconfig.test.json": "_f_tsconfig",
4010 "tsconfig.types.json": "_f_tsconfig",
4011 "tsconfig.lib.json": "_f_tsconfig",
4012 "tsconfig.lib.prod.json": "_f_tsconfig",
4013 "tsdoc.json": "_f_tsdoc",
4014 "tsdown.config.ts": "_f_tsdown",
4015 "tsdown.config.mts": "_f_tsdown",
4016 "tsdown.config.cts": "_f_tsdown",
4017 "tsdown.config.js": "_f_tsdown",
4018 "tsdown.config.mjs": "_f_tsdown",
4019 "tsdown.config.cjs": "_f_tsdown",
4020 "tsdown.config.json": "_f_tsdown",
4021 "tslint.json": "_f_tslint",
4022 "tslint.yaml": "_f_tslint",
4023 "tslint.yml": "_f_tslint",
4024 "turbo.json": "_f_turbo",
4025 "turbo.jsonc": "_f_turbo",
4026 "microfrontends.json": "_f_turbo",
4027 "microfrontends.jsonc": "_f_turbo",
4028 "typedoc.cjs": "_f_typedoc",
4029 "typedoc.js": "_f_typedoc",
4030 "typedoc.json": "_f_typedoc",
4031 "typedoc.jsonc": "_f_typedoc",
4032 "typedoc.mjs": "_f_typedoc",
4033 "typedoc.config.cjs": "_f_typedoc",
4034 "typedoc.config.js": "_f_typedoc",
4035 "typedoc.config.json": "_f_typedoc",
4036 "typedoc.config.jsonc": "_f_typedoc",
4037 "typedoc.config.mjs": "_f_typedoc",
4038 ".unibeautifyrc": "_f_unibeautify",
4039 "unibeautify.config.js": "_f_unibeautify",
4040 ".unibeautifyrc.js": "_f_unibeautify",
4041 ".unibeautifyrc.json": "_f_unibeautify",
4042 ".unibeautifyrc.yaml": "_f_unibeautify",
4043 ".unibeautifyrc.yml": "_f_unibeautify",
4044 "unlicense": "_f_unlicense",
4045 "unlicence": "_f_unlicense",
4046 "UNLICENSE": "_f_unlicense",
4047 "UNLICENCE": "_f_unlicense",
4048 "unlicense.md": "_f_unlicense",
4049 "unlicense.txt": "_f_unlicense",
4050 "unlicence.md": "_f_unlicense",
4051 "unlicence.txt": "_f_unlicense",
4052 "UNLICENSE.md": "_f_unlicense",
4053 "UNLICENSE.txt": "_f_unlicense",
4054 "UNLICENCE.md": "_f_unlicense",
4055 "UNLICENCE.txt": "_f_unlicense",
4056 "uno.config.js": "_f_unocss",
4057 "uno.config.mjs": "_f_unocss",
4058 "uno.config.ts": "_f_unocss",
4059 "uno.config.mts": "_f_unocss",
4060 "unocss.config.js": "_f_unocss",
4061 "unocss.config.mjs": "_f_unocss",
4062 "unocss.config.ts": "_f_unocss",
4063 "unocss.config.mts": "_f_unocss",
4064 "uv.lock": "_f_uv",
4065 "vagrantfile": "_f_vagrant",
4066 "vapor.yml": "_f_vapor",
4067 "now.json": "_f_vercel",
4068 ".nowignore": "_f_vercel",
4069 "vercel.json": "_f_vercel",
4070 ".vercelignore": "_f_vercel",
4071 ".vimrc": "_f_vim",
4072 ".gvimrc": "_f_vim",
4073 "vite.config.js": "_f_vite",
4074 "vite.config.mjs": "_f_vite",
4075 "vite.config.ts": "_f_vite",
4076 "vite.config.cjs": "_f_vite",
4077 "vite.config.mts": "_f_vite",
4078 "vite.config.cts": "_f_vite",
4079 "vitest.config.js": "_f_vitest",
4080 "vitest.config.mjs": "_f_vitest",
4081 "vitest.config.ts": "_f_vitest",
4082 "vitest.config.cjs": "_f_vitest",
4083 "vitest.config.mts": "_f_vitest",
4084 "vitest.config.cts": "_f_vitest",
4085 "vitest.workspace.json": "_f_vitest",
4086 "vitest.workspace.js": "_f_vitest",
4087 "vitest.workspace.mjs": "_f_vitest",
4088 "vitest.workspace.ts": "_f_vitest",
4089 "vitest.workspace.cjs": "_f_vitest",
4090 "vitest.workspace.mts": "_f_vitest",
4091 "vitest.workspace.cts": "_f_vitest",
4092 "vitest.projects.json": "_f_vitest",
4093 "vitest.projects.js": "_f_vitest",
4094 "vitest.projects.mjs": "_f_vitest",
4095 "vitest.projects.ts": "_f_vitest",
4096 "vitest.projects.cjs": "_f_vitest",
4097 "vitest.projects.mts": "_f_vitest",
4098 "vitest.projects.cts": "_f_vitest",
4099 ".vscodeignore": "_f_vscode",
4100 "launch.json": "_f_vscode",
4101 "tasks.json": "_f_vscode",
4102 "vscodeignore.json": "_f_vscode",
4103 ".vscode-test.js": "_f_vscode_test",
4104 ".vscode-test.json": "_f_vscode_test",
4105 ".vscode-test.mjs": "_f_vscode_test",
4106 ".vuerc": "_f_vueconfig",
4107 "vue.config.js": "_f_vueconfig",
4108 "vue.config.cjs": "_f_vueconfig",
4109 "vue.config.mjs": "_f_vueconfig",
4110 "wallaby.json": "_f_wallaby",
4111 "wallaby.js": "_f_wallaby",
4112 "wallaby.ts": "_f_wallaby",
4113 "wallaby.coffee": "_f_wallaby",
4114 "wallaby.conf.json": "_f_wallaby",
4115 "wallaby.conf.js": "_f_wallaby",
4116 "wallaby.conf.ts": "_f_wallaby",
4117 "wallaby.conf.coffee": "_f_wallaby",
4118 ".wallaby.json": "_f_wallaby",
4119 ".wallaby.js": "_f_wallaby",
4120 ".wallaby.ts": "_f_wallaby",
4121 ".wallaby.coffee": "_f_wallaby",
4122 ".wallaby.conf.json": "_f_wallaby",
4123 ".wallaby.conf.js": "_f_wallaby",
4124 ".wallaby.conf.ts": "_f_wallaby",
4125 ".wallaby.conf.coffee": "_f_wallaby",
4126 "wally.toml": "_f_wally",
4127 "wally.lock": "_f_wally",
4128 ".watchmanconfig": "_f_watchmanconfig",
4129 "wdio.json": "_f_wdio",
4130 "wdio.js": "_f_wdio",
4131 "wdio.mjs": "_f_wdio",
4132 "wdio.cjs": "_f_wdio",
4133 "wdio.ts": "_f_wdio",
4134 "wdio.mts": "_f_wdio",
4135 "wdio.cts": "_f_wdio",
4136 "wdio.conf.json": "_f_wdio",
4137 "wdio.conf.js": "_f_wdio",
4138 "wdio.conf.mjs": "_f_wdio",
4139 "wdio.conf.cjs": "_f_wdio",
4140 "wdio.conf.ts": "_f_wdio",
4141 "wdio.conf.mts": "_f_wdio",
4142 "wdio.conf.cts": "_f_wdio",
4143 ".weblate": "_f_weblate",
4144 ".weblate.ini": "_f_weblate",
4145 "weblate": "_f_weblate",
4146 "weblate.ini": "_f_weblate",
4147 "webpack.base.conf.js": "_f_webpack",
4148 "webpack.base.conf.cjs": "_f_webpack",
4149 "webpack.base.conf.mjs": "_f_webpack",
4150 "webpack.base.conf.coffee": "_f_webpack",
4151 "webpack.base.conf.ts": "_f_webpack",
4152 "webpack.common.js": "_f_webpack",
4153 "webpack.common.cjs": "_f_webpack",
4154 "webpack.common.mjs": "_f_webpack",
4155 "webpack.common.coffee": "_f_webpack",
4156 "webpack.common.ts": "_f_webpack",
4157 "webpack.config.js": "_f_webpack",
4158 "webpack.config.cjs": "_f_webpack",
4159 "webpack.config.mjs": "_f_webpack",
4160 "webpack.config.coffee": "_f_webpack",
4161 "webpack.config.ts": "_f_webpack",
4162 "webpack.config.base.js": "_f_webpack",
4163 "webpack.config.base.cjs": "_f_webpack",
4164 "webpack.config.base.mjs": "_f_webpack",
4165 "webpack.config.base.coffee": "_f_webpack",
4166 "webpack.config.base.ts": "_f_webpack",
4167 "webpack.config.build.js": "_f_webpack",
4168 "webpack.config.build.cjs": "_f_webpack",
4169 "webpack.config.build.mjs": "_f_webpack",
4170 "webpack.config.build.coffee": "_f_webpack",
4171 "webpack.config.build.ts": "_f_webpack",
4172 "webpack.config.common.js": "_f_webpack",
4173 "webpack.config.common.cjs": "_f_webpack",
4174 "webpack.config.common.mjs": "_f_webpack",
4175 "webpack.config.common.coffee": "_f_webpack",
4176 "webpack.config.common.ts": "_f_webpack",
4177 "webpack.config.dev.js": "_f_webpack",
4178 "webpack.config.dev.cjs": "_f_webpack",
4179 "webpack.config.dev.mjs": "_f_webpack",
4180 "webpack.config.dev.coffee": "_f_webpack",
4181 "webpack.config.dev.ts": "_f_webpack",
4182 "webpack.config.development.js": "_f_webpack",
4183 "webpack.config.development.cjs": "_f_webpack",
4184 "webpack.config.development.mjs": "_f_webpack",
4185 "webpack.config.development.coffee": "_f_webpack",
4186 "webpack.config.development.ts": "_f_webpack",
4187 "webpack.config.staging.js": "_f_webpack",
4188 "webpack.config.staging.cjs": "_f_webpack",
4189 "webpack.config.staging.mjs": "_f_webpack",
4190 "webpack.config.staging.coffee": "_f_webpack",
4191 "webpack.config.staging.ts": "_f_webpack",
4192 "webpack.config.test.js": "_f_webpack",
4193 "webpack.config.test.cjs": "_f_webpack",
4194 "webpack.config.test.mjs": "_f_webpack",
4195 "webpack.config.test.coffee": "_f_webpack",
4196 "webpack.config.test.ts": "_f_webpack",
4197 "webpack.config.prod.js": "_f_webpack",
4198 "webpack.config.prod.cjs": "_f_webpack",
4199 "webpack.config.prod.mjs": "_f_webpack",
4200 "webpack.config.prod.coffee": "_f_webpack",
4201 "webpack.config.prod.ts": "_f_webpack",
4202 "webpack.config.production.js": "_f_webpack",
4203 "webpack.config.production.cjs": "_f_webpack",
4204 "webpack.config.production.mjs": "_f_webpack",
4205 "webpack.config.production.coffee": "_f_webpack",
4206 "webpack.config.production.ts": "_f_webpack",
4207 "webpack.config.babel.js": "_f_webpack",
4208 "webpack.config.babel.cjs": "_f_webpack",
4209 "webpack.config.babel.mjs": "_f_webpack",
4210 "webpack.config.babel.coffee": "_f_webpack",
4211 "webpack.config.babel.ts": "_f_webpack",
4212 "webpack.config.base.babel.js": "_f_webpack",
4213 "webpack.config.base.babel.cjs": "_f_webpack",
4214 "webpack.config.base.babel.mjs": "_f_webpack",
4215 "webpack.config.base.babel.coffee": "_f_webpack",
4216 "webpack.config.base.babel.ts": "_f_webpack",
4217 "webpack.config.common.babel.js": "_f_webpack",
4218 "webpack.config.common.babel.cjs": "_f_webpack",
4219 "webpack.config.common.babel.mjs": "_f_webpack",
4220 "webpack.config.common.babel.coffee": "_f_webpack",
4221 "webpack.config.common.babel.ts": "_f_webpack",
4222 "webpack.config.dev.babel.js": "_f_webpack",
4223 "webpack.config.dev.babel.cjs": "_f_webpack",
4224 "webpack.config.dev.babel.mjs": "_f_webpack",
4225 "webpack.config.dev.babel.coffee": "_f_webpack",
4226 "webpack.config.dev.babel.ts": "_f_webpack",
4227 "webpack.config.development.babel.js": "_f_webpack",
4228 "webpack.config.development.babel.cjs": "_f_webpack",
4229 "webpack.config.development.babel.mjs": "_f_webpack",
4230 "webpack.config.development.babel.coffee": "_f_webpack",
4231 "webpack.config.development.babel.ts": "_f_webpack",
4232 "webpack.config.staging.babel.js": "_f_webpack",
4233 "webpack.config.staging.babel.cjs": "_f_webpack",
4234 "webpack.config.staging.babel.mjs": "_f_webpack",
4235 "webpack.config.staging.babel.coffee": "_f_webpack",
4236 "webpack.config.staging.babel.ts": "_f_webpack",
4237 "webpack.config.test.babel.js": "_f_webpack",
4238 "webpack.config.test.babel.cjs": "_f_webpack",
4239 "webpack.config.test.babel.mjs": "_f_webpack",
4240 "webpack.config.test.babel.coffee": "_f_webpack",
4241 "webpack.config.test.babel.ts": "_f_webpack",
4242 "webpack.config.prod.babel.js": "_f_webpack",
4243 "webpack.config.prod.babel.cjs": "_f_webpack",
4244 "webpack.config.prod.babel.mjs": "_f_webpack",
4245 "webpack.config.prod.babel.coffee": "_f_webpack",
4246 "webpack.config.prod.babel.ts": "_f_webpack",
4247 "webpack.config.production.babel.js": "_f_webpack",
4248 "webpack.config.production.babel.cjs": "_f_webpack",
4249 "webpack.config.production.babel.mjs": "_f_webpack",
4250 "webpack.config.production.babel.coffee": "_f_webpack",
4251 "webpack.config.production.babel.ts": "_f_webpack",
4252 "webpack.dev.js": "_f_webpack",
4253 "webpack.dev.cjs": "_f_webpack",
4254 "webpack.dev.mjs": "_f_webpack",
4255 "webpack.dev.coffee": "_f_webpack",
4256 "webpack.dev.ts": "_f_webpack",
4257 "webpack.dev.conf.js": "_f_webpack",
4258 "webpack.dev.conf.cjs": "_f_webpack",
4259 "webpack.dev.conf.mjs": "_f_webpack",
4260 "webpack.dev.conf.coffee": "_f_webpack",
4261 "webpack.dev.conf.ts": "_f_webpack",
4262 "webpack.local.js": "_f_webpack",
4263 "webpack.local.cjs": "_f_webpack",
4264 "webpack.local.mjs": "_f_webpack",
4265 "webpack.local.coffee": "_f_webpack",
4266 "webpack.local.ts": "_f_webpack",
4267 "webpack.prod.js": "_f_webpack",
4268 "webpack.prod.cjs": "_f_webpack",
4269 "webpack.prod.mjs": "_f_webpack",
4270 "webpack.prod.coffee": "_f_webpack",
4271 "webpack.prod.ts": "_f_webpack",
4272 "webpack.prod.conf.js": "_f_webpack",
4273 "webpack.prod.conf.cjs": "_f_webpack",
4274 "webpack.prod.conf.mjs": "_f_webpack",
4275 "webpack.prod.conf.coffee": "_f_webpack",
4276 "webpack.prod.conf.ts": "_f_webpack",
4277 "webpack.main.config.js": "_f_webpack",
4278 "webpack.main.config.cjs": "_f_webpack",
4279 "webpack.main.config.mjs": "_f_webpack",
4280 "webpack.main.config.coffee": "_f_webpack",
4281 "webpack.main.config.ts": "_f_webpack",
4282 "webpack.mix.js": "_f_webpack",
4283 "webpack.mix.cjs": "_f_webpack",
4284 "webpack.mix.mjs": "_f_webpack",
4285 "webpack.mix.coffee": "_f_webpack",
4286 "webpack.mix.ts": "_f_webpack",
4287 "webpack.plugins.js": "_f_webpack",
4288 "webpack.plugins.cjs": "_f_webpack",
4289 "webpack.plugins.mjs": "_f_webpack",
4290 "webpack.plugins.coffee": "_f_webpack",
4291 "webpack.plugins.ts": "_f_webpack",
4292 "webpack.renderer.config.js": "_f_webpack",
4293 "webpack.renderer.config.cjs": "_f_webpack",
4294 "webpack.renderer.config.mjs": "_f_webpack",
4295 "webpack.renderer.config.coffee": "_f_webpack",
4296 "webpack.renderer.config.ts": "_f_webpack",
4297 "webpack.rules.js": "_f_webpack",
4298 "webpack.rules.cjs": "_f_webpack",
4299 "webpack.rules.mjs": "_f_webpack",
4300 "webpack.rules.coffee": "_f_webpack",
4301 "webpack.rules.ts": "_f_webpack",
4302 "webpack.test.conf.js": "_f_webpack",
4303 "webpack.test.conf.cjs": "_f_webpack",
4304 "webpack.test.conf.mjs": "_f_webpack",
4305 "webpack.test.conf.coffee": "_f_webpack",
4306 "webpack.test.conf.ts": "_f_webpack",
4307 "wercker.yml": "_f_wercker",
4308 "windi.config.ts": "_f_windi",
4309 "windi.config.js": "_f_windi",
4310 "wpml-config.xml": "_f_wpml",
4311 "wxt.config.js": "_f_wxt",
4312 "wxt.config.cjs": "_f_wxt",
4313 "wxt.config.mjs": "_f_wxt",
4314 "wxt.config.ts": "_f_wxt",
4315 "wxt.config.cts": "_f_wxt",
4316 "wxt.config.mts": "_f_wxt",
4317 ".xo-config": "_f_xo",
4318 "xo.config.js": "_f_xo",
4319 "xo.config.cjs": "_f_xo",
4320 "xo.config.mjs": "_f_xo",
4321 "xo.config.ts": "_f_xo",
4322 "xo.config.cts": "_f_xo",
4323 "xo.config.mts": "_f_xo",
4324 ".xo-config.js": "_f_xo",
4325 ".xo-config.json": "_f_xo",
4326 ".xo-config.cjs": "_f_xo",
4327 ".xcompose": "_f_xorg",
4328 ".yamllint": "_f_yamllint",
4329 ".yaspellerrc": "_f_yandex",
4330 ".yaspeller.json": "_f_yandex",
4331 "yarn.lock": "_f_yarn",
4332 ".yarnrc": "_f_yarn",
4333 ".yarnrc.yml": "_f_yarn",
4334 ".yarnclean": "_f_yarn",
4335 ".yarn-integrity": "_f_yarn",
4336 ".yarn-metadata.json": "_f_yarn",
4337 ".yarn-state.yml": "_f_yarn",
4338 ".yarnignore": "_f_yarn",
4339 ".yo-rc.json": "_f_yeoman"
4340 },
4341 "languageIds": {
4342 "actionscript": "_f_actionscript",
4343 "ada": "_f_ada",
4344 "advpl": "_f_advpl",
4345 "affectscript": "_f_affectscript",
4346 "agda": "_f_agda",
4347 "lagda-md": "_f_agda",
4348 "lagda-rst": "_f_agda",
4349 "lagda-tex": "_f_agda",
4350 "al": "_f_al",
4351 "alloy": "_f_alloy",
4352 "ansible": "_f_ansible",
4353 "antlr": "_f_antlr",
4354 "anyscript": "_f_anyscript",
4355 "apacheconf": "_f_apache",
4356 "apex": "_f_apex",
4357 "apiblueprint": "_f_apib",
4358 "apl": "_f_apl",
4359 "applescript": "_f_applescript",
4360 "asciidoc": "_f_asciidoc",
4361 "asp": "_f_asp",
4362 "asp (html)": "_f_asp",
4363 "arm": "_f_assembly",
4364 "asm": "_f_assembly",
4365 "asm-intel-x86-generic": "_f_assembly",
4366 "platformio-debug.asm": "_f_platformio",
4367 "asm-collection": "_f_assembly",
4368 "astro": "_f_astro",
4369 "ats": "_f_ats",
4370 "ahk": "_f_autohotkey",
4371 "ahk2": "_f_autohotkey",
4372 "autoit": "_f_autoit",
4373 "avro": "_f_avro",
4374 "azcli": "_f_azure",
4375 "azure-pipelines": "_f_azurepipelines",
4376 "ballerina": "_f_ballerina",
4377 "bat": "_f_bat",
4378 "bats": "_f_bats",
4379 "bazel": "_f_bazel",
4380 "bazelrc": "_f_bazel",
4381 "starlark": "_f_bazel",
4382 "befunge": "_f_befunge",
4383 "befunge98": "_f_befunge",
4384 "bicep": "_f_bicep",
4385 "biml": "_f_biml",
4386 "code-text-binary": "_f_binary",
4387 "biome_syntax_tree": "_f_biome",
4388 "blade": "_f_blade",
4389 "laravel-blade": "_f_blade",
4390 "blitzbasic": "_f_blitzbasic",
4391 "bolt": "_f_bolt",
4392 "bosque": "_f_bosque",
4393 "browserslist": "_f_browserslist",
4394 "bruno": "_f_bruno",
4395 "buf": "_f_buf",
4396 "buf-gen": "_f_buf",
4397 "bun.lockb": "_f_bun",
4398 "c": "_f_c",
4399 "c3": "_f_c3",
4400 "c-al": "_f_c_al",
4401 "cabal": "_f_cabal",
4402 "caddyfile": "_f_caddy",
4403 "capnp": "_f_capnp",
4404 "capnproto": "_f_capnp",
4405 "casc": "_f_casc",
4406 "cddl": "_f_cddl",
4407 "ceylon": "_f_ceylon",
4408 "cfml": "_f_cf",
4409 "lang-cfml": "_f_cf",
4410 "cfc": "_f_cfc",
4411 "cfmhtml": "_f_cfm",
4412 "cookbook": "_f_chef_cookbook",
4413 "circom": "_f_circom",
4414 "clojure": "_f_clojure",
4415 "clojurescript": "_f_clojurescript",
4416 "manifest-yaml": "_f_cloudfoundry",
4417 "cmake": "_f_cmake",
4418 "cmake-cache": "_f_cmake",
4419 "cobol": "_f_cobol",
4420 "ql": "_f_codeql",
4421 "coffeescript": "_f_coffeescript",
4422 "java-properties": "_f_config",
4423 "properties": "_f_config",
4424 "spring-boot-properties": "_f_config",
4425 "confluence": "_f_confluence",
4426 "instructions": "_f_copilot",
4427 "prompt": "_f_copilot",
4428 "cpp": "_f_cpp",
4429 "crystal": "_f_crystal",
4430 "csharp": "_f_csharp",
4431 "css": "_f_css",
4432 "cucumber": "_f_cucumber",
4433 "feature": "_f_cucumber",
4434 "cuda": "_f_cuda",
4435 "cuda-cpp": "_f_cuda",
4436 "cython": "_f_cython",
4437 "dal": "_f_dal",
4438 "dart": "_f_dartlang",
4439 "debian-changelog": "_f_debian",
4440 "debian-control": "_f_debian",
4441 "debian-copyright": "_f_debian",
4442 "debian-upstream-metadata": "_f_debian",
4443 "pascal": "_f_delphi",
4444 "objectpascal": "_f_delphi",
4445 "dhall": "_f_dhall",
4446 "diff": "_f_diff",
4447 "django-html": "_f_django",
4448 "django-txt": "_f_django",
4449 "d": "_f_dlang",
4450 "dscript": "_f_dlang",
4451 "dml": "_f_dlang",
4452 "diet": "_f_dlang",
4453 "dockerbake": "_f_docker",
4454 "dockercompose": "_f_docker",
4455 "dockerfile": "_f_docker",
4456 "dotenv": "_f_dotenv",
4457 "env": "_f_dotenv",
4458 "dotjs": "_f_dotjs",
4459 "doxygen": "_f_doxygen",
4460 "drawio": "_f_drawio",
4461 "drools": "_f_drools",
4462 "dtd": "_f_dtd",
4463 "dustjs": "_f_dustjs",
4464 "dylan": "_f_dylan",
4465 "dylan-lid": "_f_dylan",
4466 "earthfile": "_f_earthly",
4467 "edge": "_f_edge",
4468 "editorconfig": "_f_editorconfig",
4469 "eex": "_f_eex",
4470 "html-eex": "_f_eex",
4471 "es": "_f_elastic",
4472 "elixir": "_f_elixir",
4473 "elm": "_f_elm",
4474 "erb": "_f_erb",
4475 "html.erb": "_f_erb",
4476 "erlang": "_f_erlang",
4477 "esphome": "_f_esphome",
4478 "excel": "_f_excel",
4479 "falcon": "_f_falcon",
4480 "fql": "_f_fauna",
4481 "fortran": "_f_fortran",
4482 "fortran-modern": "_f_fortran",
4483 "FortranFreeForm": "_f_fortran",
4484 "FortranFixedForm": "_f_fortran",
4485 "fortran_fixed-form": "_f_fortran",
4486 "ftl": "_f_freemarker",
4487 "fsharp": "_f_fsharp",
4488 "fthtml": "_f_fthtml",
4489 "galen": "_f_galen",
4490 "gml-gms": "_f_gamemaker",
4491 "gml-gms2": "_f_gamemaker2",
4492 "gml-gm81": "_f_gamemaker81",
4493 "gcode": "_f_gcode",
4494 "gdscript": "_f_gdscript",
4495 "genstat": "_f_genstat",
4496 "git-commit": "_f_git",
4497 "git-rebase": "_f_git",
4498 "ignore": "_f_git",
4499 "gleam": "_f_gleam",
4500 "glimmer-js": "_f_glimmer",
4501 "glimmer-ts": "_f_glimmer",
4502 "glsl": "_f_glsl",
4503 "glyphs": "_f_glyphs",
4504 "automake": "_f_gnu",
4505 "m4": "_f_gnu",
4506 "makefile": "_f_gnu",
4507 "makefile2": "_f_gnu",
4508 "gnuplot": "_f_gnuplot",
4509 "go": "_f_go",
4510 "go.mod": "_f_go_package",
4511 "go.work": "_f_go_work",
4512 "goctl": "_f_goctl",
4513 "gradle-kotlin-dsl": "_f_gradle",
4514 "grain": "_f_grain",
4515 "graphql": "_f_graphql",
4516 "dot": "_f_graphviz",
4517 "grit": "_f_grit",
4518 "groovy": "_f_groovy",
4519 "haml": "_f_haml",
4520 "handlebars": "_f_handlebars",
4521 "harbour": "_f_harbour",
4522 "hcl": "_f_hashicorp",
4523 "haskell": "_f_haskell",
4524 "literate haskell": "_f_haskell",
4525 "haxe": "_f_haxe",
4526 "hxml": "_f_haxe",
4527 "Haxe AST dump": "_f_haxe",
4528 "helm": "_f_helm",
4529 "hjson": "_f_hjson",
4530 "hlsl": "_f_hlsl",
4531 "home-assistant": "_f_homeassistant",
4532 "hosts": "_f_host",
4533 "html": "_f_html",
4534 "http": "_f_http",
4535 "hunspell.aff": "_f_hunspell",
4536 "hunspell.dic": "_f_hunspell",
4537 "hy": "_f_hy",
4538 "hypr": "_f_hypr",
4539 "icl": "_f_icl",
4540 "imba": "_f_imba",
4541 "4GL": "_f_informix",
4542 "ini": "_f_ini",
4543 "ink": "_f_ink",
4544 "innosetup": "_f_innosetup",
4545 "io": "_f_io",
4546 "iodine": "_f_iodine",
4547 "janet": "_f_janet",
4548 "java": "_f_java",
4549 "jekyll": "_f_jekyll",
4550 "jenkins": "_f_jenkins",
4551 "declarative": "_f_jenkins",
4552 "jenkinsfile": "_f_jenkins",
4553 "jest-snapshot": "_f_jest_snapshot",
4554 "jinja": "_f_jinja",
4555 "jinja-java": "_f_jinja",
4556 "jinja-html": "_f_jinja",
4557 "jinja-xml": "_f_jinja",
4558 "jinja-css": "_f_jinja",
4559 "jinja-json": "_f_jinja",
4560 "jinja-md": "_f_jinja",
4561 "jinja-py": "_f_jinja",
4562 "jinja-rb": "_f_jinja",
4563 "jinja-js": "_f_jinja",
4564 "jinja-yaml": "_f_jinja",
4565 "jinja-toml": "_f_jinja",
4566 "jinja-latex": "_f_jinja",
4567 "jinja-lua": "_f_jinja",
4568 "jinja-properties": "_f_jinja",
4569 "jinja-shell": "_f_jinja",
4570 "jinja-dockerfile": "_f_jinja",
4571 "jinja-sql": "_f_jinja",
4572 "jinja-terraform": "_f_jinja",
4573 "jinja-nginx": "_f_jinja",
4574 "jinja-groovy": "_f_jinja",
4575 "jinja-systemd": "_f_jinja",
4576 "jinja-cpp": "_f_jinja",
4577 "javascript": "_f_js",
4578 "json": "_f_json",
4579 "json-tmlanguage": "_f_json",
4580 "jsonc": "_f_json",
4581 "jsonl": "_f_json",
4582 "json5": "_f_json5",
4583 "jsonnet": "_f_jsonnet",
4584 "julia": "_f_julia",
4585 "juliamarkdown": "_f_julia",
4586 "just": "_f_just",
4587 "k": "_f_k",
4588 "kivy": "_f_kivy",
4589 "kos": "_f_kos",
4590 "kotlin": "_f_kotlin",
4591 "kotlinscript": "_f_kotlin",
4592 "kusto": "_f_kusto",
4593 "lark": "_f_lark",
4594 "latino": "_f_latino",
4595 "lean4": "_f_lean",
4596 "lean4markdown": "_f_lean",
4597 "less": "_f_less",
4598 "lex": "_f_lex",
4599 "lilypond": "_f_lilypond",
4600 "lisp": "_f_lisp",
4601 "autolisp": "_f_lisp",
4602 "autolispdcl": "_f_lisp",
4603 "log": "_f_log",
4604 "lolcode": "_f_lolcode",
4605 "lsl": "_f_lsl",
4606 "lua": "_f_lua",
4607 "markdown": "_f_markdown",
4608 "marko": "_f_marko",
4609 "matlab": "_f_matlab",
4610 "maxscript": "_f_maxscript",
4611 "mel": "_f_maya",
4612 "mdx": "_f_mdx",
4613 "mediawiki": "_f_mediawiki",
4614 "mermaid": "_f_mermaid",
4615 "meson": "_f_meson",
4616 "metal": "_f_metal",
4617 "bc-mcfunction": "_f_minecraft",
4618 "bc-minecraft-language": "_f_minecraft",
4619 "bc-minecraft-molang": "_f_minecraft",
4620 "bc-minecraft-project": "_f_minecraft",
4621 "mcdoc": "_f_minecraft",
4622 "mcfunction": "_f_minecraft",
4623 "mcmeta": "_f_minecraft",
4624 "mcscript": "_f_minecraft",
4625 "minecraft-lang": "_f_minecraft",
4626 "options-txt": "_f_minecraft",
4627 "snbt": "_f_minecraft",
4628 "mv": "_f_mivascript",
4629 "mjml": "_f_mjml",
4630 "powerquery": "_f_mlang",
4631 "mojo": "_f_mojo",
4632 "mojolicious": "_f_mojolicious",
4633 "mongo": "_f_mongo",
4634 "mson": "_f_mson",
4635 "mvt": "_f_mvt",
4636 "mvtcss": "_f_mvtcss",
4637 "mvtjs": "_f_mvtjs",
4638 "nearley": "_f_nearly",
4639 "cypher": "_f_neo4j",
4640 "nginx": "_f_nginx",
4641 "nim": "_f_nim",
4642 "nimble": "_f_nimble",
4643 "nix": "_f_nix",
4644 "nsis": "_f_nsi",
4645 "nfl": "_f_nsi",
4646 "nsl": "_f_nsi",
4647 "bridlensis": "_f_nsi",
4648 "nunjucks": "_f_nunjucks",
4649 "nushell": "_f_nushell",
4650 "objective-c": "_f_objectivec",
4651 "objective-cpp": "_f_objectivecpp",
4652 "ocaml": "_f_ocaml",
4653 "ocamllex": "_f_ocaml",
4654 "menhir": "_f_ocaml",
4655 "ogone": "_f_ogone",
4656 "openhab": "_f_openHAB",
4657 "scad": "_f_openscad",
4658 "org": "_f_org",
4659 "pddl": "_f_pddl",
4660 "happenings": "_f_pddl_happenings",
4661 "plan": "_f_pddl_plan",
4662 "perl": "_f_perl",
4663 "perl6": "_f_perl6",
4664 "pgsql": "_f_pgsql",
4665 "php": "_f_php",
4666 "pine": "_f_pine",
4667 "pinescript": "_f_pine",
4668 "pip-requirements": "_f_pip",
4669 "platformio-debug.disassembly": "_f_platformio",
4670 "platformio-debug.memoryview": "_f_platformio",
4671 "plsql": "_f_plsql",
4672 "oracle": "_f_plsql",
4673 "oraclesql": "_f_plsql",
4674 "oracle-sql": "_f_plsql",
4675 "po": "_f_poedit",
4676 "polyglot-notebook": "_f_polyglot",
4677 "polymer": "_f_polymer",
4678 "pony": "_f_pony",
4679 "postcss": "_f_postcss",
4680 "postscript": "_f_postscript",
4681 "powershell": "_f_powershell",
4682 "prisma": "_f_prisma",
4683 "pde": "_f_processinglang",
4684 "abl": "_f_progress",
4685 "prolog": "_f_prolog",
4686 "prometheus": "_f_prometheus",
4687 "proto3": "_f_protobuf",
4688 "proto": "_f_protobuf",
4689 "prototext": "_f_protobuf",
4690 "jade": "_f_pug",
4691 "puppet": "_f_puppet",
4692 "purescript": "_f_purescript",
4693 "pyret": "_f_pyret",
4694 "pyscript": "_f_pyscript",
4695 "python": "_f_python",
4696 "pythowo": "_f_pythowo",
4697 "qlik": "_f_qlikview",
4698 "qml": "_f_qml",
4699 "qsharp": "_f_qsharp",
4700 "r": "_f_r",
4701 "ra_syntax_tree": "_f_ra_syntax_tree",
4702 "racket": "_f_racket",
4703 "raku": "_f_raku",
4704 "raml": "_f_raml",
4705 "razor": "_f_razor",
4706 "aspnetcorerazor": "_f_razor",
4707 "javascriptreact": "_f_reactjs",
4708 "typescriptreact": "_f_reactts",
4709 "reason": "_f_reason",
4710 "red": "_f_red",
4711 "rescript": "_f_rescript",
4712 "restructuredtext": "_f_rest",
4713 "rexx": "_f_rexx",
4714 "riot": "_f_riot",
4715 "ripple": "_f_ripple",
4716 "rmd": "_f_rmd",
4717 "rnc": "_f_rnc",
4718 "robot": "_f_robotframework",
4719 "robots-txt": "_f_robots",
4720 "ruby": "_f_ruby",
4721 "rust": "_f_rust",
4722 "s-lang": "_f_s-lang",
4723 "san": "_f_san",
4724 "SAS": "_f_sas",
4725 "sass": "_f_sass",
4726 "sass.hover": "_f_sass",
4727 "sbt": "_f_sbt",
4728 "scala": "_f_scala",
4729 "scilab": "_f_scilab",
4730 "vbscript": "_f_script",
4731 "scss": "_f_scss",
4732 "sdl": "_f_sdlang",
4733 "search-result": "_f_search_result",
4734 "shaderlab": "_f_shaderlab",
4735 "shellscript": "_f_shell",
4736 "silverstripe": "_f_silverstripe",
4737 "sino": "_f_sino",
4738 "eskip": "_f_skipper",
4739 "slang": "_f_slang",
4740 "slice": "_f_slice",
4741 "slim": "_f_slim",
4742 "slint": "_f_slint",
4743 "sln": "_f_sln",
4744 "smarty": "_f_smarty",
4745 "snakemake": "_f_snakemake",
4746 "snort": "_f_snort",
4747 "solidity": "_f_solidity",
4748 "sparql": "_f_sparql",
4749 "sqf": "_f_sqf",
4750 "sql": "_f_sql",
4751 "squirrel": "_f_squirrel",
4752 "stan": "_f_stan",
4753 "stata": "_f_stata",
4754 "stencil": "_f_stencil",
4755 "stencil-html": "_f_stencil",
4756 "stylable": "_f_stylable",
4757 "source.css.styled": "_f_styled",
4758 "stylus": "_f_stylus",
4759 "svelte": "_f_svelte",
4760 "svg": "_f_svg",
4761 "Swagger": "_f_swagger",
4762 "swagger": "_f_swagger",
4763 "swift": "_f_swift",
4764 "swig": "_f_swig",
4765 "systemd-conf": "_f_systemd",
4766 "systemd-unit-file": "_f_systemd",
4767 "systemverilog": "_f_systemverilog",
4768 "t4": "_f_t4tt",
4769 "tailwindcss": "_f_tailwind",
4770 "teal": "_f_teal",
4771 "templ": "_f_templ",
4772 "tera": "_f_tera",
4773 "terraform": "_f_terraform",
4774 "tex": "_f_tex",
4775 "latex": "_f_tex",
4776 "bibtex": "_f_tex",
4777 "doctex": "_f_tex",
4778 "csv": "_f_text",
4779 "csv (pipe)": "_f_text",
4780 "csv (semicolon)": "_f_text",
4781 "csv (whitespace)": "_f_text",
4782 "dynamic csv": "_f_text",
4783 "tsv": "_f_text",
4784 "plaintext": "_f_text",
4785 "vim-help": "_f_text",
4786 "textile": "_f_textile",
4787 "tiltfile": "_f_tiltfile",
4788 "toit": "_f_toit",
4789 "toml": "_f_toml",
4790 "toon": "_f_toon",
4791 "tt": "_f_tt",
4792 "ttcn": "_f_ttcn",
4793 "tuc": "_f_tuc",
4794 "twig": "_f_twig",
4795 "typescript": "_f_typescript",
4796 "typoscript": "_f_typo3",
4797 "uiua": "_f_uiua",
4798 "unison": "_f_unison",
4799 "vb": "_f_vb",
4800 "vba": "_f_vba",
4801 "velocity": "_f_velocity",
4802 "vento": "_f_vento",
4803 "verilog": "_f_verilog",
4804 "vhdl": "_f_vhdl",
4805 "viml": "_f_vim",
4806 "vim-snippet": "_f_vim",
4807 "vitest-snapshot": "_f_vitest",
4808 "v": "_f_vlang",
4809 "volt": "_f_volt",
4810 "vroom": "_f_vroom",
4811 "snippets": "_f_vscode",
4812 "vue": "_f_vue",
4813 "vyper": "_f_vyper",
4814 "wasm": "_f_wasm",
4815 "wat": "_f_wasm",
4816 "wenyan": "_f_wenyan",
4817 "wgsl": "_f_wgsl",
4818 "wikitext": "_f_wikitext",
4819 "wai": "_f_wit",
4820 "wolfram": "_f_wolfram",
4821 "wurstlang": "_f_wurst",
4822 "wurst": "_f_wurst",
4823 "xmake": "_f_xmake",
4824 "xml": "_f_xml",
4825 "xcompose": "_f_xorg",
4826 "xquery": "_f_xquery",
4827 "xsl": "_f_xsl",
4828 "xslt": "_f_xsl",
4829 "yacc": "_f_yacc",
4830 "yaml": "_f_yaml",
4831 "yaml-tmlanguage": "_f_yaml",
4832 "spring-boot-properties-yaml": "_f_yaml",
4833 "yang": "_f_yang",
4834 "yarnlock": "_f_yarn",
4835 "zig": "_f_zig",
4836 "zip": "_f_zip"
4837 },
4838 "light": {
4839 "file": "_file_light",
4840 "folder": "_folder_light",
4841 "folderExpanded": "_folder_light_open",
4842 "rootFolder": "_root_folder_light",
4843 "rootFolderExpanded": "_root_folder_light_open",
4844 "folderNames": {
4845 "android": "_fd_android",
4846 ".angular": "_fd_angular",
4847 "apache": "_fd_apache",
4848 "apache2": "_fd_apache",
4849 "httpd": "_fd_apache",
4850 "api": "_fd_api",
4851 ".api": "_fd_api",
4852 "apis": "_fd_api",
4853 ".apis": "_fd_api",
4854 "app": "_fd_app",
4855 "apps": "_fd_app",
4856 ".app": "_fd_app",
4857 "application": "_fd_app",
4858 "applications": "_fd_app",
4859 "arangodb": "_fd_arangodb",
4860 "arango": "_fd_arangodb",
4861 "assets": "_fd_asset",
4862 ".assets": "_fd_asset",
4863 ".astro": "_fd_astro",
4864 "audio": "_fd_audio",
4865 ".audio": "_fd_audio",
4866 "audios": "_fd_audio",
4867 ".audios": "_fd_audio",
4868 "sound": "_fd_audio",
4869 ".sound": "_fd_audio",
4870 "sounds": "_fd_audio",
4871 ".sounds": "_fd_audio",
4872 "aurelia_project": "_fd_aurelia",
4873 "aws": "_fd_aws",
4874 ".aws": "_fd_aws",
4875 "azure": "_fd_azure",
4876 ".azure": "_fd_azure",
4877 "azure-pipelines": "_fd_azurepipelines",
4878 ".azure-pipelines": "_fd_azurepipelines",
4879 "bin": "_fd_binary",
4880 ".bin": "_fd_binary",
4881 "blocs": "_fd_bloc",
4882 "bloc": "_fd_bloc",
4883 "blueprint": "_fd_blueprint",
4884 ".blueprint": "_fd_blueprint",
4885 "blueprints": "_fd_blueprint",
4886 ".blueprints": "_fd_blueprint",
4887 "bot": "_fd_bot",
4888 ".bot": "_fd_bot",
4889 "bower_components": "_fd_bower",
4890 ".buildkite": "_fd_buildkite",
4891 "cake": "_fd_cake",
4892 ".cake": "_fd_cake",
4893 "cargo": "_fd_cargo",
4894 ".cargo": "_fd_cargo",
4895 "crates": "_fd_cargo",
4896 "certificates": "_fd_certificate",
4897 ".certificates": "_fd_certificate",
4898 "certs": "_fd_certificate",
4899 "certs.": "_fd_certificate",
4900 ".changeset": "_fd_changesets",
4901 "chef": "_fd_chef",
4902 ".chef": "_fd_chef",
4903 ".circleci": "_fd_circleci",
4904 ".claude": "_fd_claude",
4905 "cli": "_fd_cli",
4906 "cmd": "_fd_cli",
4907 "command": "_fd_cli",
4908 "commands": "_fd_cli",
4909 "commandline": "_fd_cli",
4910 "console": "_fd_cli",
4911 "client": "_fd_client",
4912 "clients": "_fd_client",
4913 ".cloudflare": "_fd_cloudflare",
4914 "cloudflare": "_fd_cloudflare",
4915 ".cmake": "_fd_cmake",
4916 "cmake": "_fd_cmake",
4917 "common": "_fd_common",
4918 "component": "_fd_component",
4919 "components": "_fd_component",
4920 ".components": "_fd_component",
4921 "gui": "_fd_component",
4922 "src-ui": "_fd_component",
4923 "ui": "_fd_component",
4924 "widgets": "_fd_component",
4925 "composer": "_fd_composer",
4926 ".composer": "_fd_composer",
4927 "conf": "_fd_config",
4928 ".conf": "_fd_config",
4929 "config": "_fd_config",
4930 ".config": "_fd_config",
4931 "configs": "_fd_config",
4932 ".configs": "_fd_config",
4933 "configuration": "_fd_config",
4934 ".configuration": "_fd_config",
4935 "configurations": "_fd_config",
4936 ".configurations": "_fd_config",
4937 "setting": "_fd_config",
4938 ".setting": "_fd_config",
4939 "settings": "_fd_config",
4940 ".settings": "_fd_config",
4941 "ini": "_fd_config",
4942 ".ini": "_fd_config",
4943 "initializers": "_fd_config",
4944 ".initializers": "_fd_config",
4945 "__controller__": "_fd_controller",
4946 "__controllers__": "_fd_controller",
4947 "controller": "_fd_controller",
4948 "controllers": "_fd_controller",
4949 ".controllers": "_fd_controller",
4950 "__handler__": "_fd_controller",
4951 "__handlers__": "_fd_controller",
4952 "handler": "_fd_controller",
4953 ".handler": "_fd_controller",
4954 "handlers": "_fd_controller",
4955 ".handlers": "_fd_controller",
4956 "coverage": "_fd_coverage",
4957 "css": "_fd_css",
4958 "_css": "_fd_css",
4959 "cubits": "_fd_cubit",
4960 "cubit": "_fd_cubit",
4961 ".cursor": "_fd_light_cursor",
4962 "cypress": "_fd_light_cypress",
4963 ".dapr": "_fd_dapr",
4964 "dapr": "_fd_dapr",
4965 "dart": "_fd_dart",
4966 ".dart_tool": "_fd_dart",
4967 "datadog": "_fd_datadog",
4968 ".datadog": "_fd_datadog",
4969 "db": "_fd_db",
4970 "database": "_fd_db",
4971 "sql": "_fd_db",
4972 "data": "_fd_db",
4973 "repo": "_fd_db",
4974 "repository": "_fd_db",
4975 "repositories": "_fd_db",
4976 "store": "_fd_db",
4977 "stores": "_fd_db",
4978 "debian": "_fd_debian",
4979 "deb": "_fd_debian",
4980 ".dependabot": "_fd_dependabot",
4981 ".devcontainer": "_fd_devcontainer",
4982 "dist": "_fd_dist",
4983 ".dist": "_fd_dist",
4984 "dists": "_fd_dist",
4985 "out": "_fd_dist",
4986 "outs": "_fd_dist",
4987 "export": "_fd_dist",
4988 "exports": "_fd_dist",
4989 "build": "_fd_dist",
4990 ".build": "_fd_dist",
4991 "builds": "_fd_dist",
4992 "release": "_fd_dist",
4993 "releases": "_fd_dist",
4994 "target": "_fd_dist",
4995 "targets": "_fd_dist",
4996 "docker": "_fd_docker",
4997 ".docker": "_fd_docker",
4998 "docs": "_fd_docs",
4999 "doc": "_fd_docs",

3352 more lines aren’t shown here.

apps/web/src/vscode-icons.test.ts+0−52
@@ -1,52 +0,0 @@
1import { assert, describe, it } from "vite-plus/test";
2
3import { getVscodeIconUrlForEntry } from "./vscode-icons";
4
5describe("getVscodeIconUrlForEntry", () => {
6 it("uses exact filename matches from the vscode-icons manifest", () => {
7 const darkUrl = getVscodeIconUrlForEntry("pnpm-workspace.yaml", "file", "dark");
8 const lightUrl = getVscodeIconUrlForEntry("pnpm-workspace.yaml", "file", "light");
9
10 assert.isTrue(darkUrl.endsWith("/file_type_pnpm.svg"));
11 assert.isTrue(lightUrl.endsWith("/file_type_light_pnpm.svg"));
12 });
13
14 it("uses longest extension match for compound extensions", () => {
15 const iconUrl = getVscodeIconUrlForEntry("tsconfig.tsbuildinfo", "file", "dark");
16 const manifestStyleUrl = getVscodeIconUrlForEntry("buf.yaml", "file", "dark");
17 assert.isTrue(iconUrl.endsWith("/file_type_tsbuildinfo.svg"));
18 assert.isTrue(manifestStyleUrl.endsWith("/file_type_buf.svg"));
19 });
20
21 it("uses folder mappings and light-aware filename mappings", () => {
22 const folderUrl = getVscodeIconUrlForEntry("packages/src", "directory", "light");
23 const fileUrl = getVscodeIconUrlForEntry("AGENTS.md", "file", "light");
24
25 assert.isTrue(folderUrl.endsWith("/folder_type_src.svg"));
26 assert.isTrue(fileUrl.endsWith("/file_type_light_agents.svg"));
27 });
28
29 it("falls back to language-based mappings for path-only cases", () => {
30 const tsxUrl = getVscodeIconUrlForEntry("checkbox.tsx", "file", "light");
31 const dockerfileUrl = getVscodeIconUrlForEntry("Dockerfile", "file", "dark");
32 const shellUrl = getVscodeIconUrlForEntry("entrypoint.sh", "file", "dark");
33 const htmlUrl = getVscodeIconUrlForEntry("index.html", "file", "dark");
34 const cursorRulesUrl = getVscodeIconUrlForEntry("general.mdc", "file", "dark");
35 const githubWorkflowUrl = getVscodeIconUrlForEntry(".github/workflows/ci.yml", "file", "light");
36
37 assert.isTrue(tsxUrl.endsWith("/file_type_reactts.svg"));
38 assert.isTrue(dockerfileUrl.endsWith("/file_type_docker.svg"));
39 assert.isTrue(shellUrl.endsWith("/file_type_shell.svg"));
40 assert.isTrue(htmlUrl.endsWith("/file_type_html.svg"));
41 assert.isTrue(cursorRulesUrl.endsWith("/file_type_markdown.svg"));
42 assert.isTrue(githubWorkflowUrl.endsWith("/file_type_light_yaml.svg"));
43 });
44
45 it("falls back to defaults when there is no match", () => {
46 const fileUrl = getVscodeIconUrlForEntry("foo.unknown-ext", "file", "dark");
47 const folderUrl = getVscodeIconUrlForEntry("totally-unknown-folder", "directory", "dark");
48
49 assert.isTrue(fileUrl.endsWith("/default_file.svg"));
50 assert.isTrue(folderUrl.endsWith("/default_folder.svg"));
51 });
52});
apps/web/src/vscode-icons.ts+0−211
@@ -1,211 +0,0 @@
1import vscodeIconsManifest from "./vscode-icons-manifest.json";
2import languageAssociationsData from "./vscode-icons-language-associations.json";
3
4const VSCODE_ICONS_VERSION = "v12.17.0";
5const VSCODE_ICONS_BASE_URL = `https://cdn.jsdelivr.net/gh/vscode-icons/vscode-icons@${VSCODE_ICONS_VERSION}/icons`;
6
7interface IconDefinition {
8 iconPath: string;
9}
10
11interface IconLookupSection {
12 file?: string;
13 folder?: string;
14 fileNames: Record<string, string>;
15 fileExtensions: Record<string, string>;
16 folderNames: Record<string, string>;
17 languageIds?: Record<string, string>;
18}
19
20interface VscodeIconsManifest extends IconLookupSection {
21 iconDefinitions: Record<string, IconDefinition>;
22 light: IconLookupSection;
23}
24
25interface LanguageAssociations {
26 version: string;
27 extensionToLanguageId: Record<string, string>;
28 fileNameToLanguageId: Record<string, string>;
29}
30
31const manifest = vscodeIconsManifest as VscodeIconsManifest;
32const languageAssociations = languageAssociationsData as LanguageAssociations;
33const iconDefinitions = manifest.iconDefinitions;
34
35const darkFileNames = toLowercaseLookup(manifest.fileNames);
36const lightFileNames = toLowercaseLookup(manifest.light.fileNames);
37const darkFileExtensions = toLowercaseLookup(manifest.fileExtensions);
38const lightFileExtensions = toLowercaseLookup(manifest.light.fileExtensions);
39const darkFolderNames = toLowercaseLookup(manifest.folderNames);
40const lightFolderNames = toLowercaseLookup(manifest.light.folderNames);
41const darkLanguageIds = toLowercaseLookup(manifest.languageIds ?? {});
42const lightLanguageIds = toLowercaseLookup(manifest.light.languageIds ?? {});
43const languageIdByExtension = toLowercaseLookup(languageAssociations.extensionToLanguageId);
44const languageIdByFileName = toLowercaseLookup(languageAssociations.fileNameToLanguageId);
45const localLanguageIdByExtensionOverrides = {
46 // Cursor rules files (*.mdc) are commonly treated as markdown in VSCode/Cursor.
47 mdc: "markdown",
48 // Upstream languages.ts currently maps .html to django-html before html.
49 // Prefer the base HTML icon for standalone HTML files.
50 html: "html",
51 // Upstream languages.ts maps yml/yaml to specialized language ids that can produce
52 // non-generic YAML icons (for example cloudfoundry/esphome). Prefer the base YAML icon
53 // unless a more specific basename/extension match (e.g. azure-pipelines.yml) is found.
54 yml: "yaml",
55 yaml: "yaml",
56} as const;
57
58const defaultDarkFileIconDefinition = manifest.file ?? "_file";
59const defaultLightFileIconDefinition = manifest.light.file ?? defaultDarkFileIconDefinition;
60const defaultDarkFolderIconDefinition = manifest.folder ?? "_folder";
61const defaultLightFolderIconDefinition = manifest.light.folder ?? defaultDarkFolderIconDefinition;
62
63function toLowercaseLookup(source: Record<string, string>): Record<string, string> {
64 const entries = Object.entries(source);
65 const lookup: Record<string, string> = {};
66 for (const [key, value] of entries) {
67 lookup[key.toLowerCase()] = value;
68 }
69 return lookup;
70}
71
72const extensionByLanguageId: Record<string, string> = {};
73for (const [extension, languageId] of Object.entries(languageAssociations.extensionToLanguageId)) {
74 const key = languageId.toLowerCase();
75 if (!(key in extensionByLanguageId)) {
76 extensionByLanguageId[key] = extension.toLowerCase();
77 }
78}
79
80/**
81 * Synthesizes a file name whose icon resolution best represents a markdown
82 * fence language id — handles both extension-style ids ("ts", "py") and
83 * language ids ("typescript", "python", "shellscript").
84 */
85export function syntheticFileNameForLanguageId(languageId: string): string {
86 const normalized = languageId.toLowerCase();
87 return `file.${extensionByLanguageId[normalized] ?? normalized}`;
88}
89
90export function basenameOfPath(pathValue: string): string {
91 const slashIndex = pathValue.lastIndexOf("/");
92 if (slashIndex === -1) return pathValue;
93 return pathValue.slice(slashIndex + 1);
94}
95
96export function inferEntryKindFromPath(pathValue: string): "file" | "directory" {
97 const base = basenameOfPath(pathValue);
98 if (base.startsWith(".") && !base.slice(1).includes(".")) {
99 return "directory";
100 }
101 if (base.includes(".")) {
102 return "file";
103 }
104 return "directory";
105}
106
107function extensionCandidates(fileName: string): string[] {
108 const candidates = new Set<string>();
109 if (fileName.includes(".")) {
110 candidates.add(fileName);
111 }
112 let dotIndex = fileName.indexOf(".");
113 while (dotIndex !== -1 && dotIndex < fileName.length - 1) {
114 const candidate = fileName.slice(dotIndex + 1);
115 if (candidate.length > 0) {
116 candidates.add(candidate);
117 }
118 dotIndex = fileName.indexOf(".", dotIndex + 1);
119 }
120 return [...candidates];
121}
122
123function resolveLanguageFallbackDefinition(
124 pathValue: string,
125 theme: "light" | "dark",
126): string | null {
127 const basename = basenameOfPath(pathValue).toLowerCase();
128 const languageIds = theme === "light" ? lightLanguageIds : darkLanguageIds;
129
130 const fromBasenameLanguage = languageIdByFileName[basename];
131 if (fromBasenameLanguage) {
132 return languageIds[fromBasenameLanguage] ?? darkLanguageIds[fromBasenameLanguage] ?? null;
133 }
134
135 for (const candidate of extensionCandidates(basename)) {
136 const languageId =
137 localLanguageIdByExtensionOverrides[
138 candidate as keyof typeof localLanguageIdByExtensionOverrides
139 ] ?? languageIdByExtension[candidate];
140 if (!languageId) continue;
141 return languageIds[languageId] ?? darkLanguageIds[languageId] ?? null;
142 }
143
144 return null;
145}
146
147function iconFilenameForDefinitionKey(definitionKey: string | undefined): string | null {
148 if (!definitionKey) return null;
149 const iconPath = iconDefinitions[definitionKey]?.iconPath;
150 if (!iconPath) return null;
151 const slashIndex = iconPath.lastIndexOf("/");
152 if (slashIndex === -1) {
153 return iconPath;
154 }
155 return iconPath.slice(slashIndex + 1);
156}
157
158function resolveFileDefinition(pathValue: string, theme: "light" | "dark"): string {
159 const basename = basenameOfPath(pathValue).toLowerCase();
160 const fileNames = theme === "light" ? lightFileNames : darkFileNames;
161 const fileExtensions = theme === "light" ? lightFileExtensions : darkFileExtensions;
162
163 const fromFileName = fileNames[basename] ?? darkFileNames[basename];
164 if (fromFileName) return fromFileName;
165
166 for (const candidate of extensionCandidates(basename)) {
167 const fromExtension = fileExtensions[candidate] ?? darkFileExtensions[candidate];
168 if (fromExtension) return fromExtension;
169 }
170
171 const fromLanguage = resolveLanguageFallbackDefinition(pathValue, theme);
172 if (fromLanguage) return fromLanguage;
173
174 return theme === "light" ? defaultLightFileIconDefinition : defaultDarkFileIconDefinition;
175}
176
177function resolveFolderDefinition(pathValue: string, theme: "light" | "dark"): string {
178 const basename = basenameOfPath(pathValue).toLowerCase();
179 const folderNames = theme === "light" ? lightFolderNames : darkFolderNames;
180 return (
181 folderNames[basename] ??
182 darkFolderNames[basename] ??
183 (theme === "light" ? defaultLightFolderIconDefinition : defaultDarkFolderIconDefinition)
184 );
185}
186
187/** True when the file name resolves to a real icon rather than the generic default. */
188export function hasSpecificVscodeIconForFileName(
189 fileName: string,
190 theme: "light" | "dark",
191): boolean {
192 const definition = resolveFileDefinition(fileName, theme);
193 return (
194 definition !== defaultDarkFileIconDefinition && definition !== defaultLightFileIconDefinition
195 );
196}
197
198export function getVscodeIconUrlForEntry(
199 pathValue: string,
200 kind: "file" | "directory",
201 theme: "light" | "dark",
202): string {
203 const definitionKey =
204 kind === "directory"
205 ? resolveFolderDefinition(pathValue, theme)
206 : resolveFileDefinition(pathValue, theme);
207 const iconFilename =
208 iconFilenameForDefinitionKey(definitionKey) ??
209 (kind === "directory" ? "default_folder.svg" : "default_file.svg");
210 return `${VSCODE_ICONS_BASE_URL}/${iconFilename}`;
211}
apps/web/vite.config.ts+1−0
@@ -121,6 +121,7 @@ export default defineConfig(() => { · 120 unmodified lines
121 optimizeDeps: {121 optimizeDeps: {
122 include: [122 include: [
123 "@pierre/diffs",123 "@pierre/diffs",
124 "@pierre/diffs/editor",
124 "@pierre/diffs/react",125 "@pierre/diffs/react",
125 "@pierre/diffs/worker/worker.js",126 "@pierre/diffs/worker/worker.js",
126 "effect/Array",127 "effect/Array",
package.json+0−1
@@ -35,7 +35,6 @@ · 34 unmodified lines
35 "dist:desktop:win:x64": "node scripts/build-desktop-artifact.ts --platform win --target nsis --arch x64",35 "dist:desktop:win:x64": "node scripts/build-desktop-artifact.ts --platform win --target nsis --arch x64",
36 "release:smoke": "node scripts/release-smoke.ts",36 "release:smoke": "node scripts/release-smoke.ts",
37 "clean": "rm -rf node_modules apps/*/node_modules packages/*/node_modules apps/*/dist apps/*/dist-electron packages/*/dist .vite-plus apps/*/.vite-plus packages/*/.vite-plus",37 "clean": "rm -rf node_modules apps/*/node_modules packages/*/node_modules apps/*/dist apps/*/dist-electron packages/*/dist .vite-plus apps/*/.vite-plus packages/*/.vite-plus",
38 "sync:vscode-icons": "node scripts/sync-vscode-icons.mjs",
39 "sync:repos": "node scripts/sync-reference-repos.ts"38 "sync:repos": "node scripts/sync-reference-repos.ts"
40 },39 },
41 "devDependencies": {40 "devDependencies": {
packages/client-runtime/src/wsRpcClient.ts+6−0
@@ -96,6 +96,8 @@ export interface WsRpcClient { · 95 unmodified lines
96 readonly subscribePorts: RpcStreamMethod<typeof WS_METHODS.subscribeDiscoveredLocalServers>;96 readonly subscribePorts: RpcStreamMethod<typeof WS_METHODS.subscribeDiscoveredLocalServers>;
97 };97 };
98 readonly projects: {98 readonly projects: {
99 readonly listEntries: RpcUnaryMethod<typeof WS_METHODS.projectsListEntries>;
100 readonly readFile: RpcUnaryMethod<typeof WS_METHODS.projectsReadFile>;
99 readonly searchEntries: RpcUnaryMethod<typeof WS_METHODS.projectsSearchEntries>;101 readonly searchEntries: RpcUnaryMethod<typeof WS_METHODS.projectsSearchEntries>;
100 readonly writeFile: RpcUnaryMethod<typeof WS_METHODS.projectsWriteFile>;102 readonly writeFile: RpcUnaryMethod<typeof WS_METHODS.projectsWriteFile>;
101 };103 };
@@ -267,6 +269,10 @@ export function createWsRpcClient( · 165 unmodified lines
267 ),269 ),
268 },270 },
269 projects: {271 projects: {
272 listEntries: (input) =>
273 transport.request((client) => client[WS_METHODS.projectsListEntries](input)),
274 readFile: (input) =>
275 transport.request((client) => client[WS_METHODS.projectsReadFile](input)),
270 searchEntries: (input) =>276 searchEntries: (input) =>
271 transport.request((client) => client[WS_METHODS.projectsSearchEntries](input)),277 transport.request((client) => client[WS_METHODS.projectsSearchEntries](input)),
272 writeFile: (input) =>278 writeFile: (input) =>
packages/contracts/src/ipc.ts+6−0
@@ -22,6 +22,10 @@ import type { ReviewDiffPreviewInput, ReviewDiffPreviewResult } from "./review.t · 21 unmodified lines
22import type { FilesystemBrowseInput, FilesystemBrowseResult } from "./filesystem.ts";22import type { FilesystemBrowseInput, FilesystemBrowseResult } from "./filesystem.ts";
23import type { AssetCreateUrlInput, AssetCreateUrlResult } from "./assets.ts";23import type { AssetCreateUrlInput, AssetCreateUrlResult } from "./assets.ts";
24import type {24import type {
25 ProjectListEntriesInput,
26 ProjectListEntriesResult,
27 ProjectReadFileInput,
28 ProjectReadFileResult,
25 ProjectSearchEntriesInput,29 ProjectSearchEntriesInput,
26 ProjectSearchEntriesResult,30 ProjectSearchEntriesResult,
27 ProjectWriteFileInput,31 ProjectWriteFileInput,
@@ -1110,6 +1114,8 @@ export interface EnvironmentApi { · 1082 unmodified lines
1110 ) => () => void;1114 ) => () => void;
1111 };1115 };
1112 projects: {1116 projects: {
1117 listEntries: (input: ProjectListEntriesInput) => Promise<ProjectListEntriesResult>;
1118 readFile: (input: ProjectReadFileInput) => Promise<ProjectReadFileResult>;
1113 searchEntries: (input: ProjectSearchEntriesInput) => Promise<ProjectSearchEntriesResult>;1119 searchEntries: (input: ProjectSearchEntriesInput) => Promise<ProjectSearchEntriesResult>;
1114 writeFile: (input: ProjectWriteFileInput) => Promise<ProjectWriteFileResult>;1120 writeFile: (input: ProjectWriteFileInput) => Promise<ProjectWriteFileResult>;
1115 };1121 };
packages/contracts/src/project.ts+43−1
@@ -1,8 +1,9 @@
1import * as Schema from "effect/Schema";1import * as Schema from "effect/Schema";
2import { PositiveInt, TrimmedNonEmptyString } from "./baseSchemas.ts";2import { NonNegativeInt, PositiveInt, TrimmedNonEmptyString } from "./baseSchemas.ts";
3 3
4const PROJECT_SEARCH_ENTRIES_MAX_LIMIT = 200;4const PROJECT_SEARCH_ENTRIES_MAX_LIMIT = 200;
5const PROJECT_WRITE_FILE_PATH_MAX_LENGTH = 512;5const PROJECT_WRITE_FILE_PATH_MAX_LENGTH = 512;
6const PROJECT_READ_FILE_PATH_MAX_LENGTH = 512;
6 7
7export const ProjectSearchEntriesInput = Schema.Struct({8export const ProjectSearchEntriesInput = Schema.Struct({
8 cwd: TrimmedNonEmptyString,9 cwd: TrimmedNonEmptyString,
@@ -26,6 +27,17 @@ export const ProjectSearchEntriesResult = Schema.Struct({ · 17 unmodified lines
26});27});
27export type ProjectSearchEntriesResult = typeof ProjectSearchEntriesResult.Type;28export type ProjectSearchEntriesResult = typeof ProjectSearchEntriesResult.Type;
28 29
30export const ProjectListEntriesInput = Schema.Struct({
31 cwd: TrimmedNonEmptyString,
32});
33export type ProjectListEntriesInput = typeof ProjectListEntriesInput.Type;
34
35export const ProjectListEntriesResult = Schema.Struct({
36 entries: Schema.Array(ProjectEntry),
37 truncated: Schema.Boolean,
38});
39export type ProjectListEntriesResult = typeof ProjectListEntriesResult.Type;
40
29export class ProjectSearchEntriesError extends Schema.TaggedErrorClass<ProjectSearchEntriesError>()(41export class ProjectSearchEntriesError extends Schema.TaggedErrorClass<ProjectSearchEntriesError>()(
30 "ProjectSearchEntriesError",42 "ProjectSearchEntriesError",
31 {43 {
@@ -34,6 +46,36 @@ export class ProjectSearchEntriesError extends Schema.TaggedErrorClass<ProjectSe · 2 unmodified lines
34 },46 },
35) {}47) {}
36 48
49export class ProjectListEntriesError extends Schema.TaggedErrorClass<ProjectListEntriesError>()(
50 "ProjectListEntriesError",
51 {
52 message: TrimmedNonEmptyString,
53 cause: Schema.optional(Schema.Defect()),
54 },
55) {}
56
57export const ProjectReadFileInput = Schema.Struct({
58 cwd: TrimmedNonEmptyString,
59 relativePath: TrimmedNonEmptyString.check(Schema.isMaxLength(PROJECT_READ_FILE_PATH_MAX_LENGTH)),
60});
61export type ProjectReadFileInput = typeof ProjectReadFileInput.Type;
62
63export const ProjectReadFileResult = Schema.Struct({
64 relativePath: TrimmedNonEmptyString,
65 contents: Schema.String,
66 byteLength: NonNegativeInt,
67 truncated: Schema.Boolean,
68});
69export type ProjectReadFileResult = typeof ProjectReadFileResult.Type;
70
71export class ProjectReadFileError extends Schema.TaggedErrorClass<ProjectReadFileError>()(
72 "ProjectReadFileError",
73 {
74 message: TrimmedNonEmptyString,
75 cause: Schema.optional(Schema.Defect()),
76 },
77) {}
78
37export const ProjectWriteFileInput = Schema.Struct({79export const ProjectWriteFileInput = Schema.Struct({
38 cwd: TrimmedNonEmptyString,80 cwd: TrimmedNonEmptyString,
39 relativePath: TrimmedNonEmptyString.check(Schema.isMaxLength(PROJECT_WRITE_FILE_PATH_MAX_LENGTH)),81 relativePath: TrimmedNonEmptyString.check(Schema.isMaxLength(PROJECT_WRITE_FILE_PATH_MAX_LENGTH)),
packages/contracts/src/rpc.ts+22−0
@@ -65,6 +65,12 @@ import { · 64 unmodified lines
65 RelayClientStatusSchema,65 RelayClientStatusSchema,
66} from "./relayClient.ts";66} from "./relayClient.ts";
67import {67import {
68 ProjectListEntriesError,
69 ProjectListEntriesInput,
70 ProjectListEntriesResult,
71 ProjectReadFileError,
72 ProjectReadFileInput,
73 ProjectReadFileResult,
68 ProjectSearchEntriesError,74 ProjectSearchEntriesError,
69 ProjectSearchEntriesInput,75 ProjectSearchEntriesInput,
70 ProjectSearchEntriesResult,76 ProjectSearchEntriesResult,
@@ -141,6 +147,8 @@ export const WS_METHODS = { · 70 unmodified lines
141 projectsList: "projects.list",147 projectsList: "projects.list",
142 projectsAdd: "projects.add",148 projectsAdd: "projects.add",
143 projectsRemove: "projects.remove",149 projectsRemove: "projects.remove",
150 projectsListEntries: "projects.listEntries",
151 projectsReadFile: "projects.readFile",
144 projectsSearchEntries: "projects.searchEntries",152 projectsSearchEntries: "projects.searchEntries",
145 projectsWriteFile: "projects.writeFile",153 projectsWriteFile: "projects.writeFile",
146 154
@@ -350,6 +358,18 @@ export const WsProjectsSearchEntriesRpc = Rpc.make(WS_METHODS.projectsSearchEntr · 203 unmodified lines
350 error: Schema.Union([ProjectSearchEntriesError, EnvironmentAuthorizationError]),358 error: Schema.Union([ProjectSearchEntriesError, EnvironmentAuthorizationError]),
351});359});
352 360
361export const WsProjectsListEntriesRpc = Rpc.make(WS_METHODS.projectsListEntries, {
362 payload: ProjectListEntriesInput,
363 success: ProjectListEntriesResult,
364 error: Schema.Union([ProjectListEntriesError, EnvironmentAuthorizationError]),
365});
366
367export const WsProjectsReadFileRpc = Rpc.make(WS_METHODS.projectsReadFile, {
368 payload: ProjectReadFileInput,
369 success: ProjectReadFileResult,
370 error: Schema.Union([ProjectReadFileError, EnvironmentAuthorizationError]),
371});
372
353export const WsProjectsWriteFileRpc = Rpc.make(WS_METHODS.projectsWriteFile, {373export const WsProjectsWriteFileRpc = Rpc.make(WS_METHODS.projectsWriteFile, {
354 payload: ProjectWriteFileInput,374 payload: ProjectWriteFileInput,
355 success: ProjectWriteFileResult,375 success: ProjectWriteFileResult,
@@ -676,6 +696,8 @@ export const WsRpcGroup = RpcGroup.make( · 320 unmodified lines
676 WsSourceControlLookupRepositoryRpc,696 WsSourceControlLookupRepositoryRpc,
677 WsSourceControlCloneRepositoryRpc,697 WsSourceControlCloneRepositoryRpc,
678 WsSourceControlPublishRepositoryRpc,698 WsSourceControlPublishRepositoryRpc,
699 WsProjectsListEntriesRpc,
700 WsProjectsReadFileRpc,
679 WsProjectsSearchEntriesRpc,701 WsProjectsSearchEntriesRpc,
680 WsProjectsWriteFileRpc,702 WsProjectsWriteFileRpc,
681 WsShellOpenInEditorRpc,703 WsShellOpenInEditorRpc,
patches/@pierre%2Fdiffs@1.3.0-beta.4.patchrenamed from patches/@pierre%2Fdiffs@1.1.20.patch+2−2
@@ -1,5 +1,5 @@
1diff --git a/package.json b/package.json1diff --git a/package.json b/package.json
2index c0e762b13b56247b256520754e439cbc16bb797d..d298b87c175ec891d1b50f681c9031f4b1929e67 1006442index b18c94f3f7b40bdb15084ab7cabcb19dd29c61f7..ab96ac589d368e806ef45480283d6e0fa199117f 100644
3--- a/package.json3--- a/package.json
4+++ b/package.json4+++ b/package.json
5@@ -51,6 +51,18 @@5@@ -51,6 +51,18 @@
@@ -20,4 +20,4 @@ index c0e762b13b56247b256520754e439cbc16bb797d..d298b87c175ec891d1b50f681c9031f4 · 14 unmodified lines
20+ "import": "./dist/utils/parsePatchFiles.js"20+ "import": "./dist/utils/parsePatchFiles.js"
21 }21 }
22 },22 },
23 "scripts": {23 "publishConfig": {
pnpm-lock.yaml+89−17
@@ -19,8 +19,8 @@ catalogs: · 18 unmodified lines
19 specifier: 1.8.019 specifier: 1.8.0
20 version: 1.8.020 version: 1.8.0
21 '@pierre/diffs':21 '@pierre/diffs':
22 specifier: 1.1.2022 specifier: 1.3.0-beta.4
23 version: 1.1.2023 version: 1.3.0-beta.4
24 '@typescript/native-preview':24 '@typescript/native-preview':
25 specifier: 7.0.0-dev.20260604.125 specifier: 7.0.0-dev.20260604.1
26 version: 7.0.0-dev.20260604.126 version: 7.0.0-dev.20260604.1
@@ -66,9 +66,9 @@ patchedDependencies: · 39 unmodified lines
66 '@expo/metro-config@56.0.13':66 '@expo/metro-config@56.0.13':
67 hash: 8cb08b5bb7051ed9d2dbe46a2c293c5a1e17f1bd6ddf30de27909e18c921ff4667 hash: 8cb08b5bb7051ed9d2dbe46a2c293c5a1e17f1bd6ddf30de27909e18c921ff46
68 path: patches/@expo%2Fmetro-config@56.0.13.patch68 path: patches/@expo%2Fmetro-config@56.0.13.patch
69 '@pierre/diffs@1.1.20':69 '@pierre/diffs@1.3.0-beta.4':
70 hash: e4e35ba95100de3708f900e0d9ea62bca732b1e4486024b5055f48cd128dd9e070 hash: f5d41705ce94bbafc731d92e9cb1671db710df046dd135cb894e3e3e9164a75b
71 path: patches/@pierre%2Fdiffs@1.1.20.patch71 path: patches/@pierre%2Fdiffs@1.3.0-beta.4.patch
72 effect@4.0.0-beta.78:72 effect@4.0.0-beta.78:
73 hash: 883249d8efbb462e928e21fefef96027b66aec50751178cafdce45f08eee375473 hash: 883249d8efbb462e928e21fefef96027b66aec50751178cafdce45f08eee3754
74 path: patches/effect@4.0.0-beta.78.patch74 path: patches/effect@4.0.0-beta.78.patch
@@ -198,7 +198,7 @@ importers: · 123 unmodified lines
198 version: 1.8.0198 version: 1.8.0
199 '@pierre/diffs':199 '@pierre/diffs':
200 specifier: 'catalog:'200 specifier: 'catalog:'
201 version: 1.1.20(patch_hash=e4e35ba95100de3708f900e0d9ea62bca732b1e4486024b5055f48cd128dd9e0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)201 version: 1.3.0-beta.4(patch_hash=f5d41705ce94bbafc731d92e9cb1671db710df046dd135cb894e3e3e9164a75b)(@shikijs/themes@3.23.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
202 '@react-native-menu/menu':202 '@react-native-menu/menu':
203 specifier: ^2.0.0203 specifier: ^2.0.0
204 version: 2.0.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)204 version: 2.0.0(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
@@ -400,7 +400,7 @@ importers: · 195 unmodified lines
400 version: 1.15.13400 version: 1.15.13
401 '@pierre/diffs':401 '@pierre/diffs':
402 specifier: 'catalog:'402 specifier: 'catalog:'
403 version: 1.1.20(patch_hash=e4e35ba95100de3708f900e0d9ea62bca732b1e4486024b5055f48cd128dd9e0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)403 version: 1.3.0-beta.4(patch_hash=f5d41705ce94bbafc731d92e9cb1671db710df046dd135cb894e3e3e9164a75b)(@shikijs/themes@4.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
404 effect:404 effect:
405 specifier: 4.0.0-beta.78405 specifier: 4.0.0-beta.78
406 version: 4.0.0-beta.78(patch_hash=883249d8efbb462e928e21fefef96027b66aec50751178cafdce45f08eee3754)406 version: 4.0.0-beta.78(patch_hash=883249d8efbb462e928e21fefef96027b66aec50751178cafdce45f08eee3754)
@@ -482,7 +482,10 @@ importers: · 75 unmodified lines
482 version: 0.41.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(yjs@13.6.31)482 version: 0.41.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(yjs@13.6.31)
483 '@pierre/diffs':483 '@pierre/diffs':
484 specifier: 'catalog:'484 specifier: 'catalog:'
485 version: 1.1.20(patch_hash=e4e35ba95100de3708f900e0d9ea62bca732b1e4486024b5055f48cd128dd9e0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)485 version: 1.3.0-beta.4(patch_hash=f5d41705ce94bbafc731d92e9cb1671db710df046dd135cb894e3e3e9164a75b)(@shikijs/themes@4.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
486 '@pierre/trees':
487 specifier: 1.0.0-beta.4
488 version: 1.0.0-beta.4(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
486 '@t3tools/client-runtime':489 '@t3tools/client-runtime':
487 specifier: workspace:*490 specifier: workspace:*
488 version: link:../../packages/client-runtime491 version: link:../../packages/client-runtime
@@ -3203,16 +3206,42 @@ packages: · 2714 unmodified lines
3203 resolution: {integrity: sha512-titLmukUt/h8ho7Svlf0xSBjoy2ccZKrXjpXpZCj+v6V4CJccC2KyP45BLSCMx8YIpifMyiDyUptM4+5sruKbQ==}3206 resolution: {integrity: sha512-titLmukUt/h8ho7Svlf0xSBjoy2ccZKrXjpXpZCj+v6V4CJccC2KyP45BLSCMx8YIpifMyiDyUptM4+5sruKbQ==}
3204 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}3207 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
3205 3208
3206 '@pierre/diffs@1.1.20':3209 '@pierre/diffs@1.3.0-beta.4':
3207 resolution: {integrity: sha512-lLi+3sLCm3QDd5/aLO9pw+WbF6UzhrkWm2oTZ5WZJTGemOyUNRJ4DDhcEKmVusu4C4bXx9Nssh6fF+wQcapb5w==}3210 resolution: {integrity: sha512-poFcsvhcQt9lH/InzAPaGs47WYHMidnFCjuYGNU41HiVLJP4mkIQDSdvcnPIkBuh/cYbPOQg/YE3T1kSpr01GA==}
3208 peerDependencies:3211 peerDependencies:
3209 react: ^18.3.1 || ^19.0.03212 react: ^18.3.1 || ^19.0.0
3210 react-dom: ^18.3.1 || ^19.0.03213 react-dom: ^18.3.1 || ^19.0.0
3211 3214
3212 '@pierre/theme@0.0.28':3215 '@pierre/theme@1.0.3':
3213 resolution: {integrity: sha512-1j/H/fECBuc9dEvntdWI+l435HZapw+RCJTlqCA6BboQ5TjlnE005j/ROWutXIs8aq5OAc82JI2Kwk4A1WWBgw==}3216 resolution: {integrity: sha512-sWHv11TMoqKxKDgTIk5VbhQjdPhs8DCcBxbjh3mRlS3YOM/OcrWoGX6MM8eBGn9cUu3M46Py0JnxsG2nJaFTuA==}
3214 engines: {vscode: ^1.0.0}3217 engines: {vscode: ^1.0.0}
3215 3218
3219 '@pierre/theming@0.0.1':
3220 resolution: {integrity: sha512-1thlEtJbqdyLzc1ZS2KQa1q7FzDGHT4dTEdKHoyQjOMeWWOmbVG5/ndEfOKfAb5Fzkz8cNJrOjFLiZoDH/A03A==}
3221 peerDependencies:
3222 '@pierre/theme': ^1.0.0
3223 '@shikijs/themes': ^3.0.0 || ^4.0.0
3224 react: ^18.3.1 || ^19.0.0
3225 react-dom: ^18.3.1 || ^19.0.0
3226 shiki: ^3.0.0 || ^4.0.0
3227 peerDependenciesMeta:
3228 '@pierre/theme':
3229 optional: true
3230 '@shikijs/themes':
3231 optional: true
3232 react:
3233 optional: true
3234 react-dom:
3235 optional: true
3236 shiki:
3237 optional: true
3238
3239 '@pierre/trees@1.0.0-beta.4':
3240 resolution: {integrity: sha512-OfT1yk9ne8Te5+GB5zUY8yqE6B8BqjBHQJleH4lu8ltwNpoocZl4vXt1AzlEExpxI/pp+AFX5QG+lR3JjtTEag==}
3241 peerDependencies:
3242 react: ^18.3.1 || ^19.0.0
3243 react-dom: ^18.3.1 || ^19.0.0
3244
3216 '@polka/url@1.0.0-next.29':3245 '@polka/url@1.0.0-next.29':
3217 resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}3246 resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
3218 3247
@@ -8078,6 +8107,14 @@ packages: · 4859 unmodified lines
8078 engines: {node: '>=14.0.0'}8107 engines: {node: '>=14.0.0'}
8079 hasBin: true8108 hasBin: true
8080 8109
8110 preact-render-to-string@6.6.5:
8111 resolution: {integrity: sha512-O6MHzYNIKYaiSX3bOw0gGZfEbOmlIDtDfWwN1JJdc/T3ihzRT6tGGSEWE088dWrEDGa1u7101q+6fzQnO9XCPA==}
8112 peerDependencies:
8113 preact: '>=10 || >= 11.0.0-0'
8114
8115 preact@11.0.0-beta.0:
8116 resolution: {integrity: sha512-IcODoASASYwJ9kxz7+MJeiJhvLriwSb4y4mHIyxdgaRZp6kPUud7xytrk/6GZw8U3y6EFJaRb5wi9SrEK+8+lg==}
8117
8081 prettier@3.8.3:8118 prettier@3.8.3:
8082 resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}8119 resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
8083 engines: {node: '>=14'}8120 engines: {node: '>=14'}
@@ -12439,9 +12476,10 @@ snapshots: · 4355 unmodified lines
12439 12476
12440 '@oxlint/plugins@1.68.0': {}12477 '@oxlint/plugins@1.68.0': {}
12441 12478
12442 '@pierre/diffs@1.1.20(patch_hash=e4e35ba95100de3708f900e0d9ea62bca732b1e4486024b5055f48cd128dd9e0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':12479 '@pierre/diffs@1.3.0-beta.4(patch_hash=f5d41705ce94bbafc731d92e9cb1671db710df046dd135cb894e3e3e9164a75b)(@shikijs/themes@3.23.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
12443 dependencies:12480 dependencies:
12444 '@pierre/theme': 0.0.2812481 '@pierre/theme': 1.0.3
12482 '@pierre/theming': 0.0.1(@pierre/theme@1.0.3)(@shikijs/themes@3.23.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(shiki@3.23.0)
12445 '@shikijs/transformers': 3.23.012483 '@shikijs/transformers': 3.23.0
12446 diff: 8.0.312484 diff: 8.0.3
12447 hast-util-to-html: 9.0.512485 hast-util-to-html: 9.0.5
@@ -12449,10 +12487,13 @@ snapshots: · 1 unmodified line
12449 react: 19.2.312487 react: 19.2.3
12450 react-dom: 19.2.3(react@19.2.3)12488 react-dom: 19.2.3(react@19.2.3)
12451 shiki: 3.23.012489 shiki: 3.23.0
12490 transitivePeerDependencies:
12491 - '@shikijs/themes'
12452 12492
12453 '@pierre/diffs@1.1.20(patch_hash=e4e35ba95100de3708f900e0d9ea62bca732b1e4486024b5055f48cd128dd9e0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':12493 '@pierre/diffs@1.3.0-beta.4(patch_hash=f5d41705ce94bbafc731d92e9cb1671db710df046dd135cb894e3e3e9164a75b)(@shikijs/themes@4.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
12454 dependencies:12494 dependencies:
12455 '@pierre/theme': 0.0.2812495 '@pierre/theme': 1.0.3
12496 '@pierre/theming': 0.0.1(@pierre/theme@1.0.3)(@shikijs/themes@4.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(shiki@3.23.0)
12456 '@shikijs/transformers': 3.23.012497 '@shikijs/transformers': 3.23.0
12457 diff: 8.0.312498 diff: 8.0.3
12458 hast-util-to-html: 9.0.512499 hast-util-to-html: 9.0.5
@@ -12460,8 +12501,33 @@ snapshots: · 1 unmodified line
12460 react: 19.2.612501 react: 19.2.6
12461 react-dom: 19.2.6(react@19.2.6)12502 react-dom: 19.2.6(react@19.2.6)
12462 shiki: 3.23.012503 shiki: 3.23.0
12504 transitivePeerDependencies:
12505 - '@shikijs/themes'
12463 12506
12464 '@pierre/theme@0.0.28': {}12507 '@pierre/theme@1.0.3': {}
12508
12509 '@pierre/theming@0.0.1(@pierre/theme@1.0.3)(@shikijs/themes@3.23.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(shiki@3.23.0)':
12510 optionalDependencies:
12511 '@pierre/theme': 1.0.3
12512 '@shikijs/themes': 3.23.0
12513 react: 19.2.3
12514 react-dom: 19.2.3(react@19.2.3)
12515 shiki: 3.23.0
12516
12517 '@pierre/theming@0.0.1(@pierre/theme@1.0.3)(@shikijs/themes@4.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(shiki@3.23.0)':
12518 optionalDependencies:
12519 '@pierre/theme': 1.0.3
12520 '@shikijs/themes': 4.1.0
12521 react: 19.2.6
12522 react-dom: 19.2.6(react@19.2.6)
12523 shiki: 3.23.0
12524
12525 '@pierre/trees@1.0.0-beta.4(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
12526 dependencies:
12527 preact: 11.0.0-beta.0
12528 preact-render-to-string: 6.6.5(preact@11.0.0-beta.0)
12529 react: 19.2.6
12530 react-dom: 19.2.6(react@19.2.6)
12465 12531
12466 '@polka/url@1.0.0-next.29': {}12532 '@polka/url@1.0.0-next.29': {}
12467 12533
@@ -17748,6 +17814,12 @@ snapshots: · 5280 unmodified lines
17748 commander: 9.5.017814 commander: 9.5.0
17749 optional: true17815 optional: true
17750 17816
17817 preact-render-to-string@6.6.5(preact@11.0.0-beta.0):
17818 dependencies:
17819 preact: 11.0.0-beta.0
17820
17821 preact@11.0.0-beta.0: {}
17822
17751 prettier@3.8.3: {}17823 prettier@3.8.3: {}
17752 17824
17753 pretty-cache-header@1.0.0:17825 pretty-cache-header@1.0.0:
pnpm-workspace.yaml+4−2
@@ -28,7 +28,7 @@ catalog: · 27 unmodified lines
28 "@effect/tsgo": 0.13.228 "@effect/tsgo": 0.13.2
29 "@noble/curves": 1.9.129 "@noble/curves": 1.9.1
30 "@noble/hashes": 1.8.030 "@noble/hashes": 1.8.0
31 "@pierre/diffs": 1.1.2031 "@pierre/diffs": 1.3.0-beta.4
32 "@vitest/runner": 4.1.832 "@vitest/runner": 4.1.8
33 "@types/node": 24.12.433 "@types/node": 24.12.4
34 "@typescript/native-preview": 7.0.0-dev.20260604.134 "@typescript/native-preview": 7.0.0-dev.20260604.1
@@ -83,8 +83,10 @@ packageExtensions: · 48 unmodified lines
83 dependencies:83 dependencies:
84 vite: "catalog:"84 vite: "catalog:"
85patchedDependencies:85patchedDependencies:
86 # Keep non-browser consumers off the root entrypoint, which eagerly imports
87 # DOM-dependent renderers. These utility/type subpaths are safe for mobile.
88 "@pierre/diffs@1.3.0-beta.4": patches/@pierre%2Fdiffs@1.3.0-beta.4.patch
86 "@effect/vitest@4.0.0-beta.78": patches/@effect__vitest@4.0.0-beta.78.patch89 "@effect/vitest@4.0.0-beta.78": patches/@effect__vitest@4.0.0-beta.78.patch
87 "@expo/metro-config@56.0.13": patches/@expo%2Fmetro-config@56.0.13.patch90 "@expo/metro-config@56.0.13": patches/@expo%2Fmetro-config@56.0.13.patch
88 "@pierre/diffs@1.1.20": patches/@pierre%2Fdiffs@1.1.20.patch
89 effect@4.0.0-beta.78: patches/effect@4.0.0-beta.78.patch91 effect@4.0.0-beta.78: patches/effect@4.0.0-beta.78.patch
90 react-native-nitro-modules@0.35.9: patches/react-native-nitro-modules@0.35.9.patch92 react-native-nitro-modules@0.35.9: patches/react-native-nitro-modules@0.35.9.patch
scripts/sync-vscode-icons.mjs+0−153
@@ -1,153 +0,0 @@
1#!/usr/bin/env node
2
3import fs from "node:fs/promises";
4import os from "node:os";
5import path from "node:path";
6import vm from "node:vm";
7import { execFile } from "node:child_process";
8import { promisify } from "node:util";
9
10const execFileAsync = promisify(execFile);
11
12const VERSION = process.argv[2] ?? "12.17.0";
13const VERSION_TAG = `v${VERSION}`;
14const VSIX_URL = `https://open-vsx.org/api/vscode-icons-team/vscode-icons/${VERSION}/file/vscode-icons-team.vscode-icons-${VERSION}.vsix`;
15const LANGUAGES_URL = `https://raw.githubusercontent.com/vscode-icons/vscode-icons/${VERSION_TAG}/src/iconsManifest/languages.ts`;
16
17const REPO_ROOT = process.cwd();
18const MANIFEST_PATH = path.join(REPO_ROOT, "apps/web/src/vscode-icons-manifest.json");
19const ASSOCIATIONS_PATH = path.join(
20 REPO_ROOT,
21 "apps/web/src/vscode-icons-language-associations.json",
22);
23
24function normalizeExtension(value) {
25 return value.trim().toLowerCase().replace(/^\./, "");
26}
27
28function normalizeFileName(value) {
29 return value.trim().toLowerCase();
30}
31
32function putIfAbsent(target, key, value) {
33 if (!(key in target)) {
34 target[key] = value;
35 }
36}
37
38async function downloadVsix(tmpDir) {
39 const vsixPath = path.join(tmpDir, `vscode-icons-${VERSION}.vsix`);
40 const response = await fetch(VSIX_URL);
41 if (!response.ok) {
42 throw new Error(`Failed to download VSIX: ${response.status} ${response.statusText}`);
43 }
44 const bytes = Buffer.from(await response.arrayBuffer());
45 await fs.writeFile(vsixPath, bytes);
46 return vsixPath;
47}
48
49async function extractManifestFromVsix(vsixPath) {
50 const { stdout } = await execFileAsync("unzip", [
51 "-p",
52 vsixPath,
53 "extension/dist/src/vsicons-icon-theme.json",
54 ]);
55 if (!stdout || stdout.trim().length === 0) {
56 throw new Error("Could not extract vsicons-icon-theme.json from VSIX");
57 }
58 return JSON.parse(stdout);
59}
60
61async function loadLanguagesCollection() {
62 const response = await fetch(LANGUAGES_URL);
63 if (!response.ok) {
64 throw new Error(`Failed to fetch languages.ts: ${response.status} ${response.statusText}`);
65 }
66 const rawSource = await response.text();
67 const source = rawSource
68 .replace(/^import[^;]+;\s*/gm, "")
69 .replace(/export const languages(?:\s*:\s*[^=]+)?\s*=/, "const languages =")
70 .replace(/\}\s*satisfies\s*Record<[^;]+>;/, "};");
71
72 const context = {};
73 vm.createContext(context);
74 vm.runInContext(`${source}\n;globalThis.__languages = languages;`, context);
75 const languages = context.__languages;
76 if (!languages || typeof languages !== "object") {
77 throw new Error("Failed to parse languages.ts into a collection");
78 }
79 return languages;
80}
81
82function buildLanguageAssociations(manifest, languages) {
83 const availableLanguageIds = new Set(Object.keys(manifest.languageIds ?? {}));
84 const extensionToLanguageId = {};
85 const fileNameToLanguageId = {};
86
87 for (const entry of Object.values(languages)) {
88 if (!entry || typeof entry !== "object") continue;
89 const idsRaw = entry.ids;
90 const ids = Array.isArray(idsRaw) ? idsRaw : [idsRaw];
91 const knownExtensions = Array.isArray(entry.knownExtensions) ? entry.knownExtensions : [];
92 const knownFilenames = Array.isArray(entry.knownFilenames) ? entry.knownFilenames : [];
93
94 for (const idValue of ids) {
95 if (typeof idValue !== "string") continue;
96 const languageId = idValue.trim();
97 if (languageId.length === 0) continue;
98 if (!availableLanguageIds.has(languageId)) continue;
99
100 for (const extensionValue of knownExtensions) {
101 if (typeof extensionValue !== "string") continue;
102 const extension = normalizeExtension(extensionValue);
103 if (extension.length === 0) continue;
104 putIfAbsent(extensionToLanguageId, extension, languageId);
105 }
106
107 for (const fileNameValue of knownFilenames) {
108 if (typeof fileNameValue !== "string") continue;
109 const fileName = normalizeFileName(fileNameValue);
110 if (fileName.length === 0) continue;
111 putIfAbsent(fileNameToLanguageId, fileName, languageId);
112 }
113 }
114 }
115
116 return {
117 version: VERSION,
118 source: LANGUAGES_URL,
119 generatedAt: new Date().toISOString(),
120 extensionToLanguageId,
121 fileNameToLanguageId,
122 };
123}
124
125async function main() {
126 const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "t3-vscode-icons-sync-"));
127 try {
128 const vsixPath = await downloadVsix(tmpDir);
129 const manifest = await extractManifestFromVsix(vsixPath);
130 const languages = await loadLanguagesCollection();
131 const associations = buildLanguageAssociations(manifest, languages);
132
133 await fs.writeFile(MANIFEST_PATH, `${JSON.stringify(manifest)}\n`, "utf8");
134 await fs.writeFile(ASSOCIATIONS_PATH, `${JSON.stringify(associations)}\n`, "utf8");
135
136 process.stdout.write(
137 [
138 `Synced vscode-icons ${VERSION}`,
139 `manifest: ${MANIFEST_PATH}`,
140 `language associations: ${ASSOCIATIONS_PATH}`,
141 `extension mappings: ${Object.keys(associations.extensionToLanguageId).length}`,
142 `filename mappings: ${Object.keys(associations.fileNameToLanguageId).length}`,
143 ].join("\n") + "\n",
144 );
145 } finally {
146 await fs.rm(tmpDir, { recursive: true, force: true });
147 }
148}
149
150main().catch((error) => {
151 process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
152 process.exit(1);
153});