summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/pages
diff options
context:
space:
mode:
Diffstat (limited to 'packages/app/src/pages')
-rw-r--r--packages/app/src/pages/directory-layout.tsx22
-rw-r--r--packages/app/src/pages/layout.tsx24
-rw-r--r--packages/app/src/pages/session.tsx27
3 files changed, 55 insertions, 18 deletions
diff --git a/packages/app/src/pages/directory-layout.tsx b/packages/app/src/pages/directory-layout.tsx
index caad6c996..037b08c72 100644
--- a/packages/app/src/pages/directory-layout.tsx
+++ b/packages/app/src/pages/directory-layout.tsx
@@ -1,22 +1,36 @@
-import { createMemo, Show, type ParentProps } from "solid-js"
+import { createEffect, createMemo, Show, type ParentProps } from "solid-js"
import { useNavigate, useParams } from "@solidjs/router"
import { SDKProvider, useSDK } from "@/context/sdk"
import { SyncProvider, useSync } from "@/context/sync"
import { LocalProvider } from "@/context/local"
-import { base64Decode } from "@opencode-ai/util/encode"
import { DataProvider } from "@opencode-ai/ui/context"
import { iife } from "@opencode-ai/util/iife"
import type { QuestionAnswer } from "@opencode-ai/sdk/v2"
+import { decode64 } from "@/utils/base64"
+import { showToast } from "@opencode-ai/ui/toast"
+import { useLanguage } from "@/context/language"
export default function Layout(props: ParentProps) {
const params = useParams()
const navigate = useNavigate()
+ const language = useLanguage()
const directory = createMemo(() => {
- return base64Decode(params.dir!)
+ return decode64(params.dir) ?? ""
+ })
+
+ createEffect(() => {
+ if (!params.dir) return
+ if (directory()) return
+ showToast({
+ variant: "error",
+ title: language.t("common.requestFailed"),
+ description: "Invalid directory in URL.",
+ })
+ navigate("/")
})
return (
- <Show when={params.dir}>
+ <Show when={directory()}>
<SDKProvider directory={directory()}>
<SyncProvider>
{iife(() => {
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index 283144872..ae86dbb7e 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -19,7 +19,8 @@ import { A, useNavigate, useParams } from "@solidjs/router"
import { useLayout, getAvatarColors, LocalProject } from "@/context/layout"
import { useGlobalSync } from "@/context/global-sync"
import { Persist, persisted } from "@/utils/persist"
-import { base64Decode, base64Encode } from "@opencode-ai/util/encode"
+import { base64Encode } from "@opencode-ai/util/encode"
+import { decode64 } from "@/utils/base64"
import { Avatar } from "@opencode-ai/ui/avatar"
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
import { Button } from "@opencode-ai/ui/button"
@@ -420,7 +421,7 @@ export default function Layout(props: ParentProps) {
}
}
- const currentDir = params.dir ? base64Decode(params.dir) : undefined
+ const currentDir = decode64(params.dir)
const currentSession = params.id
if (directory === currentDir && props.sessionID === currentSession) return
if (directory === currentDir && session?.parentID === currentSession) return
@@ -449,7 +450,7 @@ export default function Layout(props: ParentProps) {
onCleanup(unsub)
createEffect(() => {
- const currentDir = params.dir ? base64Decode(params.dir) : undefined
+ const currentDir = decode64(params.dir)
const currentSession = params.id
if (!currentDir || !currentSession) return
const sessionKey = `${currentDir}:${currentSession}`
@@ -503,7 +504,7 @@ export default function Layout(props: ParentProps) {
}
const currentProject = createMemo(() => {
- const directory = params.dir ? base64Decode(params.dir) : undefined
+ const directory = decode64(params.dir)
if (!directory) return
const projects = layout.projects.list()
@@ -638,7 +639,7 @@ export default function Layout(props: ParentProps) {
const compare = sortSessions(Date.now())
if (workspaceSetting()) {
const dirs = workspaceIds(project)
- const activeDir = params.dir ? base64Decode(params.dir) : ""
+ const activeDir = decode64(params.dir) ?? ""
const result: Session[] = []
for (const dir of dirs) {
const expanded = store.workspaceExpanded[dir] ?? dir === project.worktree
@@ -1188,7 +1189,7 @@ export default function Layout(props: ParentProps) {
layout.projects.close(directory)
layout.projects.open(root)
- if (params.dir && base64Decode(params.dir) === directory) {
+ if (params.dir && decode64(params.dir) === directory) {
navigateToProject(root)
}
}
@@ -1431,7 +1432,8 @@ export default function Layout(props: ParentProps) {
const dir = value.dir
const id = value.id
if (!dir || !id) return
- const directory = base64Decode(dir)
+ const directory = decode64(dir)
+ if (!directory) return
setStore("lastSession", directory, id)
notification.session.markViewed(id)
const expanded = untrack(() => store.workspaceExpanded[directory])
@@ -1454,7 +1456,7 @@ export default function Layout(props: ParentProps) {
if (!project) return
if (workspaceSetting()) {
- const activeDir = params.dir ? base64Decode(params.dir) : ""
+ const activeDir = decode64(params.dir) ?? ""
const dirs = [project.worktree, ...(project.sandboxes ?? [])]
for (const directory of dirs) {
const expanded = store.workspaceExpanded[directory] ?? directory === project.worktree
@@ -1504,7 +1506,7 @@ export default function Layout(props: ParentProps) {
const local = project.worktree
const dirs = [local, ...(project.sandboxes ?? [])]
const active = currentProject()
- const directory = active?.worktree === project.worktree && params.dir ? base64Decode(params.dir) : undefined
+ const directory = active?.worktree === project.worktree ? decode64(params.dir) : undefined
const extra = directory && directory !== local && !dirs.includes(directory) ? directory : undefined
const pending = extra ? WorktreeState.get(extra)?.status === "pending" : false
@@ -1930,7 +1932,7 @@ export default function Layout(props: ParentProps) {
})
const local = createMemo(() => props.directory === props.project.worktree)
const active = createMemo(() => {
- const current = params.dir ? base64Decode(params.dir) : ""
+ const current = decode64(params.dir) ?? ""
return current === props.directory
})
const workspaceValue = createMemo(() => {
@@ -2131,7 +2133,7 @@ export default function Layout(props: ParentProps) {
const SortableProject = (props: { project: LocalProject; mobile?: boolean }): JSX.Element => {
const sortable = createSortable(props.project.worktree)
const selected = createMemo(() => {
- const current = params.dir ? base64Decode(params.dir) : ""
+ const current = decode64(params.dir) ?? ""
return props.project.worktree === current || props.project.sandboxes?.includes(current)
})
diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx
index 39f5b057e..d316efef7 100644
--- a/packages/app/src/pages/session.tsx
+++ b/packages/app/src/pages/session.tsx
@@ -28,7 +28,7 @@ import { useSync } from "@/context/sync"
import { useTerminal, type LocalPTY } from "@/context/terminal"
import { useLayout } from "@/context/layout"
import { Terminal } from "@/components/terminal"
-import { checksum, base64Encode, base64Decode } from "@opencode-ai/util/encode"
+import { checksum, base64Encode } from "@opencode-ai/util/encode"
import { findLast } from "@opencode-ai/util/array"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { DialogSelectFile } from "@/components/dialog-select-file"
@@ -47,6 +47,7 @@ import { useComments, type LineComment } from "@/context/comments"
import { extractPromptFromParts } from "@/utils/prompt"
import { ConstrainDragYAxis, getDraggableId } from "@/utils/solid-dnd"
import { usePermission } from "@/context/permission"
+import { decode64 } from "@/utils/base64"
import { showToast } from "@opencode-ai/ui/toast"
import {
SessionHeader,
@@ -2126,8 +2127,28 @@ export default function Page() {
if (!isSvg()) return
const c = state()?.content
if (!c) return
- if (c.encoding === "base64") return base64Decode(c.content)
- return c.content
+ if (c.encoding !== "base64") return c.content
+ return decode64(c.content)
+ })
+
+ const svgDecodeFailed = createMemo(() => {
+ if (!isSvg()) return false
+ const c = state()?.content
+ if (!c) return false
+ if (c.encoding !== "base64") return false
+ return svgContent() === undefined
+ })
+
+ const svgToast = { shown: false }
+ createEffect(() => {
+ if (!svgDecodeFailed()) return
+ if (svgToast.shown) return
+ svgToast.shown = true
+ showToast({
+ variant: "error",
+ title: language.t("toast.file.loadFailed.title"),
+ description: "Invalid base64 content.",
+ })
})
const svgPreviewUrl = createMemo(() => {
if (!isSvg()) return