From 49408c00e964093c654ee270d545c0e29857e61f Mon Sep 17 00:00:00 2001 From: Dax Date: Fri, 21 Nov 2025 20:41:27 -0500 Subject: enterprise (#4617) Co-authored-by: GitHub Action Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com> --- packages/ui/src/components/basic-tool.css | 16 +- packages/ui/src/components/basic-tool.tsx | 16 +- packages/ui/src/components/button.css | 2 +- packages/ui/src/components/button.tsx | 2 +- packages/ui/src/components/checkbox.css | 34 +- packages/ui/src/components/checkbox.tsx | 14 +- packages/ui/src/components/dialog.css | 14 +- packages/ui/src/components/dialog.tsx | 14 +- packages/ui/src/components/diff-changes.css | 4 +- packages/ui/src/components/diff-changes.tsx | 4 +- packages/ui/src/components/diff.tsx | 372 - packages/ui/src/components/file-icon.css | 5 + packages/ui/src/components/file-icon.tsx | 583 + packages/ui/src/components/file-icons/sprite.svg | 11707 +++++++++++++++++++ packages/ui/src/components/file-icons/types.ts | 1095 ++ packages/ui/src/components/icon-button.css | 16 +- packages/ui/src/components/icon-button.tsx | 6 +- packages/ui/src/components/icon.css | 2 +- packages/ui/src/components/icon.tsx | 6 +- packages/ui/src/components/index.ts | 28 - packages/ui/src/components/input.css | 4 +- packages/ui/src/components/input.tsx | 8 +- packages/ui/src/components/list.css | 30 - packages/ui/src/components/list.tsx | 83 - packages/ui/src/components/logo.css | 2 +- packages/ui/src/components/logo.tsx | 34 +- packages/ui/src/components/message-part.css | 22 +- packages/ui/src/components/message-part.tsx | 36 +- packages/ui/src/components/message-progress.css | 50 + packages/ui/src/components/message-progress.tsx | 172 + packages/ui/src/components/progress-circle.css | 4 +- packages/ui/src/components/progress-circle.tsx | 10 +- packages/ui/src/components/select-dialog.css | 22 +- packages/ui/src/components/select-dialog.tsx | 28 +- packages/ui/src/components/select.css | 14 +- packages/ui/src/components/select.tsx | 19 +- packages/ui/src/components/session-review.css | 96 + packages/ui/src/components/session-review.tsx | 105 + packages/ui/src/components/session-timeline.css | 324 + packages/ui/src/components/session-timeline.tsx | 289 + packages/ui/src/components/spinner.css | 6 + packages/ui/src/components/spinner.tsx | 39 + .../ui/src/components/sticky-accordion-header.css | 16 + .../ui/src/components/sticky-accordion-header.tsx | 18 + packages/ui/src/components/tabs.css | 20 +- packages/ui/src/components/tabs.tsx | 10 +- packages/ui/src/components/tooltip.tsx | 6 +- 47 files changed, 14723 insertions(+), 684 deletions(-) create mode 100644 packages/ui/src/components/file-icon.css create mode 100644 packages/ui/src/components/file-icon.tsx create mode 100644 packages/ui/src/components/file-icons/sprite.svg create mode 100644 packages/ui/src/components/file-icons/types.ts delete mode 100644 packages/ui/src/components/index.ts delete mode 100644 packages/ui/src/components/list.css delete mode 100644 packages/ui/src/components/list.tsx create mode 100644 packages/ui/src/components/message-progress.css create mode 100644 packages/ui/src/components/message-progress.tsx create mode 100644 packages/ui/src/components/session-review.css create mode 100644 packages/ui/src/components/session-review.tsx create mode 100644 packages/ui/src/components/session-timeline.css create mode 100644 packages/ui/src/components/session-timeline.tsx create mode 100644 packages/ui/src/components/spinner.css create mode 100644 packages/ui/src/components/spinner.tsx create mode 100644 packages/ui/src/components/sticky-accordion-header.css create mode 100644 packages/ui/src/components/sticky-accordion-header.tsx (limited to 'packages/ui/src/components') diff --git a/packages/ui/src/components/basic-tool.css b/packages/ui/src/components/basic-tool.css index f3d9f865f..4ea13b781 100644 --- a/packages/ui/src/components/basic-tool.css +++ b/packages/ui/src/components/basic-tool.css @@ -6,7 +6,7 @@ gap: 20px; justify-content: space-between; - [data-slot="tool-trigger-content"] { + [data-slot="basic-tool-tool-trigger-content"] { width: 100%; display: flex; align-items: center; @@ -14,16 +14,16 @@ gap: 20px; } - [data-slot="tool-icon"] { + [data-slot="basic-tool-tool-icon"] { flex-shrink: 0; } - [data-slot="tool-info"] { + [data-slot="basic-tool-tool-info"] { flex-grow: 1; min-width: 0; } - [data-slot="tool-info-structured"] { + [data-slot="basic-tool-tool-info-structured"] { width: 100%; display: flex; align-items: center; @@ -31,7 +31,7 @@ justify-content: space-between; } - [data-slot="tool-info-main"] { + [data-slot="basic-tool-tool-info-main"] { display: flex; align-items: center; gap: 8px; @@ -40,7 +40,7 @@ text-overflow: ellipsis; } - [data-slot="tool-title"] { + [data-slot="basic-tool-tool-title"] { font-family: var(--font-family-sans); font-size: var(--font-size-small); font-style: normal; @@ -54,7 +54,7 @@ } } - [data-slot="tool-subtitle"] { + [data-slot="basic-tool-tool-subtitle"] { font-family: var(--font-family-sans); font-size: var(--font-size-small); font-style: normal; @@ -64,7 +64,7 @@ color: var(--text-weak); } - [data-slot="tool-arg"] { + [data-slot="basic-tool-tool-arg"] { font-family: var(--font-family-sans); font-size: var(--font-size-small); font-style: normal; diff --git a/packages/ui/src/components/basic-tool.tsx b/packages/ui/src/components/basic-tool.tsx index 43574fbb7..2b133a298 100644 --- a/packages/ui/src/components/basic-tool.tsx +++ b/packages/ui/src/components/basic-tool.tsx @@ -29,16 +29,16 @@ export function BasicTool(props: BasicToolProps) {
-
- -
+
+ +
{(trigger) => ( -
-
+
+
{(arg) => ( - + {props.children} diff --git a/packages/ui/src/components/checkbox.css b/packages/ui/src/components/checkbox.css index 358391bd1..b10ebbbd1 100644 --- a/packages/ui/src/components/checkbox.css +++ b/packages/ui/src/components/checkbox.css @@ -4,7 +4,7 @@ gap: 12px; cursor: default; - [data-slot="checkbox-input"] { + [data-slot="checkbox-checkbox-input"] { position: absolute; width: 1px; height: 1px; @@ -16,7 +16,7 @@ border-width: 0; } - [data-slot="checkbox-control"] { + [data-slot="checkbox-checkbox-control"] { display: flex; align-items: center; justify-content: center; @@ -30,7 +30,7 @@ /* background-color: var(--surface-weak); */ } - [data-slot="checkbox-indicator"] { + [data-slot="checkbox-checkbox-indicator"] { display: flex; align-items: center; justify-content: center; @@ -40,10 +40,10 @@ opacity: 0; } - /* [data-slot="checkbox-content"] { */ + /* [data-slot="checkbox-checkbox-content"] { */ /* } */ - [data-slot="checkbox-label"] { + [data-slot="checkbox-checkbox-label"] { user-select: none; color: var(--text-base); @@ -56,7 +56,7 @@ letter-spacing: var(--letter-spacing-normal); } - [data-slot="checkbox-description"] { + [data-slot="checkbox-checkbox-description"] { color: var(--text-base); font-family: var(--font-family-sans); font-size: 12px; @@ -65,7 +65,7 @@ letter-spacing: var(--letter-spacing-normal); } - [data-slot="checkbox-error"] { + [data-slot="checkbox-checkbox-error"] { color: var(--text-error); font-family: var(--font-family-sans); font-size: 12px; @@ -74,30 +74,30 @@ letter-spacing: var(--letter-spacing-normal); } - &:hover:not([data-disabled], [data-readonly]) [data-slot="checkbox-control"] { + &:hover:not([data-disabled], [data-readonly]) [data-slot="checkbox-checkbox-control"] { border-color: var(--border-hover); background-color: var(--surface-hover); } - &:focus-within:not([data-readonly]) [data-slot="checkbox-control"] { + &:focus-within:not([data-readonly]) [data-slot="checkbox-checkbox-control"] { border-color: var(--border-focus); box-shadow: 0 0 0 2px var(--surface-focus); } - &[data-checked] [data-slot="checkbox-control"], - &[data-indeterminate] [data-slot="checkbox-control"] { + &[data-checked] [data-slot="checkbox-checkbox-control"], + &[data-indeterminate] [data-slot="checkbox-checkbox-control"] { border-color: var(--border-base); background-color: var(--surface-weak); } - &[data-checked]:hover:not([data-disabled], [data-readonly]) [data-slot="checkbox-control"], - &[data-indeterminate]:hover:not([data-disabled]) [data-slot="checkbox-control"] { + &[data-checked]:hover:not([data-disabled], [data-readonly]) [data-slot="checkbox-checkbox-control"], + &[data-indeterminate]:hover:not([data-disabled]) [data-slot="checkbox-checkbox-control"] { border-color: var(--border-hover); background-color: var(--surface-hover); } - &[data-checked] [data-slot="checkbox-indicator"], - &[data-indeterminate] [data-slot="checkbox-indicator"] { + &[data-checked] [data-slot="checkbox-checkbox-indicator"], + &[data-indeterminate] [data-slot="checkbox-checkbox-indicator"] { opacity: 1; } @@ -105,12 +105,12 @@ cursor: not-allowed; } - &[data-disabled] [data-slot="checkbox-control"] { + &[data-disabled] [data-slot="checkbox-checkbox-control"] { border-color: var(--border-disabled); background-color: var(--surface-disabled); } - &[data-invalid] [data-slot="checkbox-control"] { + &[data-invalid] [data-slot="checkbox-checkbox-control"] { border-color: var(--border-error); } diff --git a/packages/ui/src/components/checkbox.tsx b/packages/ui/src/components/checkbox.tsx index 2009a430b..558c4bbd2 100644 --- a/packages/ui/src/components/checkbox.tsx +++ b/packages/ui/src/components/checkbox.tsx @@ -13,9 +13,9 @@ export function Checkbox(props: CheckboxProps) { const resolved = children(() => local.children) return ( - - - + + + {local.icon || ( -
+
- + {resolved()} - {local.description} + {local.description} - +
) diff --git a/packages/ui/src/components/dialog.css b/packages/ui/src/components/dialog.css index 7cf15bb23..2ac0709dd 100644 --- a/packages/ui/src/components/dialog.css +++ b/packages/ui/src/components/dialog.css @@ -21,7 +21,7 @@ align-items: center; justify-content: center; - [data-slot="container"] { + [data-slot="dialog-container"] { position: relative; z-index: 50; width: min(calc(100vw - 16px), 624px); @@ -31,7 +31,7 @@ align-items: center; justify-items: start; - [data-slot="content"] { + [data-slot="dialog-content"] { display: flex; flex-direction: column; align-items: flex-start; @@ -56,7 +56,7 @@ /* animation: contentShow 300ms ease-out; */ /* } */ - [data-slot="header"] { + [data-slot="dialog-header"] { display: flex; height: 40px; padding: 4px 4px 4px 8px; @@ -65,7 +65,7 @@ flex-shrink: 0; align-self: stretch; - [data-slot="title"] { + [data-slot="dialog-title"] { color: var(--text-strong); /* text-16-medium */ @@ -76,10 +76,10 @@ line-height: var(--line-height-x-large); /* 150% */ letter-spacing: var(--letter-spacing-tight); } - /* [data-slot="close-button"] {} */ + /* [data-slot="dialog-close-button"] {} */ } - /* [data-slot="description"] {} */ - [data-slot="body"] { + /* [data-slot="dialog-description"] {} */ + [data-slot="dialog-body"] { width: 100%; position: relative; display: flex; diff --git a/packages/ui/src/components/dialog.tsx b/packages/ui/src/components/dialog.tsx index ce7a4b3ac..4625482b5 100644 --- a/packages/ui/src/components/dialog.tsx +++ b/packages/ui/src/components/dialog.tsx @@ -45,9 +45,9 @@ export function DialogRoot(props: DialogProps) {
-
+
) { - return
+ return
} function DialogBody(props: ComponentProps<"div">) { - return
+ return
} function DialogTitle(props: DialogTitleProps & ComponentProps<"h2">) { - return + return } function DialogDescription(props: DialogDescriptionProps & ComponentProps<"p">) { - return + return } function DialogCloseButton(props: DialogCloseButtonProps & ComponentProps<"button">) { - return + return } export const Dialog = Object.assign(DialogRoot, { diff --git a/packages/ui/src/components/diff-changes.css b/packages/ui/src/components/diff-changes.css index eb95c4676..010860d13 100644 --- a/packages/ui/src/components/diff-changes.css +++ b/packages/ui/src/components/diff-changes.css @@ -4,7 +4,7 @@ justify-content: flex-end; align-items: center; - [data-slot="additions"] { + [data-slot="diff-changes-additions"] { font-family: var(--font-family-mono); font-size: var(--font-size-small); font-style: normal; @@ -15,7 +15,7 @@ color: var(--text-diff-add-base); } - [data-slot="deletions"] { + [data-slot="diff-changes-deletions"] { font-family: var(--font-family-mono); font-size: var(--font-size-small); font-style: normal; diff --git a/packages/ui/src/components/diff-changes.tsx b/packages/ui/src/components/diff-changes.tsx index a8ab737f7..9e29dbb2b 100644 --- a/packages/ui/src/components/diff-changes.tsx +++ b/packages/ui/src/components/diff-changes.tsx @@ -105,8 +105,8 @@ export function DiffChanges(props: { - {`+${additions()}`} - {`-${deletions()}`} + {`+${additions()}`} + {`-${deletions()}`}
diff --git a/packages/ui/src/components/diff.tsx b/packages/ui/src/components/diff.tsx index d0d427499..8743be290 100644 --- a/packages/ui/src/components/diff.tsx +++ b/packages/ui/src/components/diff.tsx @@ -4,8 +4,6 @@ import { type DiffLineAnnotation, type HunkData, FileDiffOptions, - registerCustomTheme, - ThemeRegistrationResolved, } from "@pierre/precision-diffs" import { ComponentProps, createEffect, splitProps } from "solid-js" @@ -173,373 +171,3 @@ export function Diff(props: DiffProps) { /> ) } - -registerCustomTheme("OpenCode", () => { - return Promise.resolve({ - name: "OpenCode", - colors: { - "editor.background": "transparent", - "editor.foreground": "var(--text-base)", - "gitDecoration.addedResourceForeground": "var(--syntax-diff-add)", - "gitDecoration.deletedResourceForeground": "var(--syntax-diff-delete)", - // "gitDecoration.conflictingResourceForeground": "#ffca00", - // "gitDecoration.modifiedResourceForeground": "#1a76d4", - // "gitDecoration.untrackedResourceForeground": "#00cab1", - // "gitDecoration.ignoredResourceForeground": "#84848A", - // "terminal.titleForeground": "#adadb1", - // "terminal.titleInactiveForeground": "#84848A", - // "terminal.background": "#141415", - // "terminal.foreground": "#adadb1", - // "terminal.ansiBlack": "#141415", - // "terminal.ansiRed": "#ff2e3f", - // "terminal.ansiGreen": "#0dbe4e", - // "terminal.ansiYellow": "#ffca00", - // "terminal.ansiBlue": "#008cff", - // "terminal.ansiMagenta": "#c635e4", - // "terminal.ansiCyan": "#08c0ef", - // "terminal.ansiWhite": "#c6c6c8", - // "terminal.ansiBrightBlack": "#141415", - // "terminal.ansiBrightRed": "#ff2e3f", - // "terminal.ansiBrightGreen": "#0dbe4e", - // "terminal.ansiBrightYellow": "#ffca00", - // "terminal.ansiBrightBlue": "#008cff", - // "terminal.ansiBrightMagenta": "#c635e4", - // "terminal.ansiBrightCyan": "#08c0ef", - // "terminal.ansiBrightWhite": "#c6c6c8", - }, - tokenColors: [ - { - scope: ["comment", "punctuation.definition.comment", "string.comment"], - settings: { - foreground: "var(--syntax-comment)", - }, - }, - { - scope: ["entity.other.attribute-name"], - settings: { - foreground: "var(--syntax-property)", // maybe attribute - }, - }, - { - scope: ["constant", "entity.name.constant", "variable.other.constant", "variable.language", "entity"], - settings: { - foreground: "var(--syntax-constant)", - }, - }, - { - scope: ["entity.name", "meta.export.default", "meta.definition.variable"], - settings: { - foreground: "var(--syntax-type)", - }, - }, - { - scope: ["meta.object.member"], - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: [ - "variable.parameter.function", - "meta.jsx.children", - "meta.block", - "meta.tag.attributes", - "entity.name.constant", - "meta.embedded.expression", - "meta.template.expression", - "string.other.begin.yaml", - "string.other.end.yaml", - ], - settings: { - foreground: "var(--syntax-punctuation)", - }, - }, - { - scope: ["entity.name.function", "support.type.primitive"], - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: ["support.class.component"], - settings: { - foreground: "var(--syntax-type)", - }, - }, - { - scope: "keyword", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: [ - "keyword.operator", - "storage.type.function.arrow", - "punctuation.separator.key-value.css", - "entity.name.tag.yaml", - "punctuation.separator.key-value.mapping.yaml", - ], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: ["storage", "storage.type"], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: ["storage.modifier.package", "storage.modifier.import", "storage.type.java"], - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: [ - "string", - "punctuation.definition.string", - "string punctuation.section.embedded source", - "entity.name.tag", - ], - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: "support", - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: ["support.type.object.module", "variable.other.object", "support.type.property-name.css"], - settings: { - foreground: "var(--syntax-object)", - }, - }, - { - scope: "meta.property-name", - settings: { - foreground: "var(--syntax-property)", - }, - }, - { - scope: "variable", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "variable.other", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: [ - "invalid.broken", - "invalid.illegal", - "invalid.unimplemented", - "invalid.deprecated", - "message.error", - "markup.deleted", - "meta.diff.header.from-file", - "punctuation.definition.deleted", - "brackethighlighter.unmatched", - "token.error-token", - ], - settings: { - foreground: "var(--syntax-critical)", - }, - }, - { - scope: "carriage-return", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "string source", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "string variable", - settings: { - foreground: "var(--syntax-constant)", - }, - }, - { - scope: [ - "source.regexp", - "string.regexp", - "string.regexp.character-class", - "string.regexp constant.character.escape", - "string.regexp source.ruby.embedded", - "string.regexp string.regexp.arbitrary-repitition", - "string.regexp constant.character.escape", - ], - settings: { - foreground: "var(--syntax-regexp)", - }, - }, - { - scope: "support.constant", - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: "support.variable", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "meta.module-reference", - settings: { - foreground: "var(--syntax-info)", - }, - }, - { - scope: "punctuation.definition.list.begin.markdown", - settings: { - foreground: "var(--syntax-punctuation)", - }, - }, - { - scope: ["markup.heading", "markup.heading entity.name"], - settings: { - fontStyle: "bold", - foreground: "var(--syntax-info)", - }, - }, - { - scope: "markup.quote", - settings: { - foreground: "var(--syntax-info)", - }, - }, - { - scope: "markup.italic", - settings: { - fontStyle: "italic", - // foreground: "", - }, - }, - { - scope: "markup.bold", - settings: { - fontStyle: "bold", - foreground: "var(--text-strong)", - }, - }, - { - scope: [ - "markup.raw", - "markup.inserted", - "meta.diff.header.to-file", - "punctuation.definition.inserted", - "markup.changed", - "punctuation.definition.changed", - "markup.ignored", - "markup.untracked", - ], - settings: { - foreground: "var(--text-base)", - }, - }, - { - scope: "meta.diff.range", - settings: { - fontStyle: "bold", - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.diff.header", - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.separator", - settings: { - fontStyle: "bold", - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.output", - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.export.default", - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: [ - "brackethighlighter.tag", - "brackethighlighter.curly", - "brackethighlighter.round", - "brackethighlighter.square", - "brackethighlighter.angle", - "brackethighlighter.quote", - ], - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: ["constant.other.reference.link", "string.other.link"], - settings: { - fontStyle: "underline", - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "token.info-token", - settings: { - foreground: "var(--syntax-info)", - }, - }, - { - scope: "token.warn-token", - settings: { - foreground: "var(--syntax-warning)", - }, - }, - { - scope: "token.debug-token", - settings: { - foreground: "var(--syntax-info)", - }, - }, - ], - semanticTokenColors: { - comment: "var(--syntax-comment)", - string: "var(--syntax-string)", - number: "var(--syntax-constant)", - regexp: "var(--syntax-regexp)", - keyword: "var(--syntax-keyword)", - variable: "var(--syntax-variable)", - parameter: "var(--syntax-variable)", - property: "var(--syntax-property)", - function: "var(--syntax-primitive)", - method: "var(--syntax-primitive)", - type: "var(--syntax-type)", - class: "var(--syntax-type)", - namespace: "var(--syntax-type)", - enumMember: "var(--syntax-primitive)", - "variable.constant": "var(--syntax-constant)", - "variable.defaultLibrary": "var(--syntax-unknown)", - }, - } as unknown as ThemeRegistrationResolved) -}) diff --git a/packages/ui/src/components/file-icon.css b/packages/ui/src/components/file-icon.css new file mode 100644 index 000000000..e650f6dc7 --- /dev/null +++ b/packages/ui/src/components/file-icon.css @@ -0,0 +1,5 @@ +[data-component="file-icon"] { + flex-shrink: 0; + width: 16px; + height: 16px; +} diff --git a/packages/ui/src/components/file-icon.tsx b/packages/ui/src/components/file-icon.tsx new file mode 100644 index 000000000..11465112f --- /dev/null +++ b/packages/ui/src/components/file-icon.tsx @@ -0,0 +1,583 @@ +import type { Component, JSX } from "solid-js" +import { createMemo, splitProps } from "solid-js" +import sprite from "./file-icons/sprite.svg" +import type { IconName } from "./file-icons/types" + +export type FileIconProps = JSX.GSVGAttributes & { + node: { path: string; type: "file" | "directory" } + expanded?: boolean +} + +export const FileIcon: Component = (props) => { + const [local, rest] = splitProps(props, ["node", "class", "classList", "expanded"]) + const name = createMemo(() => chooseIconName(local.node.path, local.node.type, local.expanded || false)) + return ( + + + + ) +} + +type IconMaps = { + fileNames: Record + fileExtensions: Record + folderNames: Record + defaults: { + file: IconName + folder: IconName + folderOpen: IconName + } +} + +const ICON_MAPS: IconMaps = { + fileNames: { + // Documentation files + "readme.md": "Readme", + "changelog.md": "Changelog", + "contributing.md": "Contributing", + "conduct.md": "Conduct", + license: "Certificate", + authors: "Authors", + credits: "Credits", + install: "Installation", + + // Node.js files + "package.json": "Nodejs", + "package-lock.json": "Nodejs", + "yarn.lock": "Yarn", + "pnpm-lock.yaml": "Pnpm", + "bun.lock": "Bun", + "bun.lockb": "Bun", + "bunfig.toml": "Bun", + ".nvmrc": "Nodejs", + ".node-version": "Nodejs", + + // Docker files + dockerfile: "Docker", + "docker-compose.yml": "Docker", + "docker-compose.yaml": "Docker", + ".dockerignore": "Docker", + + // Config files + "jest.config.js": "Jest", + "jest.config.ts": "Jest", + "jest.config.mjs": "Jest", + "vitest.config.js": "Vitest", + "vitest.config.ts": "Vitest", + "tailwind.config.js": "Tailwindcss", + "tailwind.config.ts": "Tailwindcss", + "turbo.json": "Turborepo", + "tsconfig.json": "Tsconfig", + "jsconfig.json": "Jsconfig", + ".eslintrc": "Eslint", + ".eslintrc.js": "Eslint", + ".eslintrc.json": "Eslint", + ".prettierrc": "Prettier", + ".prettierrc.js": "Prettier", + ".prettierrc.json": "Prettier", + "vite.config.js": "Vite", + "vite.config.ts": "Vite", + "webpack.config.js": "Webpack", + "rollup.config.js": "Rollup", + "astro.config.mjs": "AstroConfig", + "astro.config.js": "AstroConfig", + "next.config.js": "Next", + "next.config.mjs": "Next", + "nuxt.config.js": "Nuxt", + "nuxt.config.ts": "Nuxt", + "svelte.config.js": "Svelte", + "gatsby-config.js": "Gatsby", + "remix.config.js": "Remix", + "prisma.schema": "Prisma", + ".gitignore": "Git", + ".gitattributes": "Git", + makefile: "Makefile", + cmake: "Cmake", + "cargo.toml": "Rust", + "go.mod": "GoMod", + "go.sum": "GoMod", + "requirements.txt": "Python", + "pyproject.toml": "Python", + pipfile: "Python", + "poetry.lock": "Poetry", + gemfile: "Gemfile", + rakefile: "Ruby", + "composer.json": "Php", + "build.gradle": "Gradle", + "pom.xml": "Maven", + "deno.json": "Deno", + "deno.jsonc": "Deno", + "vercel.json": "Vercel", + "netlify.toml": "Netlify", + ".env": "Tune", + ".env.local": "Tune", + ".env.development": "Tune", + ".env.production": "Tune", + ".env.example": "Tune", + ".editorconfig": "Editorconfig", + "robots.txt": "Robots", + "favicon.ico": "Favicon", + browserlist: "Browserlist", + ".babelrc": "Babel", + "babel.config.js": "Babel", + "gulpfile.js": "Gulp", + "gruntfile.js": "Grunt", + "capacitor.config.json": "Capacitor", + "ionic.config.json": "Ionic", + "angular.json": "Angular", + ".storybook": "Storybook", + "storybook.config.js": "Storybook", + "cypress.config.js": "Cypress", + "playwright.config.js": "Playwright", + "puppeteer.config.js": "Puppeteer", + "wrangler.toml": "Wrangler", + "firebase.json": "Firebase", + supabase: "Supabase", + terraform: "Terraform", + kubernetes: "Kubernetes", + ".gitpod.yml": "Gitpod", + ".devcontainer": "Vscode", + "travis.yml": "Travis", + "appveyor.yml": "Appveyor", + ".circleci": "Circleci", + "renovate.json": "Renovate", + "dependabot.yml": "Dependabot", + "lerna.json": "Lerna", + "nx.json": "Nx", + }, + fileExtensions: { + // Test files + "spec.ts": "TestTs", + "test.ts": "TestTs", + "spec.tsx": "TestJsx", + "test.tsx": "TestJsx", + "spec.js": "TestJs", + "test.js": "TestJs", + "spec.jsx": "TestJsx", + "test.jsx": "TestJsx", + + // JavaScript/TypeScript + "js.map": "JavascriptMap", + "d.ts": "TypescriptDef", + ts: "Typescript", + tsx: "React_ts", + js: "Javascript", + jsx: "React", + mjs: "Javascript", + cjs: "Javascript", + + // Web languages + html: "Html", + htm: "Html", + css: "Css", + scss: "Sass", + sass: "Sass", + less: "Less", + styl: "Stylus", + + // Data formats + json: "Json", + xml: "Xml", + yml: "Yaml", + yaml: "Yaml", + toml: "Toml", + hjson: "Hjson", + + // Documentation + md: "Markdown", + mdx: "Mdx", + tex: "Tex", + + // Programming languages + py: "Python", + pyx: "Python", + pyw: "Python", + rs: "Rust", + go: "Go", + java: "Java", + kt: "Kotlin", + scala: "Scala", + php: "Php", + rb: "Ruby", + cs: "Csharp", + vb: "Visualstudio", + cpp: "Cpp", + cc: "Cpp", + cxx: "Cpp", + c: "C", + h: "H", + hpp: "Hpp", + swift: "Swift", + m: "ObjectiveC", + mm: "ObjectiveCpp", + dart: "Dart", + lua: "Lua", + pl: "Perl", + r: "R", + jl: "Julia", + hs: "Haskell", + elm: "Elm", + ml: "Ocaml", + clj: "Clojure", + cljs: "Clojure", + erl: "Erlang", + ex: "Elixir", + exs: "Elixir", + nim: "Nim", + zig: "Zig", + v: "Vlang", + odin: "Odin", + gleam: "Gleam", + grain: "Grain", + roc: "Rocket", + fs: "Fsharp", + + // Shell scripts + sh: "Console", + bash: "Console", + zsh: "Console", + fish: "Console", + ps1: "Powershell", + + // Config/build files + cfg: "Settings", + ini: "Settings", + conf: "Settings", + properties: "Settings", + + // Media files + svg: "Svg", + png: "Image", + jpg: "Image", + jpeg: "Image", + gif: "Image", + webp: "Image", + bmp: "Image", + ico: "Favicon", + mp4: "Video", + mov: "Video", + avi: "Video", + webm: "Video", + mp3: "Audio", + wav: "Audio", + flac: "Audio", + + // Archive files + zip: "Zip", + tar: "Zip", + gz: "Zip", + rar: "Zip", + "7z": "Zip", + + // Document files + pdf: "Pdf", + doc: "Word", + docx: "Word", + ppt: "Powerpoint", + pptx: "Powerpoint", + xls: "Document", + xlsx: "Document", + + // Database files + sql: "Database", + db: "Database", + sqlite: "Database", + + // Other + env: "Tune", + log: "Log", + lock: "Lock", + key: "Key", + pem: "Certificate", + crt: "Certificate", + proto: "Proto", + graphql: "Graphql", + gql: "Graphql", + wasm: "Webassembly", + dockerfile: "Docker", + }, + folderNames: { + // Source code + src: "FolderSrc", + source: "FolderSrc", + lib: "FolderLib", + libs: "FolderLib", + + // Testing + test: "FolderTest", + tests: "FolderTest", + testing: "FolderTest", + spec: "FolderTest", + specs: "FolderTest", + __tests__: "FolderTest", + e2e: "FolderTest", + integration: "FolderTest", + unit: "FolderTest", + cypress: "FolderCypress", + + // Dependencies + node_modules: "FolderNode", + vendor: "FolderPackages", + packages: "FolderPackages", + deps: "FolderPackages", + + // Build/dist + build: "FolderBuildkite", + dist: "FolderDist", + out: "FolderDist", + output: "FolderDist", + target: "FolderTarget", + + // Configuration + config: "FolderConfig", + configs: "FolderConfig", + configuration: "FolderConfig", + settings: "FolderConfig", + env: "FolderEnvironment", + environments: "FolderEnvironment", + + // Docker + docker: "FolderDocker", + dockerfiles: "FolderDocker", + containers: "FolderDocker", + + // Documentation + docs: "FolderDocs", + doc: "FolderDocs", + documentation: "FolderDocs", + readme: "FolderDocs", + + // Public/assets + public: "FolderPublic", + static: "FolderPublic", + assets: "FolderImages", + images: "FolderImages", + img: "FolderImages", + icons: "FolderImages", + media: "FolderImages", + fonts: "FolderFont", + styles: "FolderCss", + stylesheets: "FolderCss", + css: "FolderCss", + sass: "FolderSass", + scss: "FolderSass", + less: "FolderLess", + + // Scripts + scripts: "FolderScripts", + script: "FolderScripts", + tools: "FolderTools", + utils: "FolderUtils", + utilities: "FolderUtils", + helpers: "FolderHelper", + + // Framework specific + components: "FolderComponents", + component: "FolderComponents", + views: "FolderViews", + view: "FolderViews", + layouts: "FolderLayout", + layout: "FolderLayout", + templates: "FolderTemplate", + template: "FolderTemplate", + hooks: "FolderHook", + hook: "FolderHook", + store: "FolderStore", + stores: "FolderStore", + state: "FolderNgrxStore", + reducers: "FolderReduxReducer", + reducer: "FolderReduxReducer", + services: "FolderApi", + service: "FolderApi", + api: "FolderApi", + apis: "FolderApi", + routes: "FolderRoutes", + route: "FolderRoutes", + routing: "FolderRoutes", + middleware: "FolderMiddleware", + middlewares: "FolderMiddleware", + controllers: "FolderController", + controller: "FolderController", + models: "FolderDatabase", + model: "FolderDatabase", + schemas: "FolderDatabase", + schema: "FolderDatabase", + migrations: "FolderDatabase", + migration: "FolderDatabase", + seeders: "FolderSeeders", + seeder: "FolderSeeders", + + // TypeScript + types: "FolderTypescript", + typing: "FolderTypescript", + typings: "FolderTypescript", + "@types": "FolderTypescript", + interfaces: "FolderInterface", + interface: "FolderInterface", + + // Mobile + android: "FolderAndroid", + ios: "FolderIos", + mobile: "FolderMobile", + flutter: "FolderFlutter", + + // Infrastructure + kubernetes: "FolderKubernetes", + k8s: "FolderKubernetes", + terraform: "FolderTerraform", + aws: "FolderAws", + azure: "FolderAzurePipelines", + firebase: "FolderFirebase", + supabase: "FolderSupabase", + vercel: "FolderVercel", + netlify: "FolderNetlify", + + // CI/CD + ".github": "FolderGithub", + ".gitlab": "FolderGitlab", + ".circleci": "FolderCircleci", + ci: "FolderCi", + ".ci": "FolderCi", + workflows: "FolderGhWorkflows", + + // Git + ".git": "FolderGit", + + // Development tools + ".vscode": "FolderVscode", + ".idea": "FolderIntellij", + ".cursor": "FolderCursor", + ".devcontainer": "FolderContainer", + ".storybook": "FolderStorybook", + + // Localization + i18n: "FolderI18n", + locales: "FolderI18n", + locale: "FolderI18n", + lang: "FolderI18n", + languages: "FolderI18n", + + // Other common patterns + temp: "FolderTemp", + tmp: "FolderTemp", + logs: "FolderLog", + log: "FolderLog", + backup: "FolderBackup", + backups: "FolderBackup", + examples: "FolderExamples", + example: "FolderExamples", + demo: "FolderExamples", + demos: "FolderExamples", + samples: "FolderExamples", + sample: "FolderExamples", + fixtures: "FolderTest", + mocks: "FolderMock", + mock: "FolderMock", + data: "FolderDatabase", + database: "FolderDatabase", + db: "FolderDatabase", + sql: "FolderDatabase", + prisma: "FolderPrisma", + drizzle: "FolderDrizzle", + + // Security + security: "FolderSecure", + auth: "FolderSecure", + authentication: "FolderSecure", + authorization: "FolderSecure", + keys: "FolderKeys", + certs: "FolderKeys", + certificates: "FolderKeys", + + // Content + content: "FolderContent", + posts: "FolderContent", + articles: "FolderContent", + blog: "FolderContent", + + // Functions + functions: "FolderFunctions", + function: "FolderFunctions", + lambda: "FolderFunctions", + lambdas: "FolderFunctions", + serverless: "FolderServerless", + + // Jobs/tasks + jobs: "FolderJob", + job: "FolderJob", + tasks: "FolderTasks", + task: "FolderTasks", + cron: "FolderTasks", + queue: "FolderQueue", + queues: "FolderQueue", + + // Desktop platforms + desktop: "FolderDesktop", + windows: "FolderWindows", + macos: "FolderMacos", + linux: "FolderLinux", + }, + defaults: { + file: "Document", + folder: "Folder", + folderOpen: "FolderOpen", + }, +} + +const toOpenVariant = (icon: IconName): IconName => { + if (!icon.startsWith("Folder")) return icon + if (icon.endsWith("_light")) return icon.replace("_light", "Open_light") as IconName + if (!icon.endsWith("Open")) return (icon + "Open") as IconName + return icon +} + +const basenameOf = (p: string) => + p + .replace(/[/\\]+$/, "") + .split(/[\\/]/) + .pop() ?? "" + +const folderNameVariants = (name: string) => { + const n = name.toLowerCase() + return [n, `.${n}`, `_${n}`, `__${n}__`] +} + +const dottedSuffixesDesc = (name: string) => { + const n = name.toLowerCase() + const idxs: number[] = [] + for (let i = 0; i < n.length; i++) if (n[i] === ".") idxs.push(i) + const out = new Set() + out.add(n) // allow exact whole-name "extensions" like "dockerfile" + for (const i of idxs) if (i + 1 < n.length) out.add(n.slice(i + 1)) + return Array.from(out).sort((a, b) => b.length - a.length) // longest first +} + +export function chooseIconName(path: string, type: "directory" | "file", expanded: boolean): IconName { + const base = basenameOf(path) + const baseLower = base.toLowerCase() + + if (type === "directory") { + for (const cand of folderNameVariants(baseLower)) { + const icon = ICON_MAPS.folderNames[cand] + if (icon) return expanded ? toOpenVariant(icon) : icon + } + return expanded ? ICON_MAPS.defaults.folderOpen : ICON_MAPS.defaults.folder + } + + const byName = ICON_MAPS.fileNames[baseLower] + if (byName) return byName + + for (const ext of dottedSuffixesDesc(baseLower)) { + const icon = ICON_MAPS.fileExtensions[ext] + if (icon) return icon + } + + return ICON_MAPS.defaults.file +} diff --git a/packages/ui/src/components/file-icons/sprite.svg b/packages/ui/src/components/file-icons/sprite.svg new file mode 100644 index 000000000..619b2b58c --- /dev/null +++ b/packages/ui/src/components/file-icons/sprite.svg @@ -0,0 +1,11707 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/src/components/file-icons/types.ts b/packages/ui/src/components/file-icons/types.ts new file mode 100644 index 000000000..5b47661f9 --- /dev/null +++ b/packages/ui/src/components/file-icons/types.ts @@ -0,0 +1,1095 @@ +// This file is generated by icon spritesheet generator + +export const iconNames = [ + "Zip", + "Zig", + "Zeabur_light", + "Zeabur", + "Yarn", + "Yang", + "Yaml", + "Xml", + "Xmake", + "Xaml", + "Wxt", + "Wrangler", + "Word", + "Wolframlanguage", + "Windicss", + "Werf", + "Wepy", + "Webpack", + "Webhint", + "Webassembly", + "Watchman", + "Wally", + "Wallaby", + "Wakatime_light", + "Wakatime", + "VuexStore", + "Vue", + "VueConfig", + "Vscode", + "Vlang", + "Vitest", + "Vite", + "Visualstudio", + "Virtual", + "Vim", + "Video", + "Vfl", + "Verilog", + "Verified", + "Verdaccio", + "Vercel_light", + "Vercel", + "Velocity", + "Velite", + "Vedic", + "Varnish", + "VanillaExtract", + "Vala", + "Vagrant", + "Uv", + "Url", + "Unocss", + "Unity", + "Uml_light", + "Uml", + "Umi", + "Typst", + "Typescript", + "TypescriptDef", + "Twine", + "Twig", + "Turborepo_light", + "Turborepo", + "Tune", + "Tsil", + "Tsdoc", + "Tsconfig", + "Trigger", + "Tree", + "Travis", + "Toml_light", + "Toml", + "Todo", + "Tobimake", + "Tobi", + "Tldraw_light", + "Tldraw", + "Tilt", + "Textlint", + "Tex", + "TestTs", + "TestJsx", + "TestJs", + "Terraform", + "Template", + "Templ", + "Teal", + "Tcl", + "Taze", + "Tauri", + "Taskfile", + "Tailwindcss", + "Table", + "Systemd_light", + "Systemd", + "Syncpack", + "Swift", + "Swc", + "Sway", + "Swagger", + "Svgr", + "Svgo", + "Svg", + "Svelte", + "Supabase", + "Subtitles", + "Sublime", + "Stylus", + "Stylelint_light", + "Stylelint", + "Stylable", + "Stryker", + "Storybook", + "Stitches_light", + "Stitches", + "Stencil", + "Steadybit", + "Stan", + "Stackblitz", + "Spwn", + "Sonarcloud", + "Solidity", + "Snyk", + "Snowpack_light", + "Snowpack", + "Snapcraft", + "Snakemake", + "Sml", + "Smarty", + "Slug", + "Slint", + "Slim", + "Sketch", + "Siyuan", + "Simulink", + "Silverstripe", + "Shader", + "Settings", + "Serverless", + "Sequelize", + "Sentry", + "Semgrep", + "SemanticRelease_light", + "SemanticRelease", + "Search", + "Screwdriver", + "Scons_light", + "Scons", + "Scheme", + "Scala", + "Sbt", + "Sass", + "Sas", + "San", + "Salesforce", + "Rust", + "Ruff", + "Ruby", + "Rubocop_light", + "Rubocop", + "Rspec", + "Routing", + "Rome", + "Rollup", + "Rojo", + "Rocket", + "Robots", + "Robot", + "Roblox", + "Roadmap", + "Riot", + "Restql", + "Rescript", + "RescriptInterface", + "Replit", + "Renovate", + "Remix_light", + "Remix", + "Remark", + "Regedit", + "ReduxStore", + "ReduxSelector", + "ReduxReducer", + "ReduxAction", + "Red", + "Reason", + "Readme", + "React_ts", + "React", + "Rc", + "Rbxmk", + "Razor", + "Raml", + "Racket", + "R", + "Qwik", + "Quokka", + "Quasar", + "Quarto", + "Qsharp", + "Pytorch", + "Python", + "PythonMisc", + "Purescript", + "Puppeteer", + "Puppet", + "Pug", + "Protractor", + "Proto", + "Prompt", + "Prolog", + "Processing", + "Prisma", + "Prettier", + "PreCommit", + "Powershell", + "Powerpoint", + "Posthtml", + "Postcss", + "Poetry", + "Pnpm_light", + "Pnpm", + "Pm2Ecosystem", + "Plop", + "Playwright", + "Plastic", + "Pkl", + "Pipeline", + "Pinejs", + "Phpunit", + "Phpstan", + "Php_elephant_pink", + "Php_elephant", + "Php", + "PhpCsFixer", + "Perl", + "Percy", + "Pdm", + "Pdf", + "Payload_light", + "Payload", + "Pawn", + "Pascal", + "Parcel", + "Panda", + "Palette", + "Packship", + "Oxlint", + "Otne", + "Openapi_light", + "Openapi", + "Opam", + "Opa", + "Odin", + "Ocaml", + "ObjectiveCpp", + "ObjectiveC", + "Nx", + "Nuxt", + "Nunjucks", + "Nuget", + "Npm", + "Nodemon", + "Nodejs_alt", + "Nodejs", + "Nix", + "Nim", + "NgrxState", + "NgrxSelectors", + "NgrxReducer", + "NgrxEntity", + "NgrxEffects", + "NgrxActions", + "Nginx", + "Next_light", + "Next", + "Netlify_light", + "Netlify", + "Nest", + "Ndst", + "NanoStaged_light", + "NanoStaged", + "Mxml", + "Moonscript", + "Moon", + "Mojo", + "Modernizr", + "Mocha", + "Mjml", + "Mint", + "Minecraft", + "MinecraftFabric", + "Meson", + "Mermaid", + "Merlin", + "Mercurial", + "Mdx", + "Mdsvex", + "Maven", + "Matlab", + "Mathematica", + "Markojs", + "Markdownlint", + "Markdown", + "Markdoc", + "MarkdocConfig", + "Makefile", + "Lyric", + "Luau", + "Lua", + "Lottie", + "Lolcode", + "Log", + "Lock", + "Livescript", + "Lisp", + "Liquid", + "Lintstaged", + "Lilypond", + "Lighthouse", + "Lib", + "Liara", + "Less", + "Lerna", + "Lefthook", + "Lbx", + "Latexmk", + "Laravel", + "Label", + "Kusto", + "Kubernetes", + "Kotlin", + "Knip", + "Kl", + "Kivy", + "Keystatic", + "Key", + "Kcl", + "Karma", + "Just", + "Jupyter", + "Julia", + "Jsr_light", + "Jsr", + "Json", + "Jsconfig", + "Jinja_light", + "Jinja", + "Jest", + "Jenkins", + "Javascript", + "JavascriptMap", + "Javaclass", + "Java", + "Jar", + "Istanbul", + "Ionic", + "Installation", + "Imba", + "Image", + "IfanrCloud", + "Idris", + "I18n", + "Husky", + "Hurl", + "Huff_light", + "Huff", + "Http", + "Html", + "Hpp", + "Hosts_light", + "Hosts", + "Horusec", + "Hjson", + "Histoire", + "Hex", + "Heroku", + "Helm", + "Hcl_light", + "Hcl", + "Haxe", + "Haskell", + "Harmonix", + "Hardhat", + "Handlebars", + "Haml", + "Hadolint", + "Hack", + "H", + "Gulp", + "Grunt", + "Groovy", + "Gridsome", + "Graphql", + "Graphcool", + "Grain", + "GrafanaAlloy", + "Gradle", + "Godot", + "GodotAssets", + "Go_gopher", + "Go", + "GoMod", + "Gnuplot", + "Gleam", + "Gitpod", + "Gitlab", + "GithubSponsors", + "GithubActionsWorkflow", + "Git", + "Gemini", + "GeminiAi", + "Gemfile", + "Gcp", + "Gatsby", + "Garden", + "Gamemaker", + "Fusebox", + "Fsharp", + "Freemarker", + "Foxpro", + "Fortran", + "Forth", + "Font", + "Folder", + "FolderZeabur", + "FolderZeaburOpen", + "FolderYarn", + "FolderYarnOpen", + "FolderWordpress", + "FolderWordpressOpen", + "FolderWindows", + "FolderWindowsOpen", + "FolderWebpack", + "FolderWebpackOpen", + "FolderWakatime", + "FolderWakatimeOpen", + "FolderVuexStore", + "FolderVuexStoreOpen", + "FolderVuepress", + "FolderVuepressOpen", + "FolderVue", + "FolderVueOpen", + "FolderVueDirectives", + "FolderVueDirectivesOpen", + "FolderVscode", + "FolderVscodeOpen", + "FolderVm", + "FolderVmOpen", + "FolderViews", + "FolderViewsOpen", + "FolderVideo", + "FolderVideoOpen", + "FolderVerdaccio", + "FolderVerdaccioOpen", + "FolderVercel", + "FolderVercelOpen", + "FolderUtils", + "FolderUtilsOpen", + "FolderUpload", + "FolderUploadOpen", + "FolderUpdate", + "FolderUpdateOpen", + "FolderUnity", + "FolderUnityOpen", + "FolderUi", + "FolderUiOpen", + "FolderTypescript", + "FolderTypescriptOpen", + "FolderTurborepo", + "FolderTurborepoOpen", + "FolderTrigger", + "FolderTriggerOpen", + "FolderTrash", + "FolderTrashOpen", + "FolderTools", + "FolderToolsOpen", + "FolderTheme", + "FolderThemeOpen", + "FolderTest", + "FolderTestOpen", + "FolderTerraform", + "FolderTerraformOpen", + "FolderTemplate", + "FolderTemplateOpen", + "FolderTemp", + "FolderTempOpen", + "FolderTelevision", + "FolderTelevisionOpen", + "FolderTasks", + "FolderTasksOpen", + "FolderTaskfile", + "FolderTaskfileOpen", + "FolderTarget", + "FolderTargetOpen", + "FolderSyntax", + "FolderSyntaxOpen", + "FolderSvg", + "FolderSvgOpen", + "FolderSvelte", + "FolderSvelteOpen", + "FolderSupabase", + "FolderSupabaseOpen", + "FolderSublime", + "FolderSublimeOpen", + "FolderStylus", + "FolderStylusOpen", + "FolderStorybook", + "FolderStorybookOpen", + "FolderStore", + "FolderStoreOpen", + "FolderStencil", + "FolderStencilOpen", + "FolderStack", + "FolderStackOpen", + "FolderSrc", + "FolderSrcTauri", + "FolderSrcTauriOpen", + "FolderSrcOpen", + "FolderSnippet", + "FolderSnippetOpen", + "FolderSnapcraft", + "FolderSnapcraftOpen", + "FolderShared", + "FolderSharedOpen", + "FolderShader", + "FolderShaderOpen", + "FolderServerless", + "FolderServerlessOpen", + "FolderServer", + "FolderServerOpen", + "FolderSeeders", + "FolderSeedersOpen", + "FolderSecure", + "FolderSecureOpen", + "FolderScripts", + "FolderScriptsOpen", + "FolderScons", + "FolderSconsOpen", + "FolderScala", + "FolderScalaOpen", + "FolderSass", + "FolderSassOpen", + "FolderSandbox", + "FolderSandboxOpen", + "FolderRust", + "FolderRustOpen", + "FolderRules", + "FolderRulesOpen", + "FolderRoutes", + "FolderRoutesOpen", + "FolderRobot", + "FolderRobotOpen", + "FolderReview", + "FolderReviewOpen", + "FolderResource", + "FolderResourceOpen", + "FolderResolver", + "FolderResolverOpen", + "FolderRepository", + "FolderRepositoryOpen", + "FolderReduxReducer", + "FolderReduxReducerOpen", + "FolderReactComponents", + "FolderReactComponentsOpen", + "FolderQueue", + "FolderQueueOpen", + "FolderQuasar", + "FolderQuasarOpen", + "FolderPytorch", + "FolderPytorchOpen", + "FolderPython", + "FolderPythonOpen", + "FolderPublic", + "FolderPublicOpen", + "FolderProto", + "FolderProtoOpen", + "FolderPrompts", + "FolderPromptsOpen", + "FolderProject", + "FolderProjectOpen", + "FolderPrivate", + "FolderPrivateOpen", + "FolderPrisma", + "FolderPrismaOpen", + "FolderPowershell", + "FolderPowershellOpen", + "FolderPolicy", + "FolderPolicyOpen", + "FolderPlugin", + "FolderPluginOpen", + "FolderPlastic", + "FolderPlasticOpen", + "FolderPipe", + "FolderPipeOpen", + "FolderPhpmailer", + "FolderPhpmailerOpen", + "FolderPhp", + "FolderPhpOpen", + "FolderPdm", + "FolderPdmOpen", + "FolderPdf", + "FolderPdfOpen", + "FolderPackages", + "FolderPackagesOpen", + "FolderOther", + "FolderOtherOpen", + "FolderOrganism", + "FolderOrganismOpen", + "FolderOpen", + "FolderObsidian", + "FolderObsidianOpen", + "FolderNuxt", + "FolderNuxtOpen", + "FolderNode", + "FolderNodeOpen", + "FolderNgrxStore", + "FolderNgrxStoreOpen", + "FolderNext", + "FolderNextOpen", + "FolderNetlify", + "FolderNetlifyOpen", + "FolderMoon", + "FolderMoonOpen", + "FolderMolecule", + "FolderMoleculeOpen", + "FolderMojo", + "FolderMojoOpen", + "FolderMock", + "FolderMockOpen", + "FolderMobile", + "FolderMobileOpen", + "FolderMjml", + "FolderMjmlOpen", + "FolderMiddleware", + "FolderMiddlewareOpen", + "FolderMeta", + "FolderMetaOpen", + "FolderMessages", + "FolderMessagesOpen", + "FolderMercurial", + "FolderMercurialOpen", + "FolderMarkdown", + "FolderMarkdownOpen", + "FolderMappings", + "FolderMappingsOpen", + "FolderMail", + "FolderMailOpen", + "FolderMacos", + "FolderMacosOpen", + "FolderLuau", + "FolderLuauOpen", + "FolderLua", + "FolderLuaOpen", + "FolderLottie", + "FolderLottieOpen", + "FolderLog", + "FolderLogOpen", + "FolderLiquibase", + "FolderLiquibaseOpen", + "FolderLinux", + "FolderLinuxOpen", + "FolderLink", + "FolderLinkOpen", + "FolderLib", + "FolderLibOpen", + "FolderLess", + "FolderLessOpen", + "FolderLefthook", + "FolderLefthookOpen", + "FolderLayout", + "FolderLayoutOpen", + "FolderKusto", + "FolderKustoOpen", + "FolderKubernetes", + "FolderKubernetesOpen", + "FolderKeys", + "FolderKeysOpen", + "FolderJupyter", + "FolderJupyterOpen", + "FolderJson", + "FolderJsonOpen", + "FolderJob", + "FolderJobOpen", + "FolderJinja_light", + "FolderJinja", + "FolderJinjaOpen_light", + "FolderJinjaOpen", + "FolderJavascript", + "FolderJavascriptOpen", + "FolderJava", + "FolderJavaOpen", + "FolderIos", + "FolderIosOpen", + "FolderInterface", + "FolderInterfaceOpen", + "FolderInterceptor", + "FolderInterceptorOpen", + "FolderIntellij_light", + "FolderIntellij", + "FolderIntellijOpen_light", + "FolderIntellijOpen", + "FolderInclude", + "FolderIncludeOpen", + "FolderImport", + "FolderImportOpen", + "FolderImages", + "FolderImagesOpen", + "FolderI18n", + "FolderI18nOpen", + "FolderHusky", + "FolderHuskyOpen", + "FolderHook", + "FolderHookOpen", + "FolderHome", + "FolderHomeOpen", + "FolderHelper", + "FolderHelperOpen", + "FolderHelm", + "FolderHelmOpen", + "FolderGulp", + "FolderGulpOpen", + "FolderGuard", + "FolderGuardOpen", + "FolderGraphql", + "FolderGraphqlOpen", + "FolderGradle", + "FolderGradleOpen", + "FolderGodot", + "FolderGodotOpen", + "FolderGlobal", + "FolderGlobalOpen", + "FolderGitlab", + "FolderGitlabOpen", + "FolderGithub", + "FolderGithubOpen", + "FolderGitea", + "FolderGiteaOpen", + "FolderGit", + "FolderGitOpen", + "FolderGhWorkflows", + "FolderGhWorkflowsOpen", + "FolderGenerator", + "FolderGeneratorOpen", + "FolderGamemaker", + "FolderGamemakerOpen", + "FolderFunctions", + "FolderFunctionsOpen", + "FolderForgejo", + "FolderForgejoOpen", + "FolderFont", + "FolderFontOpen", + "FolderFlutter", + "FolderFlutterOpen", + "FolderFlow", + "FolderFlowOpen", + "FolderFirestore", + "FolderFirestoreOpen", + "FolderFirebase", + "FolderFirebaseOpen", + "FolderFavicon", + "FolderFaviconOpen", + "FolderFastlane", + "FolderFastlaneOpen", + "FolderExport", + "FolderExportOpen", + "FolderExpo", + "FolderExpoOpen", + "FolderExamples", + "FolderExamplesOpen", + "FolderEvent", + "FolderEventOpen", + "FolderError", + "FolderErrorOpen", + "FolderEnvironment", + "FolderEnvironmentOpen", + "FolderEnum", + "FolderEnumOpen", + "FolderElement", + "FolderElementOpen", + "FolderDump", + "FolderDumpOpen", + "FolderDrizzle", + "FolderDrizzleOpen", + "FolderDownload", + "FolderDownloadOpen", + "FolderDocs", + "FolderDocsOpen", + "FolderDocker", + "FolderDockerOpen", + "FolderDist", + "FolderDistOpen", + "FolderDirective", + "FolderDirectiveOpen", + "FolderDesktop", + "FolderDesktopOpen", + "FolderDelta", + "FolderDeltaOpen", + "FolderDecorators", + "FolderDecoratorsOpen", + "FolderDebug", + "FolderDebugOpen", + "FolderDatabase", + "FolderDatabaseOpen", + "FolderDart", + "FolderDartOpen", + "FolderCypress", + "FolderCypressOpen", + "FolderCustom", + "FolderCustomOpen", + "FolderCursor_light", + "FolderCursor", + "FolderCursorOpen_light", + "FolderCursorOpen", + "FolderCss", + "FolderCssOpen", + "FolderCoverage", + "FolderCoverageOpen", + "FolderCore", + "FolderCoreOpen", + "FolderController", + "FolderControllerOpen", + "FolderContract", + "FolderContractOpen", + "FolderContext", + "FolderContextOpen", + "FolderContent", + "FolderContentOpen", + "FolderContainer", + "FolderContainerOpen", + "FolderConstant", + "FolderConstantOpen", + "FolderConsole", + "FolderConsoleOpen", + "FolderConnection", + "FolderConnectionOpen", + "FolderConfig", + "FolderConfigOpen", + "FolderComponents", + "FolderComponentsOpen", + "FolderCommand", + "FolderCommandOpen", + "FolderCobol", + "FolderCobolOpen", + "FolderCluster", + "FolderClusterOpen", + "FolderCloudflare", + "FolderCloudflareOpen", + "FolderCloudFunctions", + "FolderCloudFunctionsOpen", + "FolderCline", + "FolderClineOpen", + "FolderClient", + "FolderClientOpen", + "FolderClaude", + "FolderClaudeOpen", + "FolderClass", + "FolderClassOpen", + "FolderCircleci", + "FolderCircleciOpen", + "FolderCi", + "FolderCiOpen", + "FolderChangesets", + "FolderChangesetsOpen", + "FolderCart", + "FolderCartOpen", + "FolderBuildkite", + "FolderBuildkiteOpen", + "FolderBower", + "FolderBowerOpen", + "FolderBloc", + "FolderBlocOpen", + "FolderBlender", + "FolderBlenderOpen", + "FolderBicep", + "FolderBicepOpen", + "FolderBibliography", + "FolderBibliographyOpen", + "FolderBenchmark", + "FolderBenchmarkOpen", + "FolderBatch", + "FolderBatchOpen", + "FolderBase", + "FolderBaseOpen", + "FolderBackup", + "FolderBackupOpen", + "FolderAzurePipelines", + "FolderAzurePipelinesOpen", + "FolderAws", + "FolderAwsOpen", + "FolderAurelia", + "FolderAureliaOpen", + "FolderAudio", + "FolderAudioOpen", + "FolderAttachment", + "FolderAttachmentOpen", + "FolderAtom", + "FolderAtomOpen", + "FolderAstro", + "FolderAstroOpen", + "FolderArchive", + "FolderArchiveOpen", + "FolderApp", + "FolderAppOpen", + "FolderApollo", + "FolderApolloOpen", + "FolderApi", + "FolderApiOpen", + "FolderAnsible", + "FolderAnsibleOpen", + "FolderAnimation", + "FolderAnimationOpen", + "FolderAngular", + "FolderAngularOpen", + "FolderAndroid", + "FolderAndroidOpen", + "FolderAdmin", + "FolderAdminOpen", + "Flow", + "Flash", + "Firebase", + "Figma", + "Favicon", + "Fastlane", + "Exe", + "Excalidraw", + "Eslint", + "Esbuild", + "Erlang", + "Epub", + "Ember", + "Email", + "Elm", + "Elixir", + "Ejs", + "Editorconfig", + "Edge", + "Dune", + "Duc", + "Drone_light", + "Drone", + "Drizzle", + "Drawio", + "Dotjs", + "Document", + "DoctexInstaller", + "Docker", + "Dll", + "Django", + "Disc", + "Dinophp", + "Diff", + "Dhall", + "DependenciesUpdate", + "Dependabot", + "Deno_light", + "Deno", + "Denizenscript", + "Deepsource", + "Database", + "Dart_generated", + "Dart", + "D", + "Cypress", + "Cursor_light", + "Cursor", + "Cuda", + "Cucumber", + "Css", + "CssMap", + "Csharp", + "Crystal_light", + "Crystal", + "Credits", + "Craco", + "Cpp", + "Copilot_light", + "Copilot", + "Controller", + "Contributing", + "Context", + "Contentlayer", + "Console", + "Conduct", + "Concourse", + "Commitlint", + "Commitizen", + "Command", + "Coloredpetrinets", + "Coldfusion", + "Coffee", + "CoderabbitAi", + "Codeowners", + "Codecov", + "CodeClimate_light", + "CodeClimate", + "Coconut", + "Cobol", + "Coala", + "Cmake", + "Cloudfoundry", + "Clojure", + "Cline", + "Claude", + "Clangd", + "Citation", + "Circleci_light", + "Circleci", + "Chrome", + "Chess_light", + "Chess", + "Changelog", + "Certificate", + "Cds", + "Cbx", + "Capnp", + "Capacitor", + "Cake", + "Cairo", + "Cadence", + "Caddy", + "Cabal", + "C3", + "C", + "Bun_light", + "Bun", + "Buildkite", + "Bucklescript", + "Buck", + "Bruno", + "Browserlist_light", + "Browserlist", + "Brainfuck", + "Bower", + "Blitz", + "Blink_light", + "Blink", + "Blender", + "Bithound", + "Bitbucket", + "Biome", + "Bicep", + "BibtexStyle", + "Bibliography", + "BenchTs", + "BenchJsx", + "BenchJs", + "Beancount", + "Bbx", + "Bazel", + "Ballerina", + "Babel", + "Azure", + "AzurePipelines", + "Autoit", + "Autohotkey", + "Auto_light", + "Auto", + "Authors", + "Aurelia", + "Audio", + "Astyle", + "Astro", + "AstroConfig", + "Assembly", + "Asciidoc", + "Arduino", + "Architecture", + "Appveyor", + "AppsScript", + "Applescript", + "Apollo", + "Apiblueprint", + "Antlr", + "Angular", + "Android", + "Amplify", + "Advpl", + "Adonis", + "AdobeSwc", + "AdobePhotoshop_light", + "AdobePhotoshop", + "AdobeIllustrator_light", + "AdobeIllustrator", + "Ada", + "Actionscript", + "Abc", + "Abap", + "3d", +] as const + +export type IconName = (typeof iconNames)[number] diff --git a/packages/ui/src/components/icon-button.css b/packages/ui/src/components/icon-button.css index 1d9b4be00..2584541b3 100644 --- a/packages/ui/src/components/icon-button.css +++ b/packages/ui/src/components/icon-button.css @@ -11,7 +11,7 @@ &[data-variant="primary"] { background-color: var(--icon-strong-base); - [data-slot="icon"] { + [data-slot="icon-button-icon"] { /* color: var(--icon-weak-base); */ color: var(--icon-invert-base); @@ -35,7 +35,7 @@ &:disabled { background-color: var(--icon-strong-disabled); - [data-slot="icon"] { + [data-slot="icon-button-icon"] { color: var(--icon-invert-base); } } @@ -64,7 +64,7 @@ background-color: var(--button-secondary-base); } - [data-slot="icon"] { + [data-slot="icon-button-icon"] { color: var(--icon-strong-base); } } @@ -73,14 +73,14 @@ background-color: transparent; /* color: var(--icon-base); */ - [data-slot="icon"] { + [data-slot="icon-button-icon"] { color: var(--icon-base); } &:hover:not(:disabled) { background-color: var(--surface-raised-base-hover); - /* [data-slot="icon"] { */ + /* [data-slot="icon-button-icon"] { */ /* color: var(--icon-hover); */ /* } */ } @@ -89,13 +89,13 @@ } &:active:not(:disabled) { background-color: var(--surface-raised-base-active); - /* [data-slot="icon"] { */ + /* [data-slot="icon-button-icon"] { */ /* color: var(--icon-active); */ /* } */ } &:selected:not(:disabled) { background-color: var(--surface-raised-base-active); - /* [data-slot="icon"] { */ + /* [data-slot="icon-button-icon"] { */ /* color: var(--icon-selected); */ /* } */ } @@ -115,7 +115,7 @@ /* padding: 0 8px 0 6px; */ gap: 8px; - [data-slot="icon"] { + [data-slot="icon-button-icon"] { height: 16px; width: 16px; } diff --git a/packages/ui/src/components/icon-button.tsx b/packages/ui/src/components/icon-button.tsx index 79a7cb9c9..a3d91d830 100644 --- a/packages/ui/src/components/icon-button.tsx +++ b/packages/ui/src/components/icon-button.tsx @@ -22,7 +22,11 @@ export function IconButton(props: ComponentProps<"button"> & IconButtonProps) { [split.class ?? ""]: !!split.class, }} > - + ) } diff --git a/packages/ui/src/components/icon.css b/packages/ui/src/components/icon.css index 7f1f18339..14a52bc83 100644 --- a/packages/ui/src/components/icon.css +++ b/packages/ui/src/components/icon.css @@ -22,7 +22,7 @@ height: 24px; } - [data-slot="svg"] { + [data-slot="icon-svg"] { width: 100%; height: auto; } diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index f2594cdaa..306d79649 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -165,6 +165,8 @@ const newIcons = { "folder-add-left": ``, "settings-gear": ``, "bubble-5": ``, + github: ``, + discord: ``, } export interface IconProps extends ComponentProps<"svg"> { @@ -179,7 +181,7 @@ export function Icon(props: IconProps) { return (
- + {local.label} - + - {local.description} + {local.description} - + ) } diff --git a/packages/ui/src/components/list.css b/packages/ui/src/components/list.css deleted file mode 100644 index 2136c4f92..000000000 --- a/packages/ui/src/components/list.css +++ /dev/null @@ -1,30 +0,0 @@ -[data-component="list"] { - /* overflow-y: auto; */ - - /* Hide scrollbar */ - &::-webkit-scrollbar { - display: none; - } - -ms-overflow-style: none; - scrollbar-width: none; - - [data-slot="item"] { - width: 100%; - margin-bottom: 6px; - padding: 4px 12px; - text-align: left; - - border-radius: var(--radius-md); - transition: background-color 0.2s ease-in-out; - - &[data-active="true"] { - background-color: var(--surface-raised-base-hover); - } - &:hover { - background-color: var(--surface-raised-base-hover); - } - &:focus { - outline: none; - } - } -} diff --git a/packages/ui/src/components/list.tsx b/packages/ui/src/components/list.tsx deleted file mode 100644 index aaba61fdf..000000000 --- a/packages/ui/src/components/list.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { ComponentProps, createEffect, createSignal, type JSX } from "solid-js" -import { VirtualizerHandle, VList } from "virtua/solid" -import { createList } from "solid-list" -import { createStore } from "solid-js/store" - -export interface ListProps { - data: T[] - children: (x: T) => JSX.Element - key: (x: T) => string - current?: T - onSelect?: (value: T | undefined) => void - onHover?: (value: T | undefined) => void - class?: ComponentProps<"div">["class"] -} - -export function List(props: ListProps) { - const [virtualizer, setVirtualizer] = createSignal(undefined) - const [store, setStore] = createStore({ - mouseActive: false, - }) - const list = createList({ - items: () => props.data.map(props.key), - initialActive: props.current ? props.key(props.current) : undefined, - loop: true, - }) - - createEffect(() => { - if (props.current) list.setActive(props.key(props.current)) - }) - // const resetSelection = () => { - // if (props.data.length === 0) return - // list.setActive(props.key(props.data[0])) - // } - const handleSelect = (item: T) => { - props.onSelect?.(item) - list.setActive(props.key(item)) - } - - const handleKey = (e: KeyboardEvent) => { - setStore("mouseActive", false) - - if (e.key === "Enter") { - e.preventDefault() - const selected = props.data.find((x) => props.key(x) === list.active()) - if (selected) handleSelect(selected) - } else { - list.onKeyDown(e) - } - } - - createEffect(() => { - if (store.mouseActive || props.data.length === 0) return - const index = props.data.findIndex((x) => props.key(x) === list.active()) - props.onHover?.(props.data[index]) - if (index === 0) { - virtualizer()?.scrollTo(0) - return - } - // virtualizer()?.scrollTo(list.active()) - // const element = virtualizer()?.querySelector(`[data-key="${list.active()}"]`) - // element?.scrollIntoView({ block: "nearest", behavior: "smooth" }) - }) - - return ( - - {(item) => ( - - )} - - ) -} diff --git a/packages/ui/src/components/logo.css b/packages/ui/src/components/logo.css index 36e871859..a909782b7 100644 --- a/packages/ui/src/components/logo.css +++ b/packages/ui/src/components/logo.css @@ -1,4 +1,4 @@ [data-component="logo-mark"] { width: 16px; - height: 20px; + aspect-ratio: 4/5; } diff --git a/packages/ui/src/components/logo.tsx b/packages/ui/src/components/logo.tsx index d3588f301..5ddf3fba3 100644 --- a/packages/ui/src/components/logo.tsx +++ b/packages/ui/src/components/logo.tsx @@ -7,8 +7,38 @@ export const Mark = (props: { class?: string }) => { fill="none" xmlns="http://www.w3.org/2000/svg" > - - + + + + ) +} + +export const Logo = (props: { class?: string }) => { + return ( + + + + + + + + + + + + + + + + + + + ) } diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css index 2ca0aaae5..dd6166112 100644 --- a/packages/ui/src/components/message-part.css +++ b/packages/ui/src/components/message-part.css @@ -34,18 +34,18 @@ align-items: start; gap: 8px; - [data-slot="tool-error-icon"] { + [data-slot="message-part-tool-error-icon"] { color: var(--icon-critical-base); margin-top: 4px; } - [data-slot="tool-error-content"] { + [data-slot="message-part-tool-error-content"] { display: flex; align-items: start; gap: 8px; } - [data-slot="tool-error-title"] { + [data-slot="message-part-tool-error-title"] { font-family: var(--font-family-sans); font-size: var(--font-size-small); font-style: normal; @@ -56,7 +56,7 @@ white-space: nowrap; } - [data-slot="tool-error-message"] { + [data-slot="message-part-tool-error-message"] { color: var(--text-on-critical-weak); } } @@ -72,13 +72,13 @@ justify-content: space-between; width: 100%; - [data-slot="title-area"] { + [data-slot="message-part-title-area"] { display: flex; align-items: center; gap: 8px; } - [data-slot="title"] { + [data-slot="message-part-title"] { font-family: var(--font-family-sans); font-size: var(--font-size-small); font-style: normal; @@ -89,19 +89,19 @@ text-transform: capitalize; } - [data-slot="path"] { + [data-slot="message-part-path"] { display: flex; } - [data-slot="directory"] { + [data-slot="message-part-directory"] { color: var(--text-weak); } - [data-slot="filename"] { + [data-slot="message-part-filename"] { color: var(--text-strong); } - [data-slot="actions"] { + [data-slot="message-part-actions"] { display: flex; gap: 16px; align-items: center; @@ -127,7 +127,7 @@ flex-direction: column; gap: 8px; - [data-slot="todo-content"] { + [data-slot="message-part-todo-content"] { &[data-completed="completed"] { text-decoration: line-through; color: var(--text-weaker); diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 74d9a3113..dd920f101 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -141,16 +141,16 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) { return (
- + -
-
{title}
- {rest.join(": ")} +
+
{title}
+ {rest.join(": ")}
- {cleaned} + {cleaned}
@@ -340,16 +340,16 @@ ToolRegistry.register({ icon="code-lines" trigger={
-
-
Edit
-
+
+
Edit
+
- {getDirectory(props.input.filePath!)} + {getDirectory(props.input.filePath!)} - {getFilename(props.input.filePath ?? "")} + {getFilename(props.input.filePath ?? "")}
-
+
@@ -384,16 +384,16 @@ ToolRegistry.register({ icon="code-lines" trigger={
-
-
Write
-
+
+
Write
+
- {getDirectory(props.input.filePath!)} + {getDirectory(props.input.filePath!)} - {getFilename(props.input.filePath ?? "")} + {getFilename(props.input.filePath ?? "")}
-
{/* */}
+
{/* */}
} > @@ -421,7 +421,7 @@ ToolRegistry.register({ {(todo: any) => ( -
+
{todo.content}
diff --git a/packages/ui/src/components/message-progress.css b/packages/ui/src/components/message-progress.css new file mode 100644 index 000000000..0b84e0393 --- /dev/null +++ b/packages/ui/src/components/message-progress.css @@ -0,0 +1,50 @@ +[data-component="message-progress"] { + display: flex; + flex-direction: column; + gap: 12px; +} + +[data-component="message-progress"] [data-slot="message-progress-status"] { + display: flex; + align-items: center; + column-gap: 20px; + padding-left: 12px; + border: 1px solid transparent; + color: var(--text-base); +} + +[data-component="message-progress"] [data-slot="message-progress-status-text"] { + font-size: 12px; + font-weight: 500; + line-height: 1.5; +} + +[data-component="message-progress"] [data-slot="message-progress-list-container"] { + height: 120px; + overflow: hidden; + pointer-events: none; + padding-bottom: 4px; + + mask-image: linear-gradient(to bottom, transparent 0%, black 33%, black 95%, transparent 100%); + -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 33%, black 95%, transparent 100%); +} + +[data-component="message-progress"] [data-slot="message-progress-list"] { + width: 100%; + display: flex; + flex-direction: column; + align-items: flex-start; + align-self: stretch; + gap: 8px; + padding-top: 32px; + padding-bottom: 32px; + + transition: transform 500ms cubic-bezier(0.22, 1, 0.36, 1); +} + +[data-component="message-progress"] [data-slot="message-progress-item"] { + height: 32px; + display: flex; + align-items: center; + width: 100%; +} diff --git a/packages/ui/src/components/message-progress.tsx b/packages/ui/src/components/message-progress.tsx new file mode 100644 index 000000000..48a234535 --- /dev/null +++ b/packages/ui/src/components/message-progress.tsx @@ -0,0 +1,172 @@ +import { For, JSXElement, Match, Show, Switch, createEffect, createMemo, createSignal, onCleanup } from "solid-js" +import { Part } from "./message-part" +import { Spinner } from "./spinner" +import { useData } from "../context/data" +import type { AssistantMessage as AssistantMessageType, ToolPart } from "@opencode-ai/sdk" +import "./message-progress.css" + +export function MessageProgress(props: { assistantMessages: () => AssistantMessageType[]; done?: boolean }) { + const data = useData() + const parts = createMemo(() => props.assistantMessages().flatMap((m) => data.part[m.id])) + const done = createMemo(() => props.done ?? false) + const currentTask = createMemo( + () => + parts().findLast( + (p) => + p && + p.type === "tool" && + p.tool === "task" && + p.state && + "metadata" in p.state && + p.state.metadata && + p.state.metadata.sessionId && + p.state.status === "running", + ) as ToolPart, + ) + const resolvedParts = createMemo(() => { + let resolved = parts() + const task = currentTask() + if (task && task.state && "metadata" in task.state && task.state.metadata?.sessionId) { + const messages = data.message[task.state.metadata.sessionId as string]?.filter((m) => m.role === "assistant") + resolved = messages?.flatMap((m) => data.part[m.id]) ?? parts() + } + return resolved + }) + + const eligibleItems = createMemo(() => { + return resolvedParts().filter((p) => p?.type === "tool" && p?.state.status === "completed") as ToolPart[] + }) + const finishedItems = createMemo<(JSXElement | ToolPart)[]>(() => [ +
, +
, +
, + ...eligibleItems(), + ...(done() + ? [ +
, +
, +
, + ] + : []), + ]) + + const delay = createMemo(() => (done() ? 220 : 400)) + const [visibleCount, setVisibleCount] = createSignal(eligibleItems().length) + + createEffect(() => { + const total = finishedItems().length + if (total > visibleCount()) { + const timer = setTimeout(() => { + setVisibleCount((prev) => prev + 1) + }, delay()) + onCleanup(() => clearTimeout(timer)) + } else if (total < visibleCount()) { + setVisibleCount(total) + } + }) + + const translateY = createMemo(() => { + const total = visibleCount() + if (total < 2) return "0px" + return `-${(total - 2) * 40 - 8}px` + }) + + const lastPart = createMemo(() => resolvedParts().slice(-1)?.at(0)) + const rawStatus = createMemo(() => { + const last = lastPart() + if (!last) return undefined + + if (last.type === "tool") { + switch (last.tool) { + case "task": + return "Delegating work..." + case "todowrite": + case "todoread": + return "Planning next steps..." + case "read": + return "Gathering context..." + case "list": + case "grep": + case "glob": + return "Searching the codebase..." + case "webfetch": + return "Searching the web..." + case "edit": + case "write": + return "Making edits..." + case "bash": + return "Running commands..." + default: + break + } + } else if (last.type === "reasoning") { + return "Thinking..." + } else if (last.type === "text") { + return "Gathering thoughts..." + } + return undefined + }) + + const [status, setStatus] = createSignal(rawStatus()) + let lastStatusChange = Date.now() + let statusTimeout: number | undefined + + createEffect(() => { + const newStatus = rawStatus() + if (newStatus === status() || !newStatus) return + + const timeSinceLastChange = Date.now() - lastStatusChange + + if (timeSinceLastChange >= 1500) { + setStatus(newStatus) + lastStatusChange = Date.now() + if (statusTimeout) { + clearTimeout(statusTimeout) + statusTimeout = undefined + } + } else { + if (statusTimeout) clearTimeout(statusTimeout) + statusTimeout = setTimeout(() => { + setStatus(rawStatus()) + lastStatusChange = Date.now() + statusTimeout = undefined + }, 1000 - timeSinceLastChange) as unknown as number + } + }) + + return ( +
+
+ {status() ?? "Considering next steps..."} +
+ 0}> +
+
+ + {(part) => ( + + + {(p) => { + const part = p() as ToolPart + const message = createMemo(() => + data.message[part.sessionID].find((m) => m.id === part.messageID), + ) + return ( +
+ +
+ ) + }} +
+ +
{part as JSXElement}
+
+
+ )} +
+
+
+
+
+ ) +} diff --git a/packages/ui/src/components/progress-circle.css b/packages/ui/src/components/progress-circle.css index 591825183..afaf72af6 100644 --- a/packages/ui/src/components/progress-circle.css +++ b/packages/ui/src/components/progress-circle.css @@ -1,11 +1,11 @@ [data-component="progress-circle"] { transform: rotate(-90deg); - [data-slot="background"] { + [data-slot="progress-circle-background"] { stroke: var(--border-weak-base); } - [data-slot="progress"] { + [data-slot="progress-circle-progress"] { stroke: var(--border-active); transition: stroke-dashoffset 0.35s cubic-bezier(0.65, 0, 0.35, 1); } diff --git a/packages/ui/src/components/progress-circle.tsx b/packages/ui/src/components/progress-circle.tsx index f2e497fe5..02bd36bb7 100644 --- a/packages/ui/src/components/progress-circle.tsx +++ b/packages/ui/src/components/progress-circle.tsx @@ -36,12 +36,18 @@ export function ProgressCircle(props: ProgressCircleProps) { [split.class ?? ""]: !!split.class, }} > - + extends FilteredListProps, @@ -83,10 +86,10 @@ export function SelectDialog(props: SelectDialogProps) {
-
- +
+ handleInput(value)} @@ -101,7 +104,7 @@ export function SelectDialog(props: SelectDialogProps) {
{ @@ -115,24 +118,25 @@ export function SelectDialog(props: SelectDialogProps) { 0} fallback={ -
-
- {props.emptyMessage ?? "No search results"} for "{filter()}" +
+
+ {props.emptyMessage ?? "No search results"} for{" "} + "{filter()}"
} > {(group) => ( -
+
-
{group.category}
+
{group.category}
-
+
{(item) => ( + {props.actions} +
+
+ + + {(diff) => ( + + + +
+
+ +
+ + {getDirectory(diff.file)}‎ + + {getFilename(diff.file)} +
+
+
+ + +
+
+
+
+ + + +
+ )} +
+
+
+ ) +} diff --git a/packages/ui/src/components/session-timeline.css b/packages/ui/src/components/session-timeline.css new file mode 100644 index 000000000..e86e80e6e --- /dev/null +++ b/packages/ui/src/components/session-timeline.css @@ -0,0 +1,324 @@ +[data-component="session-timeline"] { + /* flex: 1; */ + min-height: 0; + display: flex; + align-items: flex-start; + justify-content: flex-start; + + [data-slot="session-timeline-timeline-list"] { + margin-right: 32px; + flex-shrink: 0; + display: flex; + flex-direction: column; + align-items: flex-start; + margin-top: 12px; + + &[data-expanded="true"] { + position: absolute; + right: 100%; + width: 240px; + margin-top: 12px; + + @media (min-width: 80rem) { + gap: 8px; + margin-top: 4px; + } + } + } + + [data-slot="session-timeline-timeline-item"] { + display: flex; + align-items: center; + align-self: stretch; + justify-content: flex-end; + + &[data-expanded="true"] { + @media (min-width: 80rem) { + justify-content: flex-start; + } + } + } + + [data-slot="session-timeline-tick-button"] { + display: flex; + align-items: center; + justify-content: flex-start; + height: 8px; + width: 32px; + margin-right: -12px; + cursor: pointer; + border: none; + background: none; + padding: 0; + + &[data-active="true"] [data-slot="session-timeline-tick-line"] { + background-color: var(--icon-strong-base); + width: 100%; + } + + &[data-expanded="true"] { + @media (min-width: 80rem) { + display: none; + } + } + } + + [data-slot="session-timeline-tick-line"] { + height: 1px; + width: 20px; + background-color: var(--icon-base); + transition: + width 0.2s, + background-color 0.2s; + } + + [data-slot="session-timeline-tick-button"]:hover [data-slot="session-timeline-tick-line"] { + width: 100%; + background-color: var(--icon-strong-base); + } + + [data-slot="session-timeline-message-button"] { + display: none; + align-items: center; + align-self: stretch; + width: 100%; + column-gap: 8px; + cursor: default; + border: none; + background: none; + padding: 0; + + &[data-expanded="true"] { + @media (min-width: 80rem) { + display: flex; + } + } + } + + [data-slot="session-timeline-message-title-preview"] { + font-size: 14px; /* text-14-regular */ + color: var(--text-weak); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; + text-align: left; + + &[data-active="true"] { + color: var(--text-strong); + } + } + + [data-slot="session-timeline-timeline-item"]:hover [data-slot="session-timeline-message-title-preview"] { + color: var(--text-base); + } + + [data-slot="session-timeline-content"] { + flex-grow: 1; + width: 100%; + height: 100%; + min-width: 0; + overflow-y: auto; + scrollbar-width: none; + } + + [data-slot="session-timeline-content"]::-webkit-scrollbar { + display: none; + } + + [data-slot="session-timeline-message-container"] { + display: flex; + flex-direction: column; + align-items: flex-start; + align-self: stretch; + gap: 32px; + } + + [data-slot="session-timeline-message-header"] { + display: flex; + align-items: center; + gap: 8px; + align-self: stretch; + position: sticky; + top: 0; + background-color: var(--background-stronger); + z-index: 20; + height: 32px; + } + + [data-slot="session-timeline-message-content"] { + margin-top: -24px; + } + + [data-slot="session-timeline-message-title"] { + width: 100%; + font-size: 14px; /* text-14-medium */ + font-weight: 500; + color: var(--text-strong); + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; + white-space: nowrap; + } + + [data-slot="session-timeline-message-title"] h1 { + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; + white-space: nowrap; + font-size: inherit; + font-weight: inherit; + } + + [data-slot="session-timeline-typewriter"] { + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; + white-space: nowrap; + } + + [data-slot="session-timeline-summary-section"] { + width: 100%; + display: flex; + flex-direction: column; + gap: 24px; + align-items: flex-start; + align-self: stretch; + } + + [data-slot="session-timeline-summary-header"] { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; + align-self: stretch; + } + + [data-slot="session-timeline-summary-title"] { + font-size: 12px; /* text-12-medium */ + font-weight: 500; + color: var(--text-weak); + } + + [data-slot="session-timeline-markdown"] { + &[data-diffs="true"] { + font-size: 14px; /* text-14-regular */ + } + + &[data-fade="true"] > * { + animation: fade-up-text 0.3s ease-out forwards; + } + } + + [data-slot="session-timeline-accordion"] { + width: 100%; + } + + [data-component="sticky-accordion-header"] { + top: 40px; + + &[data-expanded]::before { + top: -40px; + } + } + + [data-slot="session-timeline-accordion-trigger-content"] { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + gap: 20px; + } + + [data-slot="session-timeline-file-info"] { + flex-grow: 1; + display: flex; + align-items: center; + gap: 20px; + min-width: 0; + } + + [data-slot="session-timeline-file-icon"] { + flex-shrink: 0; + width: 16px; + height: 16px; + } + + [data-slot="session-timeline-file-path"] { + display: flex; + flex-grow: 1; + min-width: 0; + } + + [data-slot="session-timeline-directory"] { + color: var(--text-base); + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + direction: rtl; + text-align: left; + } + + [data-slot="session-timeline-filename"] { + color: var(--text-strong); + flex-shrink: 0; + } + + [data-slot="session-timeline-accordion-actions"] { + flex-shrink: 0; + display: flex; + gap: 16px; + align-items: center; + justify-content: flex-end; + } + + [data-slot="session-timeline-accordion-content"] { + max-height: 240px; /* max-h-60 */ + overflow-y: auto; + scrollbar-width: none; + } + + [data-slot="session-timeline-accordion-content"]::-webkit-scrollbar { + display: none; + } + + [data-slot="session-timeline-response-section"] { + width: 100%; + } + + [data-slot="session-timeline-collapsible-trigger-content"] { + color: var(--text-weak); + cursor: pointer; + background: none; + border: none; + padding: 0; + display: flex; + align-items: center; + + &:hover { + color: var(--text-strong); + } + display: flex; + align-items: center; + gap: 4px; + align-self: stretch; + } + + [data-slot="session-timeline-details-text"] { + font-size: 12px; /* text-12-medium */ + font-weight: 500; + } + + .error-card { + color: var(--text-on-critical-base); + } + + [data-slot="session-timeline-collapsible-content-inner"] { + width: 100%; + display: flex; + flex-direction: column; + align-items: flex-start; + align-self: stretch; + gap: 12px; + } +} diff --git a/packages/ui/src/components/session-timeline.tsx b/packages/ui/src/components/session-timeline.tsx new file mode 100644 index 000000000..5d451b39d --- /dev/null +++ b/packages/ui/src/components/session-timeline.tsx @@ -0,0 +1,289 @@ +import { AssistantMessage } from "@opencode-ai/sdk" +import { useData } from "../context" +import { Binary } from "@opencode-ai/util/binary" +import { getDirectory, getFilename } from "@opencode-ai/util/path" +import { createEffect, createMemo, createSignal, For, Match, ParentProps, Show, Switch } from "solid-js" +import { createStore } from "solid-js/store" +import { DiffChanges } from "./diff-changes" +import { Spinner } from "./spinner" +import { Typewriter } from "./typewriter" +import { Message } from "./message-part" +import { Markdown } from "./markdown" +import { Accordion } from "./accordion" +import { StickyAccordionHeader } from "./sticky-accordion-header" +import { FileIcon } from "./file-icon" +import { Icon } from "./icon" +import { Diff } from "./diff" +import { Card } from "./card" +import { MessageProgress } from "./message-progress" +import { Collapsible } from "./collapsible" + +export function SessionTimeline( + props: ParentProps<{ + sessionID: string + classes?: { + root?: string + content?: string + container?: string + } + expanded?: boolean + }>, +) { + const data = useData() + const [store, setStore] = createStore({ + messageId: undefined as string | undefined, + }) + const match = Binary.search(data.session, props.sessionID, (s) => s.id) + if (!match.found) throw new Error(`Session ${props.sessionID} not found`) + + // const info = createMemo(() => data.session[match.index]) + const messages = createMemo(() => (props.sessionID ? (data.message[props.sessionID] ?? []) : [])) + const userMessages = createMemo(() => + messages() + .filter((m) => m.role === "user") + .sort((a, b) => b.id.localeCompare(a.id)), + ) + const lastUserMessage = createMemo(() => { + return userMessages()?.at(0) + }) + const activeMessage = createMemo(() => { + if (!store.messageId) return lastUserMessage() + return userMessages()?.find((m) => m.id === store.messageId) + }) + const status = createMemo( + () => + data.session_status[props.sessionID] ?? { + type: "idle", + }, + ) + const working = createMemo(() => status()?.type !== "idle") + + return ( +
+ 1}> +
    + + {(message) => { + const messageWorking = createMemo(() => message.id === lastUserMessage()?.id && working()) + const handleClick = () => setStore("messageId", message.id) + + return ( +
  • + + +
  • + ) + }} +
    +
+
+
+ + {(message) => { + const isActive = createMemo(() => activeMessage()?.id === message.id) + const titleSeen = createMemo(() => true) + const contentSeen = createMemo(() => true) + { + /* const titleSeen = createSeen(`message-title-${message.id}`) */ + } + { + /* const contentSeen = createSeen(`message-content-${message.id}`) */ + } + const [titled, setTitled] = createSignal(titleSeen()) + const assistantMessages = createMemo(() => { + return messages()?.filter((m) => m.role === "assistant" && m.parentID == message.id) as AssistantMessage[] + }) + const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error) + const [detailsExpanded, setDetailsExpanded] = createSignal(false) + const parts = createMemo(() => data.part[message.id]) + const hasToolPart = createMemo(() => + assistantMessages() + ?.flatMap((m) => data.part[m.id]) + .some((p) => p?.type === "tool"), + ) + const messageWorking = createMemo(() => message.id === lastUserMessage()?.id && working()) + const initialCompleted = !(message.id === lastUserMessage()?.id && working()) + const [completed, setCompleted] = createSignal(initialCompleted) + + // allowing time for the animations to finish + createEffect(() => { + if (titleSeen()) return + const title = message.summary?.title + if (title) setTimeout(() => setTitled(true), 10_000) + }) + createEffect(() => { + const completed = !messageWorking() + setTimeout(() => setCompleted(completed), 1200) + }) + + return ( + +
+ {/* Title */} +
+
+ + } + > +

{message.summary?.title}

+
+
+
+
+ +
+ {/* Summary */} + +
+
+

+ + Summary + Response + +

+ + {(summary) => ( + + )} + +
+ + + {(diff) => ( + + + +
+
+ +
+ + + {getDirectory(diff.file)}‎ + + + {getFilename(diff.file)} +
+
+
+ + +
+
+
+
+ + + +
+ )} +
+
+
+
+ + + {error()?.data?.message as string} + + + {/* Response */} +
+ + + + + + + +
+
+ + Hide details + Show details + +
+ +
+
+ +
+ + {(assistantMessage) => { + const parts = createMemo(() => data.part[assistantMessage.id]) + return + }} + + + + {error()?.data?.message as string} + + +
+
+
+
+
+
+
+
+ ) + }} +
+ {props.children} +
+
+ ) +} diff --git a/packages/ui/src/components/spinner.css b/packages/ui/src/components/spinner.css new file mode 100644 index 000000000..6b432d045 --- /dev/null +++ b/packages/ui/src/components/spinner.css @@ -0,0 +1,6 @@ +[data-component="spinner"] { + color: var(--text-base); + flex-shrink: 0; + width: 18px; + aspect-ratio: 1; +} diff --git a/packages/ui/src/components/spinner.tsx b/packages/ui/src/components/spinner.tsx new file mode 100644 index 000000000..5e787d86b --- /dev/null +++ b/packages/ui/src/components/spinner.tsx @@ -0,0 +1,39 @@ +import { ComponentProps, For } from "solid-js" + +export function Spinner(props: { class?: string; classList?: ComponentProps<"div">["classList"] }) { + const squares = Array.from({ length: 16 }, (_, i) => ({ + id: i, + x: (i % 4) * 4, + y: Math.floor(i / 4) * 4, + delay: Math.random() * 3, + duration: 2 + Math.random() * 2, + })) + + return ( + + + {(square) => ( + + )} + + + ) +} diff --git a/packages/ui/src/components/sticky-accordion-header.css b/packages/ui/src/components/sticky-accordion-header.css new file mode 100644 index 000000000..0fbc354fb --- /dev/null +++ b/packages/ui/src/components/sticky-accordion-header.css @@ -0,0 +1,16 @@ +[data-component="sticky-accordion-header"] { + position: sticky; + top: 0px; + + &[data-expanded] { + z-index: 10; + + &::before { + content: ""; + z-index: -10; + position: absolute; + inset: 0; + background-color: var(--background-stronger); + } + } +} diff --git a/packages/ui/src/components/sticky-accordion-header.tsx b/packages/ui/src/components/sticky-accordion-header.tsx new file mode 100644 index 000000000..89d733449 --- /dev/null +++ b/packages/ui/src/components/sticky-accordion-header.tsx @@ -0,0 +1,18 @@ +import { Accordion } from "./accordion" +import { ParentProps } from "solid-js" + +export function StickyAccordionHeader( + props: ParentProps<{ class?: string; classList?: Record }>, +) { + return ( + + {props.children} + + ) +} diff --git a/packages/ui/src/components/tabs.css b/packages/ui/src/components/tabs.css index 91efbe50d..d03e57320 100644 --- a/packages/ui/src/components/tabs.css +++ b/packages/ui/src/components/tabs.css @@ -6,7 +6,7 @@ background-color: var(--background-stronger); overflow: clip; - [data-slot="tabs-list"] { + [data-slot="tabs-tabs-list"] { height: 48px; width: 100%; position: relative; @@ -36,7 +36,7 @@ } } - [data-slot="tabs-trigger-wrapper"] { + [data-slot="tabs-tabs-trigger-wrapper"] { position: relative; height: 100%; display: flex; @@ -58,14 +58,14 @@ border-right: 1px solid var(--border-weak-base); background-color: var(--background-base); - [data-slot="tabs-trigger"] { + [data-slot="tabs-tabs-trigger"] { display: flex; align-items: center; justify-content: center; padding: 14px 24px; } - [data-slot="tabs-trigger-close-button"] { + [data-slot="tabs-tabs-trigger-close-button"] { display: flex; align-items: center; justify-content: center; @@ -84,12 +84,12 @@ box-shadow: 0 0 0 2px var(--border-focus); } &:has([data-hidden]) { - [data-slot="tabs-trigger-close-button"] { + [data-slot="tabs-tabs-trigger-close-button"] { opacity: 0; } &:hover { - [data-slot="tabs-trigger-close-button"] { + [data-slot="tabs-tabs-trigger-close-button"] { opacity: 1; } } @@ -98,23 +98,23 @@ color: var(--text-strong); background-color: transparent; border-bottom-color: transparent; - [data-slot="tabs-trigger-close-button"] { + [data-slot="tabs-tabs-trigger-close-button"] { opacity: 1; } } &:hover:not(:disabled):not([data-selected]) { color: var(--text-strong); } - &:has([data-slot="tabs-trigger-close-button"]) { + &:has([data-slot="tabs-tabs-trigger-close-button"]) { padding-right: 12px; - [data-slot="tabs-trigger"] { + [data-slot="tabs-tabs-trigger"] { padding-right: 0; } } } - [data-slot="tabs-content"] { + [data-slot="tabs-tabs-content"] { overflow-y: auto; flex: 1; diff --git a/packages/ui/src/components/tabs.tsx b/packages/ui/src/components/tabs.tsx index e2f72f158..e3363bdd5 100644 --- a/packages/ui/src/components/tabs.tsx +++ b/packages/ui/src/components/tabs.tsx @@ -29,7 +29,7 @@ function TabsList(props: TabsListProps) { return ( ) { const [split, rest] = splitProps(props, ["class", "classList", "children", "closeButton", "hideCloseButton"]) return (
- + {split.children} {(closeButton) => ( -
+
{closeButton()}
)} @@ -67,7 +67,7 @@ function TabsContent(props: ParentProps) { return ( { @@ -14,7 +14,7 @@ export function Tooltip(props: TooltipProps) { const c = children(() => local.children) - createEffect(() => { + onMount(() => { const childElements = c() if (childElements instanceof HTMLElement) { childElements.addEventListener("focus", () => setOpen(true)) @@ -40,7 +40,7 @@ export function Tooltip(props: TooltipProps) { {others.value} - {/* */} + {/* */} -- cgit v1.2.3