| author | |
| committer | |
| log | 5f7becf38688ddbd305f352b6cd9bd57d82c164c |
| tree | 12c54610bc26c94c722ef77eab40a4768f3ff458 |
| parent | 9b29be916b2c5e3736fa685d30c417f4fddcce9c |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Julius Marminge <jmarminge@gmail.com>6 files changed, 106 insertions(+), 2 deletions(-)
apps/server/src/open.test.ts+22-1| ... | ... | @@ -42,6 +42,16 @@ it.layer(NodeServices.layer)("resolveEditorLaunch", (it) => { |
| 42 | 42 | args: ["/tmp/workspace"], |
| 43 | 43 | }); |
| 44 | 44 | |
| 45 | const kiroLaunch = yield* resolveEditorLaunch( | |
| 46 | { cwd: "/tmp/workspace", editor: "kiro" }, | |
| 47 | "darwin", | |
| 48 | { PATH: "" }, | |
| 49 | ); | |
| 50 | assert.deepEqual(kiroLaunch, { | |
| 51 | command: "kiro", | |
| 52 | args: ["ide", "/tmp/workspace"], | |
| 53 | }); | |
| 54 | ||
| 45 | 55 | const vscodeLaunch = yield* resolveEditorLaunch( |
| 46 | 56 | { cwd: "/tmp/workspace", editor: "vscode" }, |
| 47 | 57 | "darwin", |
| ... | ... | @@ -122,6 +132,16 @@ it.layer(NodeServices.layer)("resolveEditorLaunch", (it) => { |
| 122 | 132 | args: ["--goto", "/tmp/workspace/src/open.ts:71:5"], |
| 123 | 133 | }); |
| 124 | 134 | |
| 135 | const kiroLineAndColumn = yield* resolveEditorLaunch( | |
| 136 | { cwd: "/tmp/workspace/src/open.ts:71:5", editor: "kiro" }, | |
| 137 | "darwin", | |
| 138 | { PATH: "" }, | |
| 139 | ); | |
| 140 | assert.deepEqual(kiroLineAndColumn, { | |
| 141 | command: "kiro", | |
| 142 | args: ["ide", "--goto", "/tmp/workspace/src/open.ts:71:5"], | |
| 143 | }); | |
| 144 | ||
| 125 | 145 | const vscodeLineAndColumn = yield* resolveEditorLaunch( |
| 126 | 146 | { cwd: "/tmp/workspace/src/open.ts:71:5", editor: "vscode" }, |
| 127 | 147 | "darwin", |
| ... | ... | @@ -354,6 +374,7 @@ it.layer(NodeServices.layer)("resolveAvailableEditors", (it) => { |
| 354 | 374 | const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-editors-" }); |
| 355 | 375 | |
| 356 | 376 | yield* fs.writeFileString(path.join(dir, "trae.CMD"), "@echo off\r\n"); |
| 377 | yield* fs.writeFileString(path.join(dir, "kiro.CMD"), "@echo off\r\n"); | |
| 357 | 378 | yield* fs.writeFileString(path.join(dir, "code-insiders.CMD"), "@echo off\r\n"); |
| 358 | 379 | yield* fs.writeFileString(path.join(dir, "codium.CMD"), "@echo off\r\n"); |
| 359 | 380 | yield* fs.writeFileString(path.join(dir, "explorer.CMD"), "MZ"); |
| ... | ... | @@ -361,7 +382,7 @@ it.layer(NodeServices.layer)("resolveAvailableEditors", (it) => { |
| 361 | 382 | PATH: dir, |
| 362 | 383 | PATHEXT: ".COM;.EXE;.BAT;.CMD", |
| 363 | 384 | }); |
| 364 | assert.deepEqual(editors, ["trae", "vscode-insiders", "vscodium", "file-manager"]); | |
| 385 | assert.deepEqual(editors, ["trae", "kiro", "vscode-insiders", "vscodium", "file-manager"]); | |
| 365 | 386 | }), |
| 366 | 387 | ); |
| 367 | 388 |
apps/server/src/open.ts+9-1| ... | ... | @@ -75,6 +75,14 @@ function resolveCommandEditorArgs( |
| 75 | 75 | } |
| 76 | 76 | } |
| 77 | 77 | |
| 78 | function resolveEditorArgs( | |
| 79 | editor: (typeof EDITORS)[number], | |
| 80 | target: string, | |
| 81 | ): ReadonlyArray<string> { | |
| 82 | const baseArgs = "baseArgs" in editor ? editor.baseArgs : []; | |
| 83 | return [...baseArgs, ...resolveCommandEditorArgs(editor, target)]; | |
| 84 | } | |
| 85 | ||
| 78 | 86 | function resolveAvailableCommand( |
| 79 | 87 | commands: ReadonlyArray<string>, |
| 80 | 88 | options: CommandAvailabilityOptions = {}, |
| ... | ... | @@ -273,7 +281,7 @@ export const resolveEditorLaunch = Effect.fn("resolveEditorLaunch")(function* ( |
| 273 | 281 | resolveAvailableCommand(editorDef.commands, { platform, env }) ?? editorDef.commands[0]; |
| 274 | 282 | return { |
| 275 | 283 | command, |
| 276 | args: resolveCommandEditorArgs(editorDef, input.cwd), | |
| 284 | args: resolveEditorArgs(editorDef, input.cwd), | |
| 277 | 285 | }; |
| 278 | 286 | } |
| 279 | 287 |
apps/web/src/components/ChatView.browser.tsx+49| ... | ... | @@ -1803,6 +1803,55 @@ describe("ChatView timeline estimator parity (full app)", () => { |
| 1803 | 1803 | } |
| 1804 | 1804 | }); |
| 1805 | 1805 | |
| 1806 | it("shows Kiro in the open picker menu and opens the project cwd with it", async () => { | |
| 1807 | setDraftThreadWithoutWorktree(); | |
| 1808 | ||
| 1809 | const mounted = await mountChatView({ | |
| 1810 | viewport: DEFAULT_VIEWPORT, | |
| 1811 | snapshot: createDraftOnlySnapshot(), | |
| 1812 | configureFixture: (nextFixture) => { | |
| 1813 | nextFixture.serverConfig = { | |
| 1814 | ...nextFixture.serverConfig, | |
| 1815 | availableEditors: ["kiro"], | |
| 1816 | }; | |
| 1817 | }, | |
| 1818 | }); | |
| 1819 | ||
| 1820 | try { | |
| 1821 | await waitForServerConfigToApply(); | |
| 1822 | const menuButton = await waitForElement( | |
| 1823 | () => document.querySelector('button[aria-label="Copy options"]'), | |
| 1824 | "Unable to find Open picker button.", | |
| 1825 | ); | |
| 1826 | (menuButton as HTMLButtonElement).click(); | |
| 1827 | ||
| 1828 | const kiroItem = await waitForElement( | |
| 1829 | () => | |
| 1830 | Array.from(document.querySelectorAll('[data-slot="menu-item"]')).find((item) => | |
| 1831 | item.textContent?.includes("Kiro"), | |
| 1832 | ) ?? null, | |
| 1833 | "Unable to find Kiro menu item.", | |
| 1834 | ); | |
| 1835 | (kiroItem as HTMLElement).click(); | |
| 1836 | ||
| 1837 | await vi.waitFor( | |
| 1838 | () => { | |
| 1839 | const openRequest = wsRequests.find( | |
| 1840 | (request) => request._tag === WS_METHODS.shellOpenInEditor, | |
| 1841 | ); | |
| 1842 | expect(openRequest).toMatchObject({ | |
| 1843 | _tag: WS_METHODS.shellOpenInEditor, | |
| 1844 | cwd: "/repo/project", | |
| 1845 | editor: "kiro", | |
| 1846 | }); | |
| 1847 | }, | |
| 1848 | { timeout: 8_000, interval: 16 }, | |
| 1849 | ); | |
| 1850 | } finally { | |
| 1851 | await mounted.cleanup(); | |
| 1852 | } | |
| 1853 | }); | |
| 1854 | ||
| 1806 | 1855 | it("filters the open picker menu and opens VSCodium from the menu", async () => { |
| 1807 | 1856 | setDraftThreadWithoutWorktree(); |
| 1808 | 1857 |
apps/web/src/components/Icons.tsx+18| ... | ... | @@ -34,6 +34,24 @@ export const TraeIcon: Icon = (props) => ( |
| 34 | 34 | </svg> |
| 35 | 35 | ); |
| 36 | 36 | |
| 37 | export const KiroIcon: Icon = (props) => ( | |
| 38 | <svg {...props} viewBox="0 0 1200 1200" fill="none"> | |
| 39 | <rect width="1200" height="1200" rx="260" fill="#9046FF" /> | |
| 40 | <path | |
| 41 | d="M398.554 818.914C316.315 1001.03 491.477 1046.74 620.672 940.156C658.687 1059.66 801.052 970.473 852.234 877.795C964.787 673.567 919.318 465.357 907.64 422.374C827.637 129.443 427.623 128.946 358.8 423.865C342.651 475.544 342.402 534.18 333.458 595.051C328.986 625.86 325.507 645.488 313.83 677.785C306.873 696.424 297.68 712.819 282.773 740.645C259.915 783.881 269.604 867.113 387.87 823.883L399.051 818.914H398.554Z" | |
| 42 | fill="#fff" | |
| 43 | /> | |
| 44 | <path | |
| 45 | d="M636.123 549.353C603.328 549.353 598.359 510.097 598.359 486.742C598.359 465.623 602.086 448.977 609.293 438.293C615.504 428.852 624.697 424.131 636.123 424.131C647.555 424.131 657.492 428.852 664.447 438.541C672.398 449.474 676.623 466.12 676.623 486.742C676.623 525.998 661.471 549.353 636.375 549.353H636.123Z" | |
| 46 | fill="#000" | |
| 47 | /> | |
| 48 | <path | |
| 49 | d="M771.24 549.353C738.445 549.353 733.477 510.097 733.477 486.742C733.477 465.623 737.203 448.977 744.41 438.293C750.621 428.852 759.814 424.131 771.24 424.131C782.672 424.131 792.609 428.852 799.564 438.541C807.516 449.474 811.74 466.12 811.74 486.742C811.74 525.998 796.588 549.353 771.492 549.353H771.24Z" | |
| 50 | fill="#000" | |
| 51 | /> | |
| 52 | </svg> | |
| 53 | ); | |
| 54 | ||
| 37 | 55 | export const VisualStudioCode: Icon = (props) => { |
| 38 | 56 | const id = useId(); |
| 39 | 57 | const maskId = `${id}-vscode-a`; |
apps/web/src/components/chat/OpenInPicker.tsx+6| ... | ... | @@ -10,6 +10,7 @@ import { |
| 10 | 10 | AntigravityIcon, |
| 11 | 11 | CursorIcon, |
| 12 | 12 | Icon, |
| 13 | KiroIcon, | |
| 13 | 14 | TraeIcon, |
| 14 | 15 | IntelliJIdeaIcon, |
| 15 | 16 | VisualStudioCode, |
| ... | ... | @@ -32,6 +33,11 @@ const resolveOptions = (platform: string, availableEditors: ReadonlyArray<Editor |
| 32 | 33 | Icon: TraeIcon, |
| 33 | 34 | value: "trae", |
| 34 | 35 | }, |
| 36 | { | |
| 37 | label: "Kiro", | |
| 38 | Icon: KiroIcon, | |
| 39 | value: "kiro", | |
| 40 | }, | |
| 35 | 41 | { |
| 36 | 42 | label: "VS Code", |
| 37 | 43 | Icon: VisualStudioCode, |
packages/contracts/src/editor.ts+2| ... | ... | @@ -8,12 +8,14 @@ type EditorDefinition = { |
| 8 | 8 | readonly id: string; |
| 9 | 9 | readonly label: string; |
| 10 | 10 | readonly commands: readonly [string, ...string[]] | null; |
| 11 | readonly baseArgs?: readonly string[]; | |
| 11 | 12 | readonly launchStyle: EditorLaunchStyle; |
| 12 | 13 | }; |
| 13 | 14 | |
| 14 | 15 | export const EDITORS = [ |
| 15 | 16 | { id: "cursor", label: "Cursor", commands: ["cursor"], launchStyle: "goto" }, |
| 16 | 17 | { id: "trae", label: "Trae", commands: ["trae"], launchStyle: "goto" }, |
| 18 | { id: "kiro", label: "Kiro", commands: ["kiro"], baseArgs: ["ide"], launchStyle: "goto" }, | |
| 17 | 19 | { id: "vscode", label: "VS Code", commands: ["code"], launchStyle: "goto" }, |
| 18 | 20 | { |
| 19 | 21 | id: "vscode-insiders", |