diff --git a/packages/app/src/components/dialog-select-provider.tsx b/packages/app/src/components/dialog-select-provider.tsx index e53738399a..226f6a0bb4 100644 --- a/packages/app/src/components/dialog-select-provider.tsx +++ b/packages/app/src/components/dialog-select-provider.tsx @@ -74,7 +74,9 @@ export const DialogSelectProvider: Component = () => { {language.t("dialog.provider.tag.recommended")} - {(value) =>
{value()}
}
+ + {(value) =>
{value}
} +
{language.t("dialog.provider.tag.recommended")} diff --git a/packages/app/src/components/model-tooltip.tsx b/packages/app/src/components/model-tooltip.tsx index 53164dae85..08b0cb48f2 100644 --- a/packages/app/src/components/model-tooltip.tsx +++ b/packages/app/src/components/model-tooltip.tsx @@ -77,10 +77,10 @@ export const ModelTooltip: Component<{ model: ModelInfo; latest?: boolean; free? return (
{title()}
- + {(value) => (
- {language.t("model.tooltip.allows", { inputs: value() })} + {language.t("model.tooltip.allows", { inputs: value })}
)}
diff --git a/packages/app/src/components/prompt-input/context-items.tsx b/packages/app/src/components/prompt-input/context-items.tsx index b138fe3ef6..8a3c556b3c 100644 --- a/packages/app/src/components/prompt-input/context-items.tsx +++ b/packages/app/src/components/prompt-input/context-items.tsx @@ -52,12 +52,10 @@ export const PromptContextItems: Component = (props) => {
{label} - + {(sel) => ( - {sel().startLine === sel().endLine - ? `:${sel().startLine}` - : `:${sel().startLine}-${sel().endLine}`} + {sel.startLine === sel.endLine ? `:${sel.startLine}` : `:${sel.startLine}-${sel.endLine}`} )} @@ -74,8 +72,8 @@ export const PromptContextItems: Component = (props) => { aria-label={props.t("prompt.context.removeFile")} />
- - {(comment) =>
{comment()}
} + + {(comment) =>
{comment}
}
diff --git a/packages/app/src/components/server/server-row.tsx b/packages/app/src/components/server/server-row.tsx index 5bb290ec30..78862d2c54 100644 --- a/packages/app/src/components/server/server-row.tsx +++ b/packages/app/src/components/server/server-row.tsx @@ -78,6 +78,7 @@ export function ServerRow(props: ServerRowProps) { @@ -86,20 +87,20 @@ export function ServerRow(props: ServerRowProps) { } > - {(badge) => badge()} + {(badge) => badge} - + {(conn) => (
- {conn().http.username ? ( - {conn().http.username} + {conn.http.username ? ( + {conn.http.username} ) : ( no username )} - {conn().http.password && ••••••••} + {conn.http.password && ••••••••}
)}
diff --git a/packages/app/src/components/session-context-usage.tsx b/packages/app/src/components/session-context-usage.tsx index 08ae4d3194..7be9b51694 100644 --- a/packages/app/src/components/session-context-usage.tsx +++ b/packages/app/src/components/session-context-usage.tsx @@ -73,15 +73,15 @@ export function SessionContextUsage(props: SessionContextUsageProps) { const tooltipValue = () => (
- + {(ctx) => ( <>
- {ctx().total.toLocaleString(language.intl())} + {ctx.total.toLocaleString(language.intl())} {language.t("context.usage.tokens")}
- {ctx().usage ?? 0}% + {ctx.usage ?? 0}% {language.t("context.usage.usage")}
diff --git a/packages/app/src/components/session/session-context-tab.tsx b/packages/app/src/components/session/session-context-tab.tsx index 39eb4b4c0e..ba66f88296 100644 --- a/packages/app/src/components/session/session-context-tab.tsx +++ b/packages/app/src/components/session/session-context-tab.tsx @@ -316,12 +316,12 @@ export function SessionContextTab() {
- + {(prompt) => (
{language.t("context.systemPrompt.title")}
- +
)} diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index bb4d981250..d068692f64 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -356,9 +356,9 @@ export function SessionHeader() { return ( <> - + {(mount) => ( - + )} - + {(mount) => ( - +
diff --git a/packages/app/src/components/session/session-new-view.tsx b/packages/app/src/components/session/session-new-view.tsx index f2ecd51501..8ef5582ba5 100644 --- a/packages/app/src/components/session/session-new-view.tsx +++ b/packages/app/src/components/session/session-new-view.tsx @@ -62,14 +62,14 @@ export function NewSessionView(props: NewSessionViewProps) {
{label(current())}
- + {(project) => (
{language.t("session.new.lastModified")}  - {DateTime.fromMillis(project().time.updated ?? project().time.created) + {DateTime.fromMillis(project.time.updated ?? project.time.created) .setLocale(language.intl()) .toRelative()} diff --git a/packages/app/src/components/session/session-sortable-tab.tsx b/packages/app/src/components/session/session-sortable-tab.tsx index dfda91c160..e6d2b24c78 100644 --- a/packages/app/src/components/session/session-sortable-tab.tsx +++ b/packages/app/src/components/session/session-sortable-tab.tsx @@ -62,7 +62,9 @@ export function SortableTab(props: { tab: string; onTabClose: (tab: string) => v hideCloseButton onMiddleClick={() => props.onTabClose(props.tab)} > - {(value) => value()} + + {(value) => value} +
diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx index 42ee4092f6..c19e4d0765 100644 --- a/packages/app/src/components/settings-general.tsx +++ b/packages/app/src/components/settings-general.tsx @@ -473,7 +473,7 @@ export const SettingsGeneral: Component = () => { - {/* + {/* {(_) => { const [enabledResource, actions] = createResource(() => platform.getWslEnabled?.()) const enabled = () => (enabledResource.state === "pending" ? undefined : enabledResource.latest) @@ -503,7 +503,7 @@ export const SettingsGeneral: Component = () => { - + {(_) => { const [valueResource, actions] = createResource(() => platform.getDisplayBackend?.()) const value = () => (valueResource.state === "pending" ? undefined : valueResource.latest) diff --git a/packages/app/src/components/settings-providers.tsx b/packages/app/src/components/settings-providers.tsx index a9839758b7..a2f1f0b643 100644 --- a/packages/app/src/components/settings-providers.tsx +++ b/packages/app/src/components/settings-providers.tsx @@ -189,8 +189,8 @@ export const SettingsProviders: Component = () => { {language.t("dialog.provider.tag.recommended")} - - {(key) => {language.t(key())}} + + {(key) => {language.t(key)}} - - {(version) => ( -

{language.t("error.page.version", { version: version() })}

- )} + + {(version) =>

{language.t("error.page.version", { version })}

}
diff --git a/packages/app/src/pages/layout/sidebar-project.tsx b/packages/app/src/pages/layout/sidebar-project.tsx index 3c3652e38f..bafc17f880 100644 --- a/packages/app/src/pages/layout/sidebar-project.tsx +++ b/packages/app/src/pages/layout/sidebar-project.tsx @@ -43,10 +43,10 @@ export const ProjectDragOverlay = (props: { }): JSX.Element => { const project = createMemo(() => props.projects().find((p) => p.worktree === props.activeProject())) return ( - + {(p) => (
- +
)}
diff --git a/packages/app/src/pages/layout/sidebar-workspace.tsx b/packages/app/src/pages/layout/sidebar-workspace.tsx index 43d99cf895..626c9bd15c 100644 --- a/packages/app/src/pages/layout/sidebar-workspace.tsx +++ b/packages/app/src/pages/layout/sidebar-workspace.tsx @@ -76,8 +76,8 @@ export const WorkspaceDragOverlay = (props: { }) return ( - - {(value) =>
{value()}
} + + {(value) =>
{value}
}
) } diff --git a/packages/app/src/pages/session/message-timeline.tsx b/packages/app/src/pages/session/message-timeline.tsx index f320a2ebbf..ba478543dc 100644 --- a/packages/app/src/pages/session/message-timeline.tsx +++ b/packages/app/src/pages/session/message-timeline.tsx @@ -731,12 +731,12 @@ export function MessageTimeline(props: { class="size-3.5 shrink-0" /> {getFilename(comment().path)} - + {(selection) => ( - {selection().startLine === selection().endLine - ? `:${selection().startLine}` - : `:${selection().startLine}-${selection().endLine}`} + {selection.startLine === selection.endLine + ? `:${selection.startLine}` + : `:${selection.startLine}-${selection.endLine}`} )} diff --git a/packages/app/src/show-keyed-guard.test.ts b/packages/app/src/show-keyed-guard.test.ts new file mode 100644 index 0000000000..f46a506e6d --- /dev/null +++ b/packages/app/src/show-keyed-guard.test.ts @@ -0,0 +1,61 @@ +import { describe, test } from "bun:test" +import { join, relative, resolve } from "node:path" +import * as ts from "typescript" + +const scan = async (dir: string) => + Promise.all( + Array.from(new Bun.Glob("**/*.tsx").scanSync({ cwd: dir })).map(async (file) => { + const full = join(dir, file) + return { + file: full, + text: await Bun.file(full).text(), + } + }), + ) + +const find = (file: string, text: string, root: string) => { + const hits: string[] = [] + const source = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX) + + const walk = (node: ts.Node): void => { + if (ts.isJsxElement(node) && node.openingElement.tagName.getText(source) === "Show") { + const keyed = node.openingElement.attributes.properties.some( + (prop) => ts.isJsxAttribute(prop) && prop.name.getText(source) === "keyed", + ) + const child = node.children.find((child) => { + if (ts.isJsxText(child)) return child.getText(source).trim() !== "" + if (ts.isJsxExpression(child)) return child.expression !== undefined + return true + }) + + if ( + !keyed && + child && + ts.isJsxExpression(child) && + child.expression && + ts.isArrowFunction(child.expression) && + child.expression.parameters.length > 0 + ) { + hits.push(`${relative(root, file)}:${source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1}`) + } + } + + ts.forEachChild(node, walk) + } + + walk(source) + return hits +} + +describe("show keyed guard", () => { + test("app and desktop show callbacks are keyed", async () => { + const root = resolve(import.meta.dir, "../../..") + const hits = (await Promise.all([scan(import.meta.dir), scan(resolve(import.meta.dir, "../../desktop/src"))])) + .flat() + .flatMap((item) => find(item.file, item.text, root)) + + if (hits.length > 0) { + throw new Error(`non-keyed callbacks found:\n${hits.join("\n")}`) + } + }) +}) diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index 9afabe918b..4577a4eca1 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -482,6 +482,7 @@ function ServerGate(props: { children: (data: ServerReadyData) => JSX.Element }) return ( @@ -489,7 +490,7 @@ function ServerGate(props: { children: (data: ServerReadyData) => JSX.Element }) } > - {(data) => props.children(data())} + {(data) => props.children(data)} ) }