Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
ce875d4
docs(mobile): plan the Files pane redesign for /m
ashrafchowdury Sep 15, 2026
1673823
feat(drive): headless state for the Files pane redesign
ashrafchowdury Sep 15, 2026
347a70b
feat(ui): ListTable.wrapRow, inline markdown toolbar, drive.save shor…
ashrafchowdury Sep 15, 2026
c8bf44a
feat(drive): Files pane redesign — two-row chrome, list view, writes,…
ashrafchowdury Sep 15, 2026
28ef36f
fix(drive): first browser pass on /m
ashrafchowdury Sep 15, 2026
6d8ab34
fix(drive): a search narrows the folder content, not only the rail
ashrafchowdury Sep 15, 2026
4b9ee9b
test(drive): history, sort, draft, kinds; files-pane icon; chrome sto…
ashrafchowdury Sep 15, 2026
e17729b
fix(drive): review pass — visibility toggles take the shown value; no…
ashrafchowdury Sep 15, 2026
1eb02d6
fix(frontend): Tab into a folder keeps the @ file palette open
ashrafchowdury Sep 15, 2026
72456c6
refactor(drive): the pane's chrome on the kit's shadcn primitives
ashrafchowdury Sep 15, 2026
3f7756b
fix(drive): markdown editor prose at 14px
ashrafchowdury Sep 15, 2026
d9c26ef
feat(ui): Tabs pill variant; drive toolbar on pill Tabs with right-si…
ashrafchowdury Sep 15, 2026
b47596a
fix(drive): menu check mark at the menu's icon size
ashrafchowdury Sep 15, 2026
d892957
fix(drive): trigger chevrons sized below the leading icon
ashrafchowdury Sep 15, 2026
83db54d
fix(drive): list view on the pane's white surface; type mark inside t…
ashrafchowdury Sep 15, 2026
dd76afe
fix(drive): grid/list tab icons at 14px
ashrafchowdury Sep 15, 2026
36f7b86
fix(drive): view pill at the row's 26px control height
ashrafchowdury Sep 15, 2026
1623838
fix(drive): top inset above the list view
ashrafchowdury Sep 15, 2026
fd10c77
fix(drive): view-options menu as plain items with a right-side check
ashrafchowdury Sep 15, 2026
c0a9ea7
fix(drive): type mark lifts off the pane in dark mode; outline house …
ashrafchowdury Sep 15, 2026
489028a
feat(drive): temporary-files toggle keyed off the loaded listing
ashrafchowdury Sep 15, 2026
c8c9989
feat(drive): drop the Move to… action and its dialog kind
ashrafchowdury Sep 15, 2026
c35bb45
fix(mobile): files toggle tooltip on top, like the bar's other controls
ashrafchowdury Sep 15, 2026
4b3bf24
feat(drive): row 2 names the file, renamed in place
ashrafchowdury Sep 15, 2026
0071787
feat(drive): code / JSON / text files open in the shared code editor
ashrafchowdury Sep 15, 2026
07d4e44
feat(drive): autosave drafts; row 2 shows the save state
ashrafchowdury Sep 15, 2026
2beae33
perf(drive): edit caps, keystrokes stay inside the editor body
ashrafchowdury Sep 15, 2026
650d7ac
fix(drive): code editor fills the column and scrolls inside it
ashrafchowdury Sep 15, 2026
e9f02fd
fix(drive): no "Unsaved" label while an autosave is pending
ashrafchowdury Sep 15, 2026
c407c1c
fix(drive): breadcrumb links keep glyph and label on one line
ashrafchowdury Sep 15, 2026
cfd0969
feat(drive): Download in the file's ⋯ menu
ashrafchowdury Sep 15, 2026
69e5ef1
refactor(drive): one actions menu, in row 2
ashrafchowdury Sep 15, 2026
9b8ea3b
fix(drive): icons on the view-options items
ashrafchowdury Sep 15, 2026
169e53f
fix(drive): icons on the sort and editor-mode items
ashrafchowdury Sep 15, 2026
f6d274b
fix(drive): file tree rail caps at 320px (was 480)
ashrafchowdury Sep 15, 2026
6a6bef6
fix(drive): grid tiles stacked after a pane resize
ashrafchowdury Sep 15, 2026
38a47bb
fix(ui): SplitPane drag that never gets its pointerup no longer kills…
ashrafchowdury Sep 15, 2026
8f724f1
feat(ui): SplitPane revealContent — the files pane's content fades wi…
ashrafchowdury Sep 15, 2026
25702d8
feat(frontend): restyle Tabs to the shadcn look
ashrafchowdury Sep 15, 2026
0315b83
fix(drive): the docked pane's file tree starts closed
ashrafchowdury Sep 15, 2026
b450600
feat(drive): HTML files edit as code, with a Source / Preview pill in…
ashrafchowdury Sep 15, 2026
09c56ae
chore(drive): lint-fix and typecheck pass
ashrafchowdury Sep 15, 2026
93da97a
feat(drive): the config pane and agent overview open the docked Files…
ashrafchowdury Sep 15, 2026
51da656
fix(drive): config-pane file rows on the section's lines, with the pa…
ashrafchowdury Sep 15, 2026
43d9f47
fix(drive): review pass — leave-flush, delete/rename against an open …
ashrafchowdury Sep 15, 2026
3657653
chore(drive): drop exports nothing imports
ashrafchowdury Sep 16, 2026
d1a9b42
fix(drive): the no-preview fallback on the kit Empty parts
ashrafchowdury Sep 16, 2026
d1c6bce
fix(drive): tree toggle stays clickable while searching; the search's…
ashrafchowdury Sep 16, 2026
4609ec8
feat(drive): keyboard navigation across the Files pane
ashrafchowdury Sep 16, 2026
3045bc9
feat(drive): name new entries and renames in place, like a file system
ashrafchowdury Sep 16, 2026
c756d28
feat(drive): New folder / New file create at once, then name in place
ashrafchowdury Sep 16, 2026
202625d
feat(ui): a confirm can stay inside a pane; the Files pane's delete c…
ashrafchowdury Sep 16, 2026
635ee4b
fix(drive): an upload's tile keeps its slot until the listing carries…
ashrafchowdury Sep 16, 2026
729458b
fix(drive): one tree node per path
ashrafchowdury Sep 16, 2026
3d4d4df
feat(drive): media tiles show the file — image, PDF page, video frame
ashrafchowdury Sep 16, 2026
2104df9
fix(drive): review round one — CodeQL regexes, prefs on init, chips, …
ashrafchowdury Sep 16, 2026
3801484
feat(chat): fences highlight in One Dark, as one surface
ashrafchowdury Sep 16, 2026
b36a1c5
fix(mobile): 2px more leading on assistant prose
ashrafchowdury Sep 16, 2026
e843b55
fix(mobile): the config toggle wears the files pane's glyph, mirrored
ashrafchowdury Sep 16, 2026
c9776a1
fix(drive): the editor mode trigger shows the label only
ashrafchowdury Sep 16, 2026
bf531fb
fix(ui): editor toolbar icons at one 14px size
ashrafchowdury Sep 16, 2026
73262ab
feat(drive): the editor mode is an icon-only pill, like the grid/list…
ashrafchowdury Sep 16, 2026
5c6bdeb
feat(drive): drop Duplicate from the Files pane
ashrafchowdury Sep 16, 2026
d32cc2c
feat(drive): plain-text mode shows the file name in row 2
ashrafchowdury Sep 16, 2026
a41aad4
fix(drive): markdown / plain-text icons on the mode pill
ashrafchowdury Sep 16, 2026
c922784
fix(mobile): tab-bar pane toggles wear phosphor's SidebarSimple, not …
ashrafchowdury Sep 16, 2026
3921412
fix(mobile): the files toggle keeps full ink while the pane is closed
ashrafchowdury Sep 16, 2026
1df98eb
fix(build): the pdf worker copy script lives with the package that ow…
ashrafchowdury Sep 16, 2026
26e0970
fix(drive): text files open in the prose editor; no source-view flash…
ashrafchowdury Sep 16, 2026
a1f61ac
fix(ui): a markdown document opens at the top
ashrafchowdury Sep 16, 2026
fb5615d
feat(drive): the Files pane remembers its tree
ashrafchowdury Sep 16, 2026
70bd3ae
fix(drive): the tree follows a file opened from outside it
ashrafchowdury Sep 17, 2026
31dc9fe
fix(drive): the config pane's Files header is a count, not a second p…
ashrafchowdury Sep 17, 2026
42ee749
fix(drive): the Files count sits flush with the header's right edge
ashrafchowdury Sep 17, 2026
b498a4b
fix(drive): the Files error state says what failed once, in the body
ashrafchowdury Sep 17, 2026
79f8daa
fix(drive): the Files error line is one sentence, aligned with the ti…
ashrafchowdury Sep 17, 2026
c993356
fix(drive): config-pane file rows show the time only, not the size
ashrafchowdury Sep 17, 2026
d8feba4
feat(drive): a folder toggles the Files pane, a panel glyph toggles i…
ashrafchowdury Sep 17, 2026
fbe0947
feat(drive): a just-created entry stays at the top of its folder
ashrafchowdury Sep 17, 2026
a54fb08
feat(drive): the drive's error and empty states use the kit Empty
ashrafchowdury Sep 17, 2026
7e9de9d
fix(drive): a PDF thumbnail gives up on a worker that never starts
ashrafchowdury Sep 17, 2026
d475953
fix(mobile): drop the panel glyph import the config reveal no longer …
ashrafchowdury Sep 17, 2026
f7bcae7
feat(drive): newest first by default; a folder sorts by its time too
ashrafchowdury Sep 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions docs/design/agenta-mobile/plans/files-pane-redesign.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion web/ee/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ next-env.d.ts



