diff options
| author | Adam <[email protected]> | 2026-02-08 05:02:19 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-08 05:02:19 -0600 |
| commit | d1ebe0767c264d395c8bc504c0957ccc3af90103 (patch) | |
| tree | 4e10adf10def7a5ec731d1b33a6f21dbe67769f6 /packages/app/src/pages | |
| parent | 19b1222cd85060f7b0999b99586e93f84821b94b (diff) | |
| download | opencode-d1ebe0767c264d395c8bc504c0957ccc3af90103.tar.gz opencode-d1ebe0767c264d395c8bc504c0957ccc3af90103.zip | |
chore: refactoring and tests (#12629)
Diffstat (limited to 'packages/app/src/pages')
| -rw-r--r-- | packages/app/src/pages/directory-layout.tsx | 5 | ||||
| -rw-r--r-- | packages/app/src/pages/layout/deep-links.ts | 10 | ||||
| -rw-r--r-- | packages/app/src/pages/layout/helpers.test.ts | 29 | ||||
| -rw-r--r-- | packages/app/src/pages/layout/helpers.ts | 7 | ||||
| -rw-r--r-- | packages/app/src/pages/session.tsx | 10 | ||||
| -rw-r--r-- | packages/app/src/pages/session/helpers.test.ts | 12 | ||||
| -rw-r--r-- | packages/app/src/pages/session/helpers.ts | 7 |
7 files changed, 70 insertions, 10 deletions
diff --git a/packages/app/src/pages/directory-layout.tsx b/packages/app/src/pages/directory-layout.tsx index 2f4db8564..b2a17b96b 100644 --- a/packages/app/src/pages/directory-layout.tsx +++ b/packages/app/src/pages/directory-layout.tsx @@ -15,6 +15,7 @@ export default function Layout(props: ParentProps) { const params = useParams() const navigate = useNavigate() const language = useLanguage() + let invalid = "" const directory = createMemo(() => { return decode64(params.dir) ?? "" }) @@ -22,12 +23,14 @@ export default function Layout(props: ParentProps) { createEffect(() => { if (!params.dir) return if (directory()) return + if (invalid === params.dir) return + invalid = params.dir showToast({ variant: "error", title: language.t("common.requestFailed"), description: language.t("directory.error.invalidUrl"), }) - navigate("/") + navigate("/", { replace: true }) }) return ( <Show when={directory()}> diff --git a/packages/app/src/pages/layout/deep-links.ts b/packages/app/src/pages/layout/deep-links.ts index 772e6ece6..7bdb002a3 100644 --- a/packages/app/src/pages/layout/deep-links.ts +++ b/packages/app/src/pages/layout/deep-links.ts @@ -2,7 +2,15 @@ export const deepLinkEvent = "opencode:deep-link" export const parseDeepLink = (input: string) => { if (!input.startsWith("opencode://")) return - const url = new URL(input) + if (typeof URL.canParse === "function" && !URL.canParse(input)) return + const url = (() => { + try { + return new URL(input) + } catch { + return undefined + } + })() + if (!url) return if (url.hostname !== "open-project") return const directory = url.searchParams.get("directory") if (!directory) return diff --git a/packages/app/src/pages/layout/helpers.test.ts b/packages/app/src/pages/layout/helpers.test.ts index 8a8ea78c7..83d8f4748 100644 --- a/packages/app/src/pages/layout/helpers.test.ts +++ b/packages/app/src/pages/layout/helpers.test.ts @@ -12,6 +12,27 @@ describe("layout deep links", () => { expect(parseDeepLink("https://example.com")).toBeUndefined() }) + test("ignores malformed deep links safely", () => { + expect(() => parseDeepLink("opencode://open-project/%E0%A4%A%")).not.toThrow() + expect(parseDeepLink("opencode://open-project/%E0%A4%A%")).toBeUndefined() + }) + + test("parses links when URL.canParse is unavailable", () => { + const original = Object.getOwnPropertyDescriptor(URL, "canParse") + Object.defineProperty(URL, "canParse", { configurable: true, value: undefined }) + try { + expect(parseDeepLink("opencode://open-project?directory=/tmp/demo")).toBe("/tmp/demo") + } finally { + if (original) Object.defineProperty(URL, "canParse", original) + if (!original) Reflect.deleteProperty(URL, "canParse") + } + }) + + test("ignores open-project deep links without directory", () => { + expect(parseDeepLink("opencode://open-project")).toBeUndefined() + expect(parseDeepLink("opencode://open-project?directory=")).toBeUndefined() + }) + test("collects only valid open-project directories", () => { const result = collectOpenProjectDeepLinks([ "opencode://open-project?directory=/a", @@ -39,6 +60,14 @@ describe("layout workspace helpers", () => { expect(workspaceKey("C:\\tmp\\demo\\\\")).toBe("C:\\tmp\\demo") }) + test("preserves posix and drive roots in workspace key", () => { + expect(workspaceKey("/")).toBe("/") + expect(workspaceKey("///")).toBe("/") + expect(workspaceKey("C:\\")).toBe("C:\\") + expect(workspaceKey("C:\\\\\\")).toBe("C:\\") + expect(workspaceKey("C:///")).toBe("C:/") + }) + test("keeps local first while preserving known order", () => { const result = syncWorkspaceOrder("/root", ["/root", "/b", "/c"], ["/root", "/c", "/a", "/b"]) expect(result).toEqual(["/root", "/c", "/b"]) diff --git a/packages/app/src/pages/layout/helpers.ts b/packages/app/src/pages/layout/helpers.ts index 4d144f34e..6ecccb95c 100644 --- a/packages/app/src/pages/layout/helpers.ts +++ b/packages/app/src/pages/layout/helpers.ts @@ -1,7 +1,12 @@ import { getFilename } from "@opencode-ai/util/path" import { type Session } from "@opencode-ai/sdk/v2/client" -export const workspaceKey = (directory: string) => directory.replace(/[\\/]+$/, "") +export const workspaceKey = (directory: string) => { + const drive = directory.match(/^([A-Za-z]:)[\\/]+$/) + if (drive) return `${drive[1]}${directory.includes("\\") ? "\\" : "/"}` + if (/^[\\/]+$/.test(directory)) return directory.includes("\\") ? "\\" : "/" + return directory.replace(/[\\/]+$/, "") +} export function sortSessions(now: number) { const oneMinuteAgo = now - 60 * 1000 diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 98f8bdbcd..7678ea6a8 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -40,7 +40,7 @@ import { showToast } from "@opencode-ai/ui/toast" import { SessionHeader, SessionContextTab, SortableTab, FileVisual, NewSessionView } from "@/components/session" import { navMark, navParams } from "@/utils/perf" import { same } from "@/utils/same" -import { createOpenReviewFile, focusTerminalById } from "@/pages/session/helpers" +import { createOpenReviewFile, focusTerminalById, getTabReorderIndex } from "@/pages/session/helpers" import { createScrollSpy } from "@/pages/session/scroll-spy" import { createFileTabListSync } from "@/pages/session/file-tab-scroll" import { FileTabContent } from "@/pages/session/file-tabs" @@ -844,11 +844,9 @@ export default function Page() { const { draggable, droppable } = event if (draggable && droppable) { const currentTabs = tabs().all() - const fromIndex = currentTabs?.indexOf(draggable.id.toString()) - const toIndex = currentTabs?.indexOf(droppable.id.toString()) - if (fromIndex !== toIndex && toIndex !== undefined) { - tabs().move(draggable.id.toString(), toIndex) - } + const toIndex = getTabReorderIndex(currentTabs, draggable.id.toString(), droppable.id.toString()) + if (toIndex === undefined) return + tabs().move(draggable.id.toString(), toIndex) } } diff --git a/packages/app/src/pages/session/helpers.test.ts b/packages/app/src/pages/session/helpers.test.ts index 0afc7eb6a..d877d5b2e 100644 --- a/packages/app/src/pages/session/helpers.test.ts +++ b/packages/app/src/pages/session/helpers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import { combineCommandSections, createOpenReviewFile, focusTerminalById } from "./helpers" +import { combineCommandSections, createOpenReviewFile, focusTerminalById, getTabReorderIndex } from "./helpers" describe("createOpenReviewFile", () => { test("opens and loads selected review file", () => { @@ -59,3 +59,13 @@ describe("combineCommandSections", () => { expect(result.map((item) => item.id)).toEqual(["a", "b", "c"]) }) }) + +describe("getTabReorderIndex", () => { + test("returns target index for valid drag reorder", () => { + expect(getTabReorderIndex(["a", "b", "c"], "a", "c")).toBe(2) + }) + + test("returns undefined for unknown droppable id", () => { + expect(getTabReorderIndex(["a", "b", "c"], "a", "missing")).toBeUndefined() + }) +}) diff --git a/packages/app/src/pages/session/helpers.ts b/packages/app/src/pages/session/helpers.ts index d9ce90793..dcf2c8784 100644 --- a/packages/app/src/pages/session/helpers.ts +++ b/packages/app/src/pages/session/helpers.ts @@ -36,3 +36,10 @@ export const createOpenReviewFile = (input: { export const combineCommandSections = (sections: readonly (readonly CommandOption[])[]) => { return sections.flatMap((section) => section) } + +export const getTabReorderIndex = (tabs: readonly string[], from: string, to: string) => { + const fromIndex = tabs.indexOf(from) + const toIndex = tabs.indexOf(to) + if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) return undefined + return toIndex +} |
