Skip to content

Commit 88dd714

Browse files
committed
Merge remote-tracking branch 'origin/main'
2 parents 5329dcc + a77c977 commit 88dd714

24 files changed

Lines changed: 657 additions & 84 deletions

apps/server/src/open.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ it.layer(NodeServices.layer)("resolveEditorLaunch", (it) => {
4242
args: ["/tmp/workspace"],
4343
});
4444

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+
4555
const vscodeLaunch = yield* resolveEditorLaunch(
4656
{ cwd: "/tmp/workspace", editor: "vscode" },
4757
"darwin",
@@ -122,6 +132,16 @@ it.layer(NodeServices.layer)("resolveEditorLaunch", (it) => {
122132
args: ["--goto", "/tmp/workspace/src/open.ts:71:5"],
123133
});
124134

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+
125145
const vscodeLineAndColumn = yield* resolveEditorLaunch(
126146
{ cwd: "/tmp/workspace/src/open.ts:71:5", editor: "vscode" },
127147
"darwin",
@@ -354,14 +374,15 @@ it.layer(NodeServices.layer)("resolveAvailableEditors", (it) => {
354374
const dir = yield* fs.makeTempDirectoryScoped({ prefix: "marcode-editors-" });
355375

356376
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");
357378
yield* fs.writeFileString(path.join(dir, "code-insiders.CMD"), "@echo off\r\n");
358379
yield* fs.writeFileString(path.join(dir, "codium.CMD"), "@echo off\r\n");
359380
yield* fs.writeFileString(path.join(dir, "explorer.CMD"), "MZ");
360381
const editors = resolveAvailableEditors("win32", {
361382
PATH: dir,
362383
PATHEXT: ".COM;.EXE;.BAT;.CMD",
363384
});
364-
assert.deepEqual(editors, ["trae", "vscode-insiders", "vscodium", "file-manager"]);
385+
assert.deepEqual(editors, ["trae", "kiro", "vscode-insiders", "vscodium", "file-manager"]);
365386
}),
366387
);
367388

apps/server/src/open.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ function resolveCommandEditorArgs(
7575
}
7676
}
7777

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+
7886
function resolveAvailableCommand(
7987
commands: ReadonlyArray<string>,
8088
options: CommandAvailabilityOptions = {},
@@ -273,7 +281,7 @@ export const resolveEditorLaunch = Effect.fn("resolveEditorLaunch")(function* (
273281
resolveAvailableCommand(editorDef.commands, { platform, env }) ?? editorDef.commands[0];
274282
return {
275283
command,
276-
args: resolveCommandEditorArgs(editorDef, input.cwd),
284+
args: resolveEditorArgs(editorDef, input.cwd),
277285
};
278286
}
279287

apps/web/src/components/ChatView.browser.tsx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,6 +2204,55 @@ describe("ChatView timeline estimator parity (full app)", () => {
22042204
}
22052205
});
22062206

2207+
it("shows Kiro in the open picker menu and opens the project cwd with it", async () => {
2208+
setDraftThreadWithoutWorktree();
2209+
2210+
const mounted = await mountChatView({
2211+
viewport: DEFAULT_VIEWPORT,
2212+
snapshot: createDraftOnlySnapshot(),
2213+
configureFixture: (nextFixture) => {
2214+
nextFixture.serverConfig = {
2215+
...nextFixture.serverConfig,
2216+
availableEditors: ["kiro"],
2217+
};
2218+
},
2219+
});
2220+
2221+
try {
2222+
await waitForServerConfigToApply();
2223+
const menuButton = await waitForElement(
2224+
() => document.querySelector('button[aria-label="Copy options"]'),
2225+
"Unable to find Open picker button.",
2226+
);
2227+
(menuButton as HTMLButtonElement).click();
2228+
2229+
const kiroItem = await waitForElement(
2230+
() =>
2231+
Array.from(document.querySelectorAll('[data-slot="menu-item"]')).find((item) =>
2232+
item.textContent?.includes("Kiro"),
2233+
) ?? null,
2234+
"Unable to find Kiro menu item.",
2235+
);
2236+
(kiroItem as HTMLElement).click();
2237+
2238+
await vi.waitFor(
2239+
() => {
2240+
const openRequest = wsRequests.find(
2241+
(request) => request._tag === WS_METHODS.shellOpenInEditor,
2242+
);
2243+
expect(openRequest).toMatchObject({
2244+
_tag: WS_METHODS.shellOpenInEditor,
2245+
cwd: "/repo/project",
2246+
editor: "kiro",
2247+
});
2248+
},
2249+
{ timeout: 8_000, interval: 16 },
2250+
);
2251+
} finally {
2252+
await mounted.cleanup();
2253+
}
2254+
});
2255+
22072256
it("filters the open picker menu and opens VSCodium from the menu", async () => {
22082257
setDraftThreadWithoutWorktree();
22092258

apps/web/src/components/ChatView.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
PROVIDER_SEND_TURN_MAX_ATTACHMENTS,
1515
PROVIDER_SEND_TURN_MAX_IMAGE_BYTES,
1616
type ServerProvider,
17+
type ResolvedKeybindingsConfig,
1718
type ScopedThreadRef,
1819
type ThreadId,
1920
type TurnId,
@@ -553,6 +554,7 @@ interface PersistentThreadTerminalDrawerProps {
553554
splitShortcutLabel: string | undefined;
554555
newShortcutLabel: string | undefined;
555556
closeShortcutLabel: string | undefined;
557+
keybindings: ResolvedKeybindingsConfig;
556558
onAddTerminalContext: (selection: TerminalContextSelection) => void;
557559
}
558560

@@ -565,6 +567,7 @@ function PersistentThreadTerminalDrawer({
565567
splitShortcutLabel,
566568
newShortcutLabel,
567569
closeShortcutLabel,
570+
keybindings,
568571
onAddTerminalContext,
569572
}: PersistentThreadTerminalDrawerProps) {
570573
const serverThread = useThreadById(threadId);
@@ -719,6 +722,7 @@ function PersistentThreadTerminalDrawer({
719722
splitShortcutLabel={visible ? splitShortcutLabel : undefined}
720723
newShortcutLabel={visible ? newShortcutLabel : undefined}
721724
closeShortcutLabel={visible ? closeShortcutLabel : undefined}
725+
keybindings={keybindings}
722726
onActiveTerminalChange={activateTerminal}
723727
onCloseTerminal={closeTerminal}
724728
onHeightChange={setTerminalHeight}
@@ -3001,8 +3005,8 @@ export default function ChatView({
30013005
event.stopPropagation();
30023006
void runProjectScript(script);
30033007
};
3004-
window.addEventListener("keydown", handler);
3005-
return () => window.removeEventListener("keydown", handler);
3008+
window.addEventListener("keydown", handler, true);
3009+
return () => window.removeEventListener("keydown", handler, true);
30063010
}, [
30073011
activeProject,
30083012
terminalState.terminalOpen,
@@ -5231,6 +5235,7 @@ export default function ChatView({
52315235
splitShortcutLabel={splitTerminalShortcutLabel ?? undefined}
52325236
newShortcutLabel={newTerminalShortcutLabel ?? undefined}
52335237
closeShortcutLabel={closeTerminalShortcutLabel ?? undefined}
5238+
keybindings={keybindings}
52345239
onAddTerminalContext={addTerminalContextToDraft}
52355240
/>
52365241
))}

apps/web/src/components/DiffPanel.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -493,12 +493,6 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
493493
</div>
494494
) : (
495495
<>
496-
{canScrollTurnStripLeft && (
497-
<div className="pointer-events-none absolute inset-y-0 left-8 z-10 w-7 bg-linear-to-r from-card to-transparent" />
498-
)}
499-
{canScrollTurnStripRight && (
500-
<div className="pointer-events-none absolute inset-y-0 right-8 z-10 w-7 bg-linear-to-l from-card to-transparent" />
501-
)}
502496
<button
503497
type="button"
504498
className={cn(
@@ -530,6 +524,13 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
530524
<div
531525
ref={turnStripRef}
532526
className="turn-chip-strip flex gap-1 overflow-x-auto px-8 py-0.5"
527+
style={
528+
canScrollTurnStripLeft || canScrollTurnStripRight
529+
? {
530+
maskImage: `linear-gradient(to right, ${canScrollTurnStripLeft ? "transparent 24px, black 72px" : "black"}, ${canScrollTurnStripRight ? "black calc(100% - 72px), transparent calc(100% - 24px)" : "black"})`,
531+
}
532+
: undefined
533+
}
533534
onWheel={onTurnStripWheel}
534535
>
535536
<button

apps/web/src/components/Icons.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,24 @@ export const TraeIcon: Icon = (props) => (
6363
</svg>
6464
);
6565

66+
export const KiroIcon: Icon = (props) => (
67+
<svg {...props} viewBox="0 0 1200 1200" fill="none">
68+
<rect width="1200" height="1200" rx="260" fill="#9046FF" />
69+
<path
70+
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"
71+
fill="#fff"
72+
/>
73+
<path
74+
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"
75+
fill="#000"
76+
/>
77+
<path
78+
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"
79+
fill="#000"
80+
/>
81+
</svg>
82+
);
83+
6684
export const VisualStudioCode: Icon = (props) => {
6785
const id = useId();
6886
const maskId = `${id}-vscode-a`;

apps/web/src/components/Sidebar.logic.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,42 @@ describe("orderItemsByPreferredIds", () => {
273273
ProjectId.make("project-1"),
274274
]);
275275
});
276+
277+
it("honors projectOrder physical keys via getProjectOrderKey", async () => {
278+
// Regression guard for #1904 / the regression introduced by #2055:
279+
// `projectOrder` is populated with physical keys (envId + cwd-derived)
280+
// by the store and by drag-end handlers. Readers must identify projects
281+
// with the same key format, or manual sort silently snaps back.
282+
const { getProjectOrderKey } = await import("../logicalProject");
283+
const projects = [
284+
{
285+
environmentId: EnvironmentId.make("environment-local"),
286+
id: ProjectId.make("id-alpha"),
287+
cwd: "/work/alpha",
288+
},
289+
{
290+
environmentId: EnvironmentId.make("environment-local"),
291+
id: ProjectId.make("id-beta"),
292+
cwd: "/work/beta",
293+
},
294+
{
295+
environmentId: EnvironmentId.make("environment-local"),
296+
id: ProjectId.make("id-gamma"),
297+
cwd: "/work/gamma",
298+
},
299+
];
300+
const ordered = orderItemsByPreferredIds({
301+
items: projects,
302+
preferredIds: [getProjectOrderKey(projects[2]!), getProjectOrderKey(projects[0]!)],
303+
getId: getProjectOrderKey,
304+
});
305+
306+
expect(ordered.map((project) => project.cwd)).toEqual([
307+
"/work/gamma",
308+
"/work/alpha",
309+
"/work/beta",
310+
]);
311+
});
276312
});
277313

278314
describe("resolveAdjacentThreadId", () => {

apps/web/src/components/Sidebar.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,11 @@ import { CommandDialogTrigger } from "./ui/command";
174174
import { readEnvironmentApi } from "../environmentApi";
175175
import { useSettings, useUpdateSettings } from "~/hooks/useSettings";
176176
import { useServerKeybindings } from "../rpc/serverState";
177-
import { derivePhysicalProjectKey, deriveProjectGroupingOverrideKey } from "../logicalProject";
177+
import {
178+
derivePhysicalProjectKey,
179+
deriveProjectGroupingOverrideKey,
180+
getProjectOrderKey,
181+
} from "../logicalProject";
178182
import {
179183
useSavedEnvironmentRegistryStore,
180184
useSavedEnvironmentRuntimeStore,
@@ -2778,7 +2782,7 @@ export default function Sidebar() {
27782782
return orderItemsByPreferredIds({
27792783
items: projects,
27802784
preferredIds: projectOrder,
2781-
getId: (project) => scopedProjectKey(scopeProjectRef(project.environmentId, project.id)),
2785+
getId: getProjectOrderKey,
27822786
});
27832787
}, [projectOrder, projects]);
27842788

apps/web/src/components/ThreadTerminalDrawer.browser.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ async function mountTerminalViewport(props: {
177177
autoFocus={false}
178178
resizeEpoch={0}
179179
drawerHeight={320}
180+
keybindings={[]}
180181
/>,
181182
{ container: host },
182183
);
@@ -196,6 +197,7 @@ async function mountTerminalViewport(props: {
196197
autoFocus={false}
197198
resizeEpoch={0}
198199
drawerHeight={320}
200+
keybindings={[]}
199201
/>,
200202
);
201203
},

0 commit comments

Comments
 (0)