# pdfjs worker copied into public/ at dev+build time (see ../oss/scripts/copy-pdf-worker.mjs)
# pdfjs worker copied into public/ at dev+build time (see ../packages/agenta-entities/scripts/copy-pdf-worker.mjs)
public/pdf.worker.min.mjs
8 changes: 4 additions & 4 deletions web/ee/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
"node": "24.x"
},
"scripts": {
"dev": "node ../oss/scripts/copy-pdf-worker.mjs public && next dev --turbopack",
"dev:local": "node ../oss/scripts/copy-pdf-worker.mjs public && ENV_FILE=.local.env next dev",
"dev:turbo": "node ../oss/scripts/copy-pdf-worker.mjs public && ENV_FILE=.local.env next dev --turbo",
"build": "node ../oss/scripts/copy-pdf-worker.mjs public && next build && cp -r public/. ./.next/standalone/ee/public && cp -r .next/static/. ./.next/standalone/ee/.next/static",
"dev": "node ../packages/agenta-entities/scripts/copy-pdf-worker.mjs public && next dev --turbopack",
"dev:local": "node ../packages/agenta-entities/scripts/copy-pdf-worker.mjs public && ENV_FILE=.local.env next dev",
"dev:turbo": "node ../packages/agenta-entities/scripts/copy-pdf-worker.mjs public && ENV_FILE=.local.env next dev --turbo",
"build": "node ../packages/agenta-entities/scripts/copy-pdf-worker.mjs public && next build && cp -r public/. ./.next/standalone/ee/public && cp -r .next/static/. ./.next/standalone/ee/.next/static",
"start": "next start",
"lint": "next lint",
"lint-fix": "next lint --fix",
Expand Down
2 changes: 2 additions & 0 deletions web/mobile/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
tsconfig.tsbuildinfo
# runtime config written by web/entrypoint.sh (dev mounts this dir from the host)
public/__env.js
# pdfjs worker copied into public/ at dev+build time (see ../packages/agenta-entities/scripts/copy-pdf-worker.mjs)
public/pdf.worker.min.mjs
4 changes: 2 additions & 2 deletions web/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"node": "24.x"
},
"scripts": {
"dev": "next dev",
"build": "next build && cp -r public/. .next/standalone/mobile/public && cp -r .next/static .next/standalone/mobile/.next",
"dev": "node ../packages/agenta-entities/scripts/copy-pdf-worker.mjs public && next dev",
"build": "node ../packages/agenta-entities/scripts/copy-pdf-worker.mjs public && next build && cp -r public/. .next/standalone/mobile/public && cp -r .next/static .next/standalone/mobile/.next",
"start": "next start",
"lint": "eslint src tests && pnpm run tokens:check",
"lint:fix": "eslint src tests --fix",
Expand Down
21 changes: 21 additions & 0 deletions web/mobile/public/pdf.worker.min.mjs

Large diffs are not rendered by default.

89 changes: 37 additions & 52 deletions web/mobile/src/features/agents/AgentDriveCard.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,42 @@
import {useMemo, useState} from "react"
import {useMemo} from "react"

import {AGENT_FILES_DIR, agentMountQueryFamily, useSessionDrive} from "@agenta/entities/drive"
import {
AGENT_FILES_DIR,
agentMountQueryFamily,
cleanPath,
humanSize,
itemCountLabel,
nameOf,
} from "@agenta/entities/drive"
import {latestMountFilesQueryFamily, type MountFile} from "@agenta/entities/session"
import {File, Folder} from "@phosphor-icons/react"
import {useAtomValue} from "jotai"
import dynamic from "next/dynamic"
import {
DriveFolderGlyph,
DriveTypeMark,
driveQuickLookAtomFamily,
sessionFilesPaneOpenAtomFamily,
} from "@agenta/entity-ui/drive"
import {useAtomValue, useStore} from "jotai"

import {useStartBlankSession} from "../chat/useStartBlankSession"

import {AgentOverviewCard} from "./AgentOverviewCard"
import {AgentOverviewCardRow} from "./AgentOverviewCardRow"
import {AgentOverviewCardSkeleton} from "./states/AgentOverviewCardSkeleton"

// The whole drive explorer, pulled in only once the drawer is actually opened.
const FilesDrawer = dynamic(
() => import("@agenta/entity-ui/drive").then((mod) => mod.FilesDrawer),
{ssr: false},
)

const ICON = 14
/** The card shows this many, newest first, and never more: the drawer is where the whole drive lives. */
/** Newest first; the Files pane is where the whole drive lives. */
const LIMIT = 5

const formatSize = (bytes: number | null | undefined): string | null => {
if (bytes == null) return null
if (bytes < 1024) return `${bytes} B`
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
}

const fileDetail = (file: MountFile): string | null =>
file.is_folder
? file.item_count != null
? `${file.item_count} item${file.item_count === 1 ? "" : "s"}`
? itemCountLabel(file.item_count)
: null
: formatSize(file.size)
: humanSize(file.size) || null

/**
* The agent's OWN drive — the files it carries between runs, not a session's scratch mount. The
* same mount and file queries the shared card reads, so this and the drawer share one cache.
*/
export const AgentDriveCard = ({agentId}: {agentId: string}) => {
const [openPath, setOpenPath] = useState<string | null>(null)
const [open, setOpen] = useState(false)
/** The agent's own drive. Opening a file starts a session with the docked pane on it. */
export const AgentDriveCard = ({agentId, base}: {agentId: string; base: string}) => {
const store = useStore()
const startBlank = useStartBlankSession(base)

const mountsAtom = useMemo(() => agentMountQueryFamily(agentId), [agentId])
const mounts = useAtomValue(mountsAtom)
Expand All @@ -51,15 +47,15 @@ export const AgentDriveCard = ({agentId}: {agentId: string}) => {
[mountId],
)
const files = useAtomValue(filesAtom)
// No session id: only the agent mount loads, and only once the drawer is open.
const drive = useSessionDrive("", open ? agentId : undefined)

const rows: MountFile[] = files.data?.files ?? []
const isPending = mounts.isPending || (Boolean(mountId) && files.isPending)

// Seed the pane's state before the route changes; both atoms are read on mount.
const openDrive = (path: string | null) => {
setOpenPath(path)
setOpen(true)
const sessionId = startBlank(agentId)
store.set(sessionFilesPaneOpenAtomFamily(agentId), true)
if (path) store.set(driveQuickLookAtomFamily(sessionId), {path})
}

return (
Expand All @@ -80,32 +76,21 @@ export const AgentDriveCard = ({agentId}: {agentId: string}) => {
rows.map((file) => (
<AgentOverviewCardRow
key={file.path}
icon={file.is_folder ? <Folder size={ICON} /> : <File size={ICON} />}
label={file.path.split("/").filter(Boolean).pop() || file.path}
icon={
file.is_folder ? (
<DriveFolderGlyph size={16} />
) : (
<DriveTypeMark path={file.path} size="mini" />
)
}
label={nameOf(cleanPath(file.path)) || file.path}
detail={fileDetail(file)}
title={file.path}
// The drive folds the agent mount in under `agent-files/`.
onClick={() => openDrive(`${AGENT_FILES_DIR}/${file.path}`)}
className="py-1.5"
/>
))
)}

{open ? (
<FilesDrawer
open={open}
onClose={() => {
setOpen(false)
setOpenPath(null)
}}
drive={drive}
scope="app"
initialPath={openPath}
driveIds={
mountId ? [{key: "mount", label: "Drive ID", value: mountId}] : undefined
}
/>
) : null}
</AgentOverviewCard>
)
}
2 changes: 1 addition & 1 deletion web/mobile/src/features/agents/AgentOverviewBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export const AgentOverviewBody = ({
// tap away in the session workspace.
<div className="hidden w-full flex-col gap-3.5 lg:flex">
<AgentConfigCard agentId={agentId} onEdit={onEditConfig} />
<AgentDriveCard agentId={agentId} />
<AgentDriveCard agentId={agentId} base={base} />
<AgentAutomationsCard agentId={agentId} agentNames={agentNames} base={base} />
</div>
}
Expand Down
31 changes: 22 additions & 9 deletions web/mobile/src/features/chat/AssistantMarkdown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {useTypewriter} from "@agenta/chat/hooks"
import ChatMarkdown from "@agenta/chat/markdown"
import {chatFileResolver} from "@agenta/entity-ui/drive"
import {Check, Copy} from "lucide-react"
import type {IconMap} from "streamdown"

/**
* Streamdown's built-in classes assume a 14–30px type scale; the mobile app's base is 12px.
Expand All @@ -12,23 +14,33 @@ import {chatFileResolver} from "@agenta/entity-ui/drive"
* From `sm:` the prose steps up to the desktop app's 14px body (oss AgentChatSlice/markdown.tsx),
* so a wide window reads at the same scale as /w instead of staying phone-sized.
*/
const proseClassName = [
"w-full min-w-0 space-y-2 overflow-hidden text-xs wrap-anywhere sm:text-sm",
export const proseClassName = [
// 20/22px leading, two over the size's own: prose that reads at length wants the air.
"w-full min-w-0 space-y-2 overflow-hidden text-xs leading-5 wrap-anywhere sm:text-sm sm:leading-[22px]",
"[&_a]:text-primary [&_a]:font-medium [&_a]:underline",
"[&_p]:text-foreground [&_p]:text-xs sm:[&_p]:text-sm",
"[&_p]:text-foreground [&_p]:text-xs [&_p]:leading-5 sm:[&_p]:text-sm sm:[&_p]:leading-[22px]",
"[&_:is(h1,h2,h3,h4,h5,h6)]:mt-3 [&_:is(h1,h2,h3,h4,h5,h6)]:mb-1",
"[&_h1]:text-base [&_:is(h2,h3)]:text-sm [&_:is(h4,h5,h6)]:text-xs sm:[&_:is(h4,h5,h6)]:text-sm",
"[&_:is(ul,ol)]:my-1 [&_li]:py-0.5 [&_li]:text-xs sm:[&_li]:text-sm",
"[&_blockquote]:my-2 [&_blockquote]:text-xs sm:[&_blockquote]:text-sm [&_blockquote_p]:text-muted-foreground",
"[&_:is(ul,ol)]:my-1 [&_li]:py-0.5 [&_li]:text-xs [&_li]:leading-5 sm:[&_li]:text-sm sm:[&_li]:leading-[22px]",
"[&_blockquote]:my-2 [&_blockquote]:text-xs [&_blockquote]:leading-5 sm:[&_blockquote]:text-sm sm:[&_blockquote]:leading-[22px] [&_blockquote_p]:text-muted-foreground",
"[&_hr]:my-3",
"[&_code]:text-[0.95em]",
"[&_:is(th,td)]:px-2 [&_:is(th,td)]:py-1 [&_:is(th,td)]:text-xs",
"[&_[data-streamdown=code-block]]:bg-muted [&_[data-streamdown=code-block]]:rounded-md",
"[&_[data-streamdown=code-block-actions]]:bg-muted",
"[&_[data-streamdown=code-block-body]]:bg-transparent [&_[data-streamdown=code-block-body]]:p-2 [&_[data-streamdown=code-block-body]]:text-xs",
// A fence as one surface: Streamdown ships three borders for one block.
"[&_[data-streamdown=code-block]]:my-2 [&_[data-streamdown=code-block]]:gap-0 [&_[data-streamdown=code-block]]:overflow-hidden [&_[data-streamdown=code-block]]:rounded-md [&_[data-streamdown=code-block]]:border-border [&_[data-streamdown=code-block]]:bg-muted [&_[data-streamdown=code-block]]:p-0",
"[&_[data-streamdown=code-block-header]]:h-7 [&_[data-streamdown=code-block-header]]:border-b [&_[data-streamdown=code-block-header]]:border-border [&_[data-streamdown=code-block-header]]:px-3 [&_[data-streamdown=code-block-header]]:text-[11px] [&_[data-streamdown=code-block-header]]:tracking-[0.02em] [&_[data-streamdown=code-block-header]_span]:ml-0",
// The actions' sticky frame is unlabelled: reach it as the header's sibling.
"[&_[data-streamdown=code-block-header]+div]:-mt-7 [&_[data-streamdown=code-block-header]+div]:h-7 [&_[data-streamdown=code-block-header]+div]:top-0 [&_[data-streamdown=code-block-header]+div]:pr-1.5",
"[&_[data-streamdown=code-block-actions]]:rounded-none [&_[data-streamdown=code-block-actions]]:border-0 [&_[data-streamdown=code-block-actions]]:bg-transparent [&_[data-streamdown=code-block-actions]]:p-0 [&_[data-streamdown=code-block-actions]]:backdrop-blur-none",
"[&_[data-streamdown=code-block-copy-button]]:flex [&_[data-streamdown=code-block-copy-button]]:size-[22px] [&_[data-streamdown=code-block-copy-button]]:items-center [&_[data-streamdown=code-block-copy-button]]:justify-center [&_[data-streamdown=code-block-copy-button]]:rounded [&_[data-streamdown=code-block-copy-button]]:p-0 [&_[data-streamdown=code-block-copy-button]]:hover:bg-accent",
"[&_[data-streamdown=code-block-body]]:rounded-none [&_[data-streamdown=code-block-body]]:border-0 [&_[data-streamdown=code-block-body]]:bg-transparent [&_[data-streamdown=code-block-body]]:px-3 [&_[data-streamdown=code-block-body]]:py-2.5 [&_[data-streamdown=code-block-body]]:text-xs [&_[data-streamdown=code-block-body]]:leading-[18px]",
"[&_[data-streamdown=table-wrapper]]:bg-muted",
].join(" ")

/** The app's own glyph set on the fence's copy control — Streamdown's default is a different
* icon family. Module scope: one identity across streamed tokens. */
export const markdownIcons: Partial<IconMap> = {CopyIcon: Copy, CheckIcon: Check}

/**
* Assistant message text rendered as markdown through the shared `ChatMarkdown` renderer, so
* mobile and desktop parse, highlight, and heal identically; only the token layer differs.
Expand All @@ -38,7 +50,7 @@ const proseClassName = [
*/
/** A markdown link to a path, not to the web, opens the file in the Files pane (#6659). Module
* scope so the renderer's resolver context keeps a stable identity across streamed tokens. */
const useDriveLinkResolver = () => chatFileResolver
export const useDriveLinkResolver = () => chatFileResolver

export const AssistantMarkdown = ({
streaming,
Expand All @@ -57,6 +69,7 @@ export const AssistantMarkdown = ({
content={revealed}
streaming={streaming || !settled}
useLinkResolver={useDriveLinkResolver}
icons={markdownIcons}
/>
)
}
Expand Down
15 changes: 15 additions & 0 deletions web/mobile/src/features/chat/DriveMarkdownRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import ChatMarkdown from "@agenta/chat/markdown"
import type {DriveMarkdownProps} from "@agenta/entity-ui/drive"

import {markdownIcons, proseClassName, useDriveLinkResolver} from "./AssistantMarkdown"

/** The Files pane's markdown renderer on `/m`: the chat's own, registered once in `_app`. */
export const DriveMarkdownRenderer = ({content, className}: DriveMarkdownProps) => (
<ChatMarkdown
content={content}
baseClassName={proseClassName}
className={className}
useLinkResolver={useDriveLinkResolver}
icons={markdownIcons}
/>
)
80 changes: 44 additions & 36 deletions web/mobile/src/features/chat/SessionTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {SessionTabRail, withShortcutKey} from "@agenta/sessions-ui"
import {shortcutAria} from "@agenta/shared/utils"
import {ShortcutKeys} from "@agenta/ui/shortcuts"
import {Button, SimpleTooltip} from "@agenta/ui/ui"
import {Folder, FolderOpen} from "@phosphor-icons/react"
import {useQuery} from "@tanstack/react-query"
import {useAtomValue} from "jotai"
import {ChevronsLeft} from "lucide-react"
import {useRouter} from "next/router"

import {PageTitle} from "@/components/PageTitle"
Expand Down Expand Up @@ -56,7 +56,10 @@ export const SessionTabs = ({
const startBlank = useStartBlankSession(base)
const closeTabs = useSessionTabClose({agentId, sessionId, base})
const configCollapsed = useAtomValue(configPanelCollapsedAtom)
const {open: filesOpen, openPane} = useSessionFilesPane(agentId ?? sessionId, sessionId)
const {open: filesOpen, toggle: toggleFiles} = useSessionFilesPane(
agentId ?? sessionId,
sessionId,
)
// Key leads with `session-stream`: a rename patches by key PREFIX, so a nested key never
// matches and the title lags. The singular GET redirects onto the web app, so POST it.
const query = useQuery({
Expand Down Expand Up @@ -112,40 +115,45 @@ export const SessionTabs = ({
!chatMaximized && configCollapsed ? <ConfigRevealButton /> : undefined
}
extra={
chatMaximized ? undefined : (
<>
<InspectSessionButton sessionId={sessionId} />
{/* Same slot and order as the desktop bar: inspector, history, then
the files opener at the right edge the pane expands from. */}
<SessionHistoryMenu
agentId={agentId}
base={base}
activeSessionId={sessionId}
/>
{filesOpen ? null : (
<SimpleTooltip
title={
<span className="flex items-center gap-1.5">
Show files{" "}
<ShortcutKeys id="panel.files" tone="inverse" />
</span>
}
side="left"
>
<Button
variant="ghost"
size="icon-sm"
aria-label="Show files pane"
aria-keyshortcuts={shortcutAria("panel.files")}
onClick={openPane}
className="h-7 w-7 shrink-0 p-0"
>
<ChevronsLeft size={14} />
</Button>
</SimpleTooltip>
)}
</>
)
<>
{chatMaximized ? null : (
<>
<InspectSessionButton sessionId={sessionId} />
<SessionHistoryMenu
agentId={agentId}
base={base}
activeSessionId={sessionId}
/>
</>
)}
{/* Shows the state and flips it; hidden below md, where the pane never mounts. */}
<SimpleTooltip
title={
<span className="flex items-center gap-1.5">
{filesOpen ? "Hide files" : "Show files"}{" "}
<ShortcutKeys id="panel.files" tone="inverse" />
</span>
}
>
<Button
variant="ghost"
size="icon-sm"
aria-label={filesOpen ? "Hide files pane" : "Show files pane"}
aria-pressed={filesOpen}
aria-keyshortcuts={shortcutAria("panel.files")}
onClick={toggleFiles}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
// The glyph's weight carries the state; no colour shift on top.
className="h-7 w-7 shrink-0 p-0 max-md:hidden"
>
{/* A folder says "files" where a panel glyph wouldn't; open = pane shown. */}
{filesOpen ? (
<FolderOpen size={14} weight="fill" />
) : (
<Folder size={14} />
)}
</Button>
</SimpleTooltip>
</>
}
/>
</>
Expand Down
Loading
Loading