@rexvane/piweb 0.3.9 → 0.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +10 -10
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/react-loadable-manifest.json +2 -2
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/agent/[id]/events/route.js +2 -2
- package/.next/server/app/api/agent/[id]/events/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/[id]/route.js +1 -1
- package/.next/server/app/api/agent/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/[id]/tree/route.js +1 -1
- package/.next/server/app/api/agent/[id]/tree/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/new/route.js +1 -1
- package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
- package/.next/server/app/api/growth/route.js +1 -1
- package/.next/server/app/api/growth/route.js.nft.json +1 -1
- package/.next/server/app/api/models/route.js +1 -1
- package/.next/server/app/api/models/route.js.nft.json +1 -1
- package/.next/server/app/api/plugins/route.js +1 -1
- package/.next/server/app/api/plugins/route.js.nft.json +1 -1
- package/.next/server/app/api/security/route.js +1 -1
- package/.next/server/app/api/security/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/route.js +1 -1
- package/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route.js +2 -2
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/update/route.js +2 -2
- package/.next/server/app/api/update/route.js.nft.json +1 -1
- package/.next/server/app/api/version/route.js +1 -1
- package/.next/server/app/api/workspaces/route.js +1 -1
- package/.next/server/app/api/workspaces/route.js.nft.json +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +4 -4
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +1 -1
- package/.next/server/app/login.rsc +3 -3
- package/.next/server/app/login.segments/_full.segment.rsc +3 -3
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +3 -3
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +10 -10
- package/.next/server/chunks/229.js +1 -0
- package/.next/server/chunks/496.js +1 -1
- package/.next/server/chunks/686.js +2 -2
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/127.c31e688e156605e3.js +1 -0
- package/.next/static/chunks/686-9f15e4e370d26815.js +1 -0
- package/.next/static/chunks/app/page-708cd3ee4cfe8af5.js +1 -0
- package/.next/static/chunks/webpack-9d84b382d4dec27a.js +1 -0
- package/.next/static/css/b2d35abc828d4713.css +1 -0
- package/README.md +4 -3
- package/package.json +1 -1
- package/src/app/api/workspaces/route.ts +4 -0
- package/src/components/AppShell.tsx +47 -41
- package/src/components/ChatWindow.tsx +118 -8
- package/src/components/SettingsPanel.tsx +127 -2
- package/src/hooks/usePiWeb.ts +27 -11
- package/src/i18n.tsx +52 -0
- package/src/lib/agent-manager.ts +77 -3
- package/src/lib/event-batcher.ts +62 -0
- package/src/lib/models-service.ts +430 -19
- package/src/lib/process-format.ts +27 -10
- package/src/lib/provider-usage.ts +91 -0
- package/src/lib/session-retention.ts +119 -0
- package/src/lib/types.ts +2 -0
- package/.next/server/chunks/780.js +0 -1
- package/.next/static/chunks/18.8b025ee3303c3dc7.js +0 -1
- package/.next/static/chunks/686-7861c776412c335c.js +0 -1
- package/.next/static/chunks/app/page-8ef773ce62ce3aaa.js +0 -1
- package/.next/static/chunks/webpack-eebc3d300017fa47.js +0 -1
- package/.next/static/css/080ec3dba8907e83.css +0 -1
- /package/.next/static/{tU0v6KuZoq5gHtJ8zOyb1 → 222KLUIvefiGf7M0Ov-Qz}/_buildManifest.js +0 -0
- /package/.next/static/{tU0v6KuZoq5gHtJ8zOyb1 → 222KLUIvefiGf7M0Ov-Qz}/_ssgManifest.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,::backdrop,:after,:before{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial}}}@layer theme{:host,:root{--font-sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--font-weight-medium:500;--font-weight-bold:700;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--animate-spin:spin 1s linear infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,::backdrop,:after,:before{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}:host,html{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}menu,ol,ul{list-style:none}audio,canvas,embed,iframe,img,object,svg,video{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,optgroup,select,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.\!relative{position:relative!important}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{inset-inline:0}.-top-1{top:calc(var(--spacing) * -1)}.-top-1\.5{top:calc(var(--spacing) * -1.5)}.top-0{top:0}.top-0\.5{top:calc(var(--spacing) * .5)}.top-1\/2{top:50%}.top-3{top:calc(var(--spacing) * 3)}.top-5{top:calc(var(--spacing) * 5)}.top-6{top:calc(var(--spacing) * 6)}.top-8{top:calc(var(--spacing) * 8)}.top-9{top:calc(var(--spacing) * 9)}.top-full{top:100%}.-right-1{right:calc(var(--spacing) * -1)}.-right-1\.5{right:calc(var(--spacing) * -1.5)}.right-0{right:0}.right-1{right:var(--spacing)}.right-2{right:calc(var(--spacing) * 2)}.right-4{right:calc(var(--spacing) * 4)}.right-5{right:calc(var(--spacing) * 5)}.bottom-2{bottom:calc(var(--spacing) * 2)}.bottom-9{bottom:calc(var(--spacing) * 9)}.bottom-10{bottom:calc(var(--spacing) * 10)}.bottom-24{bottom:calc(var(--spacing) * 24)}.bottom-full{bottom:100%}.left-0{left:0}.left-1\/2{left:50%}.left-3{left:calc(var(--spacing) * 3)}.z-20{z-index:20}.z-50{z-index:50}.z-\[70\]{z-index:70}.z-\[80\]{z-index:80}.z-\[85\]{z-index:85}.z-\[95\]{z-index:95}.z-\[100\]{z-index:100}.z-\[105\]{z-index:105}.z-\[110\]{z-index:110}.z-\[120\]{z-index:120}.z-\[130\]{z-index:130}.z-\[9999\]{z-index:9999}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-1{margin-inline:var(--spacing)}.mx-3{margin-inline:calc(var(--spacing) * 3)}.mx-auto{margin-inline:auto}.my-1{margin-block:var(--spacing)}.my-1\.5{margin-block:calc(var(--spacing) * 1.5)}.my-2{margin-block:calc(var(--spacing) * 2)}.-mt-3{margin-top:calc(var(--spacing) * -3)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-7{margin-top:calc(var(--spacing) * 7)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-1{height:var(--spacing)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-8{height:calc(var(--spacing) * 8)}.h-11{height:calc(var(--spacing) * 11)}.h-14{height:calc(var(--spacing) * 14)}.h-\[42px\]{height:42px}.h-full{height:100%}.h-screen{height:100vh}.max-h-72{max-height:calc(var(--spacing) * 72)}.max-h-96{max-height:calc(var(--spacing) * 96)}.max-h-\[70vh\]{max-height:70vh}.max-h-\[340px\]{max-height:340px}.min-h-0{min-height:0}.min-h-10{min-height:calc(var(--spacing) * 10)}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-14{width:calc(var(--spacing) * 14)}.w-36{width:calc(var(--spacing) * 36)}.w-44{width:calc(var(--spacing) * 44)}.w-52{width:calc(var(--spacing) * 52)}.w-56{width:calc(var(--spacing) * 56)}.w-64{width:calc(var(--spacing) * 64)}.w-\[260px\]{width:260px}.w-\[312px\]{width:312px}.w-\[calc\(100\%-8px\)\]{width:calc(100% - 8px)}.w-\[min\(92vw\,360px\)\]{width:min(92vw,360px)}.w-\[min\(92vw\,380px\)\]{width:min(92vw,380px)}.w-\[min\(92vw\,480px\)\]{width:min(92vw,480px)}.w-full{width:100%}.w-max{width:max-content}.w-screen{width:100vw}.max-w-\[45\%\]{max-width:45%}.max-w-\[85\%\]{max-width:85%}.max-w-\[90vw\]{max-width:90vw}.max-w-\[180px\]{max-width:180px}.max-w-\[220px\]{max-width:220px}.max-w-\[420px\]{max-width:420px}.max-w-\[calc\(100vw-24px\)\]{max-width:calc(100vw - 24px)}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.min-w-0{min-width:0}.min-w-4{min-width:calc(var(--spacing) * 4)}.min-w-\[86px\]{min-width:86px}.flex-1{flex:1}.flex-none{flex:none}.grow{flex-grow:1}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1)}.-translate-x-1\/2,.-translate-y-1\/2{translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-spin{animation:var(--animate-spin)}.cursor-col-resize{cursor:col-resize}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-3\.5{gap:calc(var(--spacing) * 3.5)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-\[6px\]{gap:6px}.gap-x-2{column-gap:calc(var(--spacing) * 2)}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-y-1{row-gap:var(--spacing)}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.rounded-\[10px\]{border-radius:10px}.rounded-\[24px\]{border-radius:24px}.rounded-full{border-radius:3.40282e+38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-\[var\(--dsw-border-l1\)\]{border-color:var(--dsw-border-l1)}.border-\[var\(--dsw-label-tertiary\)\]{border-color:var(--dsw-label-tertiary)}.border-current{border-color:currentColor}.border-transparent{border-color:#0000}.border-white\/10{border-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.border-white\/10{border-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.border-white\/15{border-color:#ffffff26}@supports (color:color-mix(in lab,red,red)){.border-white\/15{border-color:color-mix(in oklab,var(--color-white) 15%,transparent)}}.border-t-transparent{border-top-color:#0000}.bg-\[\#2c2c2c\]{background-color:#2c2c2c}.bg-\[\#212121\]{background-color:#212121}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-white\/\[0\.05\]{background-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.bg-white\/\[0\.05\]{background-color:color-mix(in oklab,var(--color-white) 5%,transparent)}}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-1{padding:var(--spacing)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-7{padding:calc(var(--spacing) * 7)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-12{padding-inline:calc(var(--spacing) * 12)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-3\.5{padding-block:calc(var(--spacing) * 3.5)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-10{padding-block:calc(var(--spacing) * 10)}.pt-1{padding-top:var(--spacing)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-1{padding-right:var(--spacing)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pb-0{padding-bottom:0}.pb-1{padding-bottom:var(--spacing)}.pb-1\.5{padding-bottom:calc(var(--spacing) * 1.5)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-5{padding-bottom:calc(var(--spacing) * 5)}.pl-1{padding-left:var(--spacing)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-5{padding-left:calc(var(--spacing) * 5)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[14\.5px\]{font-size:14.5px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.text-ellipsis{text-overflow:ellipsis}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#f87171\]{color:#f87171}.text-\[var\(--dsw-label-primary\)\]{color:var(--dsw-label-primary)}.text-black{color:var(--color-black)}.text-white{color:var(--color-white)}.text-zinc-200{color:var(--color-zinc-200)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-400{color:var(--color-zinc-400)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.opacity-0{opacity:0}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040)}.shadow-2xl,.shadow-xl{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.group-focus-within\:flex:is(:where(.group):focus-within *){display:flex}.group-focus-within\:hidden:is(:where(.group):focus-within *){display:none}@media (hover:hover){.group-hover\:block:is(:where(.group):hover *){display:block}.group-hover\:flex:is(:where(.group):hover *){display:flex}.group-hover\:hidden:is(:where(.group):hover *){display:none}.group-hover\/tool\:opacity-100:is(:where(.group\/tool):hover *){opacity:1}}.first\:mt-0:first-child{margin-top:0}@media (hover:hover){.hover\:border-\[\#f87171\]\/40:hover{border-color:oklab(71.0627% .153814 .0628211/.4)}.hover\:border-white\/25:hover{border-color:#ffffff40}@supports (color:color-mix(in lab,red,red)){.hover\:border-white\/25:hover{border-color:color-mix(in oklab,var(--color-white) 25%,transparent)}}.hover\:bg-\[\#383838\]:hover{background-color:#383838}.hover\:bg-\[var\(--dsw-hover\)\]:hover{background-color:var(--dsw-hover)}.hover\:bg-white\/10:hover{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/10:hover{background-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.hover\:bg-zinc-200:hover{background-color:var(--color-zinc-200)}.hover\:text-\[\#ef4444\]:hover{color:#ef4444}.hover\:text-white:hover{color:var(--color-white)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:border-white\/40:focus{border-color:#fff6}@supports (color:color-mix(in lab,red,red)){.focus\:border-white\/40:focus{border-color:color-mix(in oklab,var(--color-white) 40%,transparent)}}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-white\/30:focus{--tw-ring-color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.focus\:ring-white\/30:focus{--tw-ring-color:color-mix(in oklab,var(--color-white) 30%,transparent)}}.focus-visible\:opacity-100:focus-visible{opacity:1}.disabled\:cursor-wait:disabled{cursor:wait}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}:root{--dsw-deepseek-400:#679efe;--dsw-deepseek-450:#5686fe;--dsw-deepseek-500:#4176e6;--dsw-amber-500:#f59e0b;--dsw-green-500:#22c55e;--dsw-red-600:#ec1313;--dsw-bg-base:#fff;--dsw-bg-elevated:#fff;--dsw-sidebar-fill:#f9fafb;--dsw-input-major:#fff;--dsw-bubble:#eff4ff;--dsw-hover:#0000000a;--dsw-active:#0000000f;--dsw-label-primary:#000;--dsw-label-secondary:#404042;--dsw-label-tertiary:#6e6e73;--dsw-label-caption:#96969b;--dsw-border-l1:#0000000a;--dsw-border-l2:#0000001a;--dsw-border-l3:#0000001f;--dsw-border-l4:#00000029;--dsw-accent:var(--dsw-deepseek-500);--dsw-accent-soft:#4176e61a;--dsw-warn:var(--dsw-amber-500);--dsw-success:var(--dsw-green-500);--dsw-danger:var(--dsw-red-600);--dsw-selector:#0000000d;--dsw-layer-2:#fff;--dsw-layer-3:#fff;--dsw-btn-elevated:#fff;--dsw-btn-floating-hover:#f0f1f3;--dsw-module-platform:#eeeff1;--dsw-border-strong:#adb2b8;--dsw-glass-popover:#fff;--dsw-glass-input:#fff;--dsw-glass-modal:#fff;--dsw-mask:#0000003d;--dsw-mask-blur:blur(6px);--dsw-elevation-stroke:var(--dsw-border-l2);--dsw-elevation-soft:0 0 0 .5px var(--dsw-elevation-stroke),0 4px 16px #00000008,0 0 24px #00000008;--dsw-elevation-panel:0 0 0 .5px var(--dsw-elevation-stroke),0 8px 32px #00000014;--dsw-elevation-prominent:0 0 0 .5px var(--dsw-elevation-stroke),0 16px 48px #00000029;--ds-ease-in-out:cubic-bezier(.4,0,.2,1);--ds-duration-fast:.1s;--ds-duration-normal:.2s;--ds-duration-slow:.3s;--dsh-content-font-size:14px;--dsh-content-font-delta:0px;--dsh-content-font-size-secondary:13px;--piweb-chat-font-size:var(--dsh-content-font-size);--piweb-chat-font-l:calc(var(--piweb-chat-font-size) * .96);--piweb-chat-font-m:calc(var(--piweb-chat-font-size) * .93);--piweb-chat-font-s:calc(var(--piweb-chat-font-size) * .89);--piweb-chat-font-t:calc(var(--piweb-chat-font-size) * .86);--piweb-chat-font-xs:calc(var(--piweb-chat-font-size) * .82);--font-ui:"Maple Mono CN",ui-monospace,SFMono-Regular,Menlo,Consolas,"PingFang SC","Microsoft YaHei",monospace;--font-mono:"Maple Mono CN",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;--dsh-chat-content-width:clamp(544px,51.2%,736px);--dsh-composer-card-max-width:calc(var(--dsh-chat-content-width) + 32px)}@media (prefers-color-scheme:dark){:root:not([data-ds-light-theme]),body:not([data-ds-light-theme]){--dsw-bg-base:#151517;--dsw-bg-elevated:#1e1e20;--dsw-sidebar-fill:#1b1b1c;--dsw-input-major:#2c2c2e;--dsw-bubble:#26282e;--dsw-hover:#ffffff0d;--dsw-active:#ffffff14;--dsw-label-primary:#f9fafb;--dsw-label-secondary:#cfd3d6;--dsw-label-tertiary:#adb2b8;--dsw-label-caption:#81858c;--dsw-border-l1:#ffffff0f;--dsw-border-l2:#ffffff1f;--dsw-border-l3:#ffffff29;--dsw-border-l4:#fff3;--dsw-accent:var(--dsw-deepseek-400);--dsw-accent-soft:#679efe24;--dsw-danger:#f25a5a;--dsw-selector:#ffffff14;--dsw-layer-2:#2c2c2e;--dsw-layer-3:#353638;--dsw-btn-elevated:#43454a;--dsw-btn-floating-hover:#353638;--dsw-module-platform:#353638;--dsw-glass-popover:#353638;--dsw-glass-input:#2c2c2e;--dsw-glass-modal:#2c2c2e;--dsw-mask:#00000080;--dsw-elevation-stroke:var(--dsw-border-l2);--dsw-elevation-soft:0 0 0 .5px var(--dsw-elevation-stroke),0 4px 16px #0003,0 0 24px #0003;--dsw-elevation-panel:0 0 0 .5px var(--dsw-elevation-stroke),0 8px 32px #0006;--dsw-elevation-prominent:0 0 0 .5px var(--dsw-elevation-stroke),0 16px 48px #0000008c}}body[data-ds-dark-theme],html[data-ds-dark-theme]{--dsw-bg-base:#151517;--dsw-bg-elevated:#1e1e20;--dsw-sidebar-fill:#1b1b1c;--dsw-input-major:#2c2c2e;--dsw-bubble:#26282e;--dsw-hover:#ffffff0d;--dsw-active:#ffffff14;--dsw-label-primary:#f9fafb;--dsw-label-secondary:#cfd3d6;--dsw-label-tertiary:#adb2b8;--dsw-label-caption:#81858c;--dsw-border-l1:#ffffff0f;--dsw-border-l2:#ffffff1f;--dsw-border-l3:#ffffff29;--dsw-border-l4:#fff3;--dsw-accent:var(--dsw-deepseek-400);--dsw-accent-soft:#679efe24;--dsw-danger:#f25a5a;--dsw-selector:#ffffff14;--dsw-layer-2:#2c2c2e;--dsw-layer-3:#353638;--dsw-btn-elevated:#43454a;--dsw-btn-floating-hover:#353638;--dsw-module-platform:#353638;--dsw-glass-popover:#353638;--dsw-glass-input:#2c2c2e;--dsw-glass-modal:#2c2c2e;--dsw-mask:#00000080;--dsw-elevation-stroke:var(--dsw-border-l2);--dsw-elevation-soft:0 0 0 .5px var(--dsw-elevation-stroke),0 4px 16px #0003,0 0 24px #0003;--dsw-elevation-panel:0 0 0 .5px var(--dsw-elevation-stroke),0 8px 32px #0006;--dsw-elevation-prominent:0 0 0 .5px var(--dsw-elevation-stroke),0 16px 48px #0000008c}body,html{height:100%}body{background:var(--dsw-bg-base);color:var(--dsw-label-primary);font-family:var(--font-ui);font-size:var(--dsh-content-font-size);line-height:calc(24px + var(--dsh-content-font-delta));-webkit-font-smoothing:antialiased;overflow:hidden}button{font-size:inherit;cursor:pointer;padding:0}button,input,select,textarea{font-family:inherit;color:inherit;background:0 0;border:none}input,select,textarea{outline:none}select{color-scheme:light}select option{background:var(--dsw-layer-2);color:var(--dsw-label-primary)}body[data-ds-dark-theme] select{color-scheme:dark}input::placeholder,textarea::placeholder{color:var(--dsw-label-caption)}*{box-sizing:border-box;corner-shape:superellipse(1.5)}@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}@media (max-width:840px){:root{--dsh-chat-content-width:100%;--dsh-composer-card-max-width:100%}.pi-main>.hairline-b{padding-left:56px;padding-right:12px}.settings-dialog{flex-direction:column;border-radius:22px!important;width:calc(100vw - 16px)!important;max-width:none!important;height:calc(100vh - 16px)!important}.settings-nav{border-bottom:.5px solid var(--dsw-border-l2);overflow-x:auto;flex-direction:row!important;width:100%!important;padding:10px!important}.settings-nav>button{flex:none;height:36px}.settings-title{display:none}}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{background:var(--dsw-active);background-clip:content-box;border:2.5px solid #0000;border-radius:8px}::-webkit-scrollbar-thumb:hover{background:var(--dsw-border-l3);background-clip:content-box;border:2px solid #0000}::-webkit-scrollbar-track{background:0 0}.icon-btn{width:28px;height:28px;color:var(--dsw-label-secondary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out),color var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:8px;justify-content:center;align-items:center;display:inline-flex}.icon-btn:hover{background:var(--dsw-hover)}.chip,.icon-btn:hover{color:var(--dsw-label-primary)}.chip{height:28px;white-space:nowrap;transition:background var(--ds-duration-fast) var(--ds-ease-in-out),color var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:24px;align-items:center;gap:5px;padding:0 10px;font-size:13px;font-weight:500;line-height:20px;display:inline-flex}.chip:hover{background:var(--dsw-hover);color:var(--dsw-label-primary)}.chip .chevron{color:var(--dsw-label-caption);transition:transform .12s var(--ds-ease-in-out)}.chip[data-open=true] .chevron{transform:rotate(180deg)}.hero-chip{height:28px;color:var(--dsw-label-primary);white-space:nowrap;cursor:pointer;transition:background var(--ds-duration-fast) var(--ds-ease-in-out);background:0 0;border-radius:14px;align-items:center;gap:6px;padding:0 8px;font-size:13px;font-weight:500;line-height:20px;display:inline-flex}.hero-chip:hover,.hero-chip[data-open=true]{background:var(--dsw-hover)}.hero-chip .hero-chip-icon{color:var(--dsw-label-primary);flex:none}.hero-chip .chevron{color:var(--dsw-label-caption);transition:transform .12s var(--ds-ease-in-out);flex:none}.hero-chip[data-open=true] .chevron{transform:rotate(180deg)}.btn-outline{border:.5px solid var(--dsw-border-l3);height:36px;color:var(--dsw-label-primary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out);background:0 0;border-radius:18px;justify-content:center;align-items:center;gap:6px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.btn-outline:hover{background:var(--dsw-hover)}.btn-primary-white{background:var(--dsw-label-primary);height:36px;color:var(--dsw-bg-base);transition:opacity var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:18px;justify-content:center;align-items:center;gap:6px;padding:0 14px;font-size:14px;font-weight:500;line-height:22px;display:inline-flex}.btn-primary-white:disabled{opacity:.5;cursor:default}.btn-new-session{border:.5px solid var(--dsw-border-l3);background:var(--dsw-btn-elevated);height:38px;color:var(--dsw-label-primary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:12px;justify-content:center;align-items:center;gap:6px;padding:8px 16px;font-size:14px;font-weight:500;line-height:22px;display:inline-flex}.btn-new-session:hover{background:var(--dsw-btn-floating-hover)}.select-chip{border:.5px solid var(--dsw-border-l2);height:32px;color:var(--dsw-label-primary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:16px;align-items:center;gap:6px;padding:0 12px;font-size:13px;display:inline-flex}.select-chip:hover{background:var(--dsw-hover)}.tab-underline{color:var(--dsw-label-tertiary);transition:color var(--ds-duration-fast) var(--ds-ease-in-out);padding:6px 2px;font-size:13.5px;position:relative}.tab-underline:hover{color:var(--dsw-label-primary)}.tab-underline[data-active=true]{color:var(--dsw-accent);font-weight:600}.tab-underline:after{content:"";background:0 0;border-radius:2px;height:2px;position:absolute;bottom:-1px;left:0;right:0}.tab-underline[data-active=true]:after{background:var(--dsw-accent)}.settings-tab-underline{color:var(--dsw-label-tertiary);transition:color var(--ds-duration-fast) var(--ds-ease-in-out);padding:6px 2px 10px;font-size:13.5px;position:relative}.settings-tab-underline:hover{color:var(--dsw-label-primary)}.settings-tab-underline[data-active=true]{color:var(--dsw-label-primary);font-weight:500}.settings-tab-underline:after{content:"";background:0 0;border-radius:2px;height:2px;position:absolute;bottom:-1px;left:0;right:0}.settings-tab-underline[data-active=true]:after{background:var(--dsw-label-primary)}.btn-primary-circle{background:var(--dsw-accent);color:#fff;width:34px;height:34px;transition:opacity var(--ds-duration-fast) var(--ds-ease-in-out),transform var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:999px;flex:none;justify-content:center;align-items:center;margin-top:-3px;margin-bottom:-3px;display:inline-flex;transform:translateY(-2px)}.btn-primary-circle:disabled{opacity:.4;cursor:default}.btn-ghost-circle{width:30px;height:30px;color:var(--dsw-label-secondary);border:.5px solid var(--dsw-border-l2);background:0 0;border-radius:999px;flex:none;justify-content:center;align-items:center;margin-top:-3px;margin-bottom:-3px;display:inline-flex;transform:translateY(-2px)}.btn-ghost-circle:hover{color:var(--dsw-danger);border-color:var(--dsw-danger)}.pw-queue{flex-direction:column;gap:3px;margin:0 14px 8px;font-size:12.5px;display:flex}.pw-queue-row{min-width:0;color:var(--dsw-label-secondary);align-items:center;gap:8px;display:flex}.pw-queue-tag{text-transform:capitalize;color:var(--dsw-label-tertiary);border:.5px solid var(--dsw-border-l2);border-radius:4px;flex:none;padding:0 6px;font-size:10.5px;line-height:16px}.pw-queue-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.pw-queue-foot{color:var(--dsw-label-caption);gap:12px;margin-top:1px;padding-left:2px;font-size:11.5px;display:flex}.pw-queue-foot button:hover{color:var(--dsw-label-secondary)}.pw-busy-hint{color:var(--dsw-label-caption);margin:6px 14px 0;font-size:11.5px;line-height:16px}.hairline-t{border-top:.5px solid var(--dsw-border-l2)}.hairline-b{border-bottom:.5px solid var(--dsw-border-l2)}.hairline-r{border-right:.5px solid var(--dsw-border-l2)}.rail-btn{width:36px;height:36px;color:var(--dsw-label-secondary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out),color var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:12px;justify-content:center;align-items:center;display:inline-flex}.rail-btn:hover{background:var(--dsw-hover);color:var(--dsw-label-primary)}.state-dot{border-radius:999px;flex:none;width:8px;height:8px;position:relative}.state-dot.running{background:var(--dsw-deepseek-450);animation:dot-breathe 1s ease-in-out infinite}.state-dot.pending{background:var(--dsw-warn);box-shadow:0 0 0 3px #f59e0b1a}.state-dot.done{background:var(--dsw-success)}@keyframes dot-breathe{0%,to{opacity:1}50%{opacity:.45}}.popover{background:var(--dsw-glass-popover);box-shadow:var(--dsw-elevation-prominent);border:.5px solid var(--dsw-border-l1);border-radius:16px;padding:6px;overflow:hidden}.modal-mask{background:var(--dsw-mask);-webkit-backdrop-filter:var(--dsw-mask-blur)}.glass-modal{background:var(--dsw-glass-modal)}.md{overflow-wrap:break-word;min-width:0;font-size:var(--piweb-chat-font-size);line-height:1.65}.md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}.md p{margin:.5em 0}.md h1,.md h2,.md h3,.md h4{margin:1em 0 .4em;font-weight:600;line-height:1.35}.md h1{font-size:1.35em}.md h2{font-size:1.22em}.md h3{font-size:1.1em}.md ol,.md ul{margin:.5em 0;padding-left:1.5em}.md li{margin:.2em 0}.md a{color:var(--dsw-accent);text-decoration:none}.md a:hover{text-decoration:underline}.md blockquote{border-left:3px solid var(--dsw-border-l3);color:var(--dsw-label-secondary);margin:.6em 0;padding:.1em 0 .1em .9em}.md code{font-family:var(--font-mono);border-radius:5px;padding:.1em .35em;font-size:.9em}.md code,.md pre{background:var(--dsw-selector)}.md pre{border:.5px solid var(--dsw-border-l1);border-radius:12px;margin:.7em 0;padding:12px 14px;overflow-x:auto}.md pre code{background:0 0;padding:0;font-size:13px;line-height:1.55}.md table{border-collapse:collapse;width:100%;margin:.7em 0;font-size:.95em}.md td,.md th{border:.5px solid var(--dsw-border-l2);text-align:left;padding:5px 10px}.md th{background:var(--dsw-hover)}.md hr{border:none;border-top:.5px solid var(--dsw-border-l2);margin:1em 0}:root{--hl-comment:#828892;--hl-keyword:#a046c8;--hl-string:#1e825a;--hl-number:#be6414;--hl-func:#3764dc;--hl-type:#148296;--hl-attr:#8c5a1e;--hl-tag:#c8325a;--hl-regex:#c83c28;--hl-meta:#786e28;--hl-punct:var(--dsw-label-tertiary);--hl-variable:#1e64aa}body[data-ds-dark-theme],html[data-ds-dark-theme]{--hl-comment:#78808c;--hl-keyword:#c896ff;--hl-string:#96d2a5;--hl-number:#f0af6e;--hl-func:#8cb9ff;--hl-type:#64c8d2;--hl-attr:#dcc382;--hl-tag:#f08296;--hl-regex:#eb826e;--hl-meta:#c8be78;--hl-punct:var(--dsw-label-tertiary);--hl-variable:#96c8fa}.hljs{color:var(--dsw-label-secondary)}.hljs-comment,.hljs-quote{color:var(--hl-comment);font-style:italic}.hljs-doctag,.hljs-keyword,.hljs-literal,.hljs-section,.hljs-selector-tag,.hljs-strong{color:var(--hl-keyword)}.hljs-strong{font-weight:600}.hljs-addition,.hljs-bullet,.hljs-link,.hljs-string,.hljs-symbol{color:var(--hl-string)}.hljs-number,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-template-variable{color:var(--hl-number)}.hljs-code,.hljs-name,.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:var(--hl-func)}.hljs-built_in,.hljs-builtin-name,.hljs-class .hljs-title,.hljs-type{color:var(--hl-type)}.hljs-attr,.hljs-attribute,.hljs-property{color:var(--hl-attr)}.hljs-deletion,.hljs-selector-attr,.hljs-tag{color:var(--hl-tag)}.hljs-char.escape_,.hljs-regexp,.hljs-subst{color:var(--hl-regex)}.hljs-formula,.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:var(--hl-meta)}.hljs-operator,.hljs-params,.hljs-variable,.hljs-variable.language_{color:var(--hl-variable)}.hljs-punctuation{color:var(--hl-punct)}.hljs-emphasis{font-style:italic}.hljs-addition,.hljs-deletion{background:0 0}.msg-user-bubble{background:var(--dsw-input-major);max-width:85%;font-size:var(--piweb-chat-font-size);border-radius:18px;padding:10px 16px;line-height:1.65;display:inline-block}.msg-actions{opacity:.45;transition:opacity var(--ds-duration-fast) var(--ds-ease-in-out);align-items:center;gap:6px;margin-top:6px;display:flex}.msg-actions:hover{opacity:1}.msg-clock{font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-label-tertiary)}.msg-clock-start{padding-right:12px}:root{--pw-danger-soft:#ec13131f;--pw-shimmer-hi:#96beff}body[data-ds-dark-theme],html[data-ds-dark-theme]{--pw-danger-soft:#f25a5a29;--pw-shimmer-hi:#e1ecff}.pw-turn{padding:6px 0 2px}.pw-run,.pw-turn{flex-direction:column;gap:1px;display:flex}.pw-step{min-height:26px;animation:pw-in .16s var(--ds-ease-in-out,ease-out) both;align-items:flex-start;gap:10px;display:flex}@keyframes pw-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:none}}.pw-icon{width:16px;height:26px;color:var(--dsw-label-caption);flex:none;justify-content:center;align-items:center;display:inline-flex}.pw-step[data-tone=live] .pw-icon{color:var(--dsw-accent);animation:dot-breathe 1.4s ease-in-out infinite}.pw-step[data-tone=fail] .pw-icon{color:var(--dsw-danger)}.pw-step[data-tone=warn] .pw-icon{color:var(--dsw-warn)}.pw-step[data-kind=retry] .pw-icon{animation:pw-rotate 1.2s linear infinite}@keyframes pw-rotate{to{transform:rotate(1turn)}}.pw-pi{font-family:var(--font-mono);color:var(--dsw-accent);font-size:15px;font-weight:600;line-height:1;transform:translateY(-1px)}.pw-body{flex:1;min-width:0}.pw-line{min-height:26px;line-height:26px;font-size:var(--piweb-chat-font-m);color:var(--dsw-label-tertiary);align-items:center;gap:6px;display:flex}button.pw-line{text-align:left;cursor:pointer;width:100%;font:inherit;font-size:var(--piweb-chat-font-m);background:0 0;border:0;padding:0}button.pw-line:hover{color:var(--dsw-label-secondary)}.pw-verb{flex:none}.pw-verb[data-failed=true]{color:var(--dsw-danger)}.pw-arg{font-family:var(--font-mono);font-size:var(--piweb-chat-font-s);color:var(--dsw-label-secondary);flex:0 auto}.pw-arg,.pw-gist{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.pw-gist{flex:1}.pw-meta{font-size:var(--piweb-chat-font-t);white-space:nowrap}.pw-line .pw-chev,.pw-meta{color:var(--dsw-label-caption);flex:none}.pw-line .pw-chev{transition:transform .12s var(--ds-ease-in-out,ease)}.pw-line[aria-expanded=true] .pw-chev{transform:rotate(90deg)}.pw-summary strong{color:var(--dsw-label-secondary);font-weight:600}.pw-run[data-open]>.pw-step:not(:first-child){margin-left:4px}.pw-shimmer{background:linear-gradient(90deg,var(--dsw-accent) 0,var(--pw-shimmer-hi) 35%,var(--dsw-accent) 70%);color:#0000;background-size:220% 100%;-webkit-background-clip:text;background-clip:text;flex:none;animation:pw-shimmer 2.2s linear infinite}@keyframes pw-shimmer{0%{background-position:120% 0}to{background-position:-100% 0}}.pw-tail{font-size:var(--piweb-chat-font-s);color:var(--dsw-label-caption);word-break:break-word;-webkit-line-clamp:2;-webkit-box-orient:vertical;margin:-4px 0 5px;line-height:1.5;display:-webkit-box;overflow:hidden}.pw-result{font-family:var(--font-mono);font-size:var(--piweb-chat-font-t);color:var(--dsw-label-tertiary);align-items:flex-start;gap:6px;margin:-4px 0 5px;line-height:1.55;display:flex}.pw-result[role=button]{cursor:pointer}.pw-rail{text-align:center;width:14px;color:var(--dsw-label-caption);-webkit-user-select:none;user-select:none;flex:none}.pw-lines{white-space:pre-wrap;word-break:break-word;flex:1;min-width:0}.pw-lines>div{text-overflow:ellipsis;white-space:pre;overflow:hidden}.pw-lines .pw-head{color:var(--dsw-label-tertiary)}.pw-lines .pw-more{color:var(--dsw-label-caption)}.pw-result[role=button]:hover .pw-more{text-decoration:underline}.pw-result[data-failed=true],.pw-result[data-failed=true] .pw-head,.pw-result[data-failed=true] .pw-rail{color:var(--dsw-danger)}.pw-diff{max-height:420px;margin:-2px 0 6px 20px;padding:6px 8px}.pw-diff,.pw-output{background:var(--dsw-markdown-code-block,var(--dsw-hover));border-radius:10px;overflow:auto}.pw-output{max-height:360px;font-family:var(--font-mono);font-size:var(--piweb-chat-font-t);color:var(--dsw-label-secondary);white-space:pre-wrap;word-break:break-word;margin:0 0 6px 20px;padding:8px 12px;line-height:1.55}.pw-output.pw-think-full{color:var(--dsw-label-tertiary);font-family:inherit}.pw-output .cmdline{color:var(--dsw-label-tertiary);border-bottom:.5px solid var(--dsw-border-l2);white-space:pre-wrap;word-break:break-all;margin-bottom:6px;padding-bottom:6px}.pw-output .cmdline:before{content:"$ ";color:var(--dsw-label-caption)}button.pw-more{font:inherit;font-family:var(--font-mono);font-size:var(--piweb-chat-font-t);color:var(--dsw-label-caption);cursor:pointer;text-align:left;background:0 0;border:0;margin-top:4px;padding:0;display:block}button.pw-more:hover{color:var(--dsw-label-secondary);text-decoration:underline}.pw-narr{font-size:var(--piweb-chat-font-l);color:var(--dsw-label-primary);margin:7px 0 3px;line-height:1.65}.pw-turn>.pw-narr:first-child{margin-top:1px}.pw-narr[data-error=true]{color:var(--dsw-danger)}.pw-narr .md p{margin:0}.pw-narr .md p+p{margin-top:.5em}.pw-narr .md pre{margin:.5em 0}.pw-warn-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-warn);flex:1;overflow:hidden}.pw-step[data-kind=abort] .pw-line,.pw-step[data-kind=context] .pw-line{color:var(--dsw-label-caption);font-size:var(--piweb-chat-font-s)}body[data-pebrel-theme],html[data-pebrel-theme]{--dsw-bg-base:#fff;--dsw-bg-elevated:#fff;--dsw-input-major:#fff;--dsw-bubble:#64748b0d;--dsw-hover:#64748b21;--dsw-active:#64748b33;--dsw-label-primary:#334155;--dsw-label-secondary:#64748b;--dsw-label-tertiary:#94a3b8;--dsw-label-caption:#a3adbb;--dsw-border-l1:#33415514;--dsw-border-l2:#3341551f;--dsw-border-l3:#33415529;--dsw-border-l4:#3341553d;--dsw-warn:#d97706;--dsw-success:#548c9e;--dsw-danger:#b23a48;--dsw-selector:#64748b14;--dsw-layer-2:#fff;--dsw-layer-3:#f5f5f6;--dsw-btn-elevated:#fff;--dsw-btn-floating-hover:#64748b21;--dsw-module-platform:#f5f5f6;--dsw-border-strong:#64748b;--dsw-glass-popover:#fff;--dsw-glass-input:#fff;--dsw-glass-modal:#fff;--dsw-mask:#0f172a38;--dsw-elevation-stroke:var(--dsw-border-l2)}body[data-pebrel-theme=pebrel],body[data-pebrel-theme=silver-steel],html[data-pebrel-theme=pebrel],html[data-pebrel-theme=silver-steel]{--dsw-sidebar-fill:#f3f4f6;--dsw-accent:#495057;--dsw-accent-soft:#4950571f}body[data-pebrel-theme=limestone-coal],html[data-pebrel-theme=limestone-coal]{--dsw-sidebar-fill:#f0efeb;--dsw-layer-3:#f0efeb;--dsw-module-platform:#f0efeb;--dsw-accent:#58554c;--dsw-accent-soft:#58554c1f}body[data-pebrel-theme=linen-moss],html[data-pebrel-theme=linen-moss]{--dsw-sidebar-fill:#f2f2ec;--dsw-layer-3:#f2f2ec;--dsw-module-platform:#f2f2ec;--dsw-accent:#5f635f;--dsw-accent-soft:#5f635f1f}body[data-pebrel-theme=nord-paper],html[data-pebrel-theme=nord-paper]{--dsw-bg-base:#fcfbf9;--dsw-bg-elevated:#f5f4f0;--dsw-sidebar-fill:#f5f4f0;--dsw-input-major:#fcfbf9;--dsw-bubble:#8c8a8014;--dsw-hover:#ebeae5;--dsw-active:#2b5a382e;--dsw-label-primary:#1a1a1a;--dsw-label-secondary:#474646;--dsw-label-tertiary:#8c8a80;--dsw-label-caption:#a8a49a;--dsw-border-l1:#eceae4;--dsw-border-l2:#e0dfd5;--dsw-border-l3:#d5d3c9;--dsw-border-l4:#c1beb5;--dsw-accent:#2b5a38;--dsw-accent-soft:#2b5a382e;--dsw-warn:#a85a20;--dsw-success:#2b5a38;--dsw-danger:#a33a3a;--dsw-selector:#ebeae5;--dsw-layer-2:#fcfbf9;--dsw-layer-3:#f5f4f0;--dsw-btn-elevated:#fcfbf9;--dsw-btn-floating-hover:#ebeae5;--dsw-module-platform:#f0efe9;--dsw-border-strong:#8c8a80;--dsw-glass-popover:#fcfbf9;--dsw-glass-input:#fcfbf9;--dsw-glass-modal:#f5f4f0;--dsw-mask:#1a1a1a30}body[data-pebrel-theme][data-ds-dark-theme],html[data-pebrel-theme][data-ds-dark-theme]{--dsw-label-primary:#e2e8f0;--dsw-label-secondary:#b9c3cf;--dsw-label-tertiary:#94a3b8;--dsw-label-caption:#64748b;--dsw-bubble:#ffffff0d;--dsw-hover:#cbd5e124;--dsw-active:#cbd5e136;--dsw-border-l1:#cbd5e114;--dsw-border-l2:#cbd5e11f;--dsw-border-l3:#cbd5e129;--dsw-border-l4:#cbd5e13d;--dsw-warn:#f59e0b;--dsw-success:#7db2c2;--dsw-danger:#c44a58;--dsw-selector:#cbd5e114;--dsw-border-strong:#94a3b8;--dsw-mask:#02061780;--dsw-elevation-stroke:var(--dsw-border-l2)}body[data-pebrel-theme=silver-steel][data-ds-dark-theme],html[data-pebrel-theme=silver-steel][data-ds-dark-theme]{--dsw-bg-base:#16181e;--dsw-bg-elevated:#1e2129;--dsw-sidebar-fill:#16181e;--dsw-input-major:#1a1c24;--dsw-layer-2:#1e2129;--dsw-layer-3:#343a48;--dsw-btn-elevated:#1e2129;--dsw-btn-floating-hover:#cbd5e124;--dsw-module-platform:#1e2129;--dsw-glass-popover:#1e2129;--dsw-glass-input:#1a1c24;--dsw-glass-modal:#1e2129;--dsw-accent:#94a3b8;--dsw-accent-soft:#94a3b826}body[data-pebrel-theme=limestone-coal][data-ds-dark-theme],html[data-pebrel-theme=limestone-coal][data-ds-dark-theme]{--dsw-bg-base:#161616;--dsw-bg-elevated:#1e1e1e;--dsw-sidebar-fill:#161616;--dsw-input-major:#171717;--dsw-layer-2:#1e1e1e;--dsw-layer-3:#383836;--dsw-btn-elevated:#1e1e1e;--dsw-btn-floating-hover:#cbd5e124;--dsw-module-platform:#1e1e1e;--dsw-glass-popover:#1e1e1e;--dsw-glass-input:#171717;--dsw-glass-modal:#1e1e1e;--dsw-accent:#d4d4d4;--dsw-accent-soft:#d4d4d424;--dsw-warn:#ceb27e}body[data-pebrel-theme=linen-moss][data-ds-dark-theme],html[data-pebrel-theme=linen-moss][data-ds-dark-theme]{--dsw-bg-base:#191c19;--dsw-bg-elevated:#212521;--dsw-sidebar-fill:#191c19;--dsw-input-major:#1e211e;--dsw-layer-2:#212521;--dsw-layer-3:#363d36;--dsw-btn-elevated:#212521;--dsw-btn-floating-hover:#cbd5e124;--dsw-module-platform:#212521;--dsw-glass-popover:#212521;--dsw-glass-input:#1e211e;--dsw-glass-modal:#212521;--dsw-accent:#a3b3a3;--dsw-accent-soft:#a3b3a326}body[data-pebrel-theme=nord-paper][data-ds-dark-theme],html[data-pebrel-theme=nord-paper][data-ds-dark-theme]{--dsw-bg-base:#2e3440;--dsw-bg-elevated:#3b4252;--dsw-sidebar-fill:#2e3440;--dsw-input-major:#3b4252;--dsw-bubble:#3b4252;--dsw-hover:#ffffff0f;--dsw-active:#88c0d02e;--dsw-label-primary:#e5e9f0;--dsw-label-secondary:#c0c7d3;--dsw-label-tertiary:#7b8294;--dsw-label-caption:#6f7688;--dsw-border-l1:#ffffff0d;--dsw-border-l2:#ffffff0f;--dsw-border-l3:#434c5e;--dsw-border-l4:#4c566a;--dsw-accent:#88c0d0;--dsw-accent-soft:#88c0d02e;--dsw-warn:#ebcb8b;--dsw-success:#a3be8c;--dsw-danger:#bf616a;--dsw-selector:#ffffff0f;--dsw-layer-2:#3b4252;--dsw-layer-3:#434c5e;--dsw-btn-elevated:#3b4252;--dsw-btn-floating-hover:#434c5e;--dsw-module-platform:#3b4252;--dsw-border-strong:#7b8294;--dsw-glass-popover:#3b4252;--dsw-glass-input:#3b4252;--dsw-glass-modal:#3b4252;--dsw-mask:#0206175c}body[data-pebrel-theme=pebrel][data-ds-dark-theme],html[data-pebrel-theme=pebrel][data-ds-dark-theme]{--dsw-bg-base:#222630;--dsw-bg-elevated:#2b303b;--dsw-sidebar-fill:#222630;--dsw-input-major:#0f111a;--dsw-layer-2:#2b303b;--dsw-layer-3:#414858;--dsw-btn-elevated:#2b303b;--dsw-btn-floating-hover:#cbd5e124;--dsw-module-platform:#2b303b;--dsw-glass-popover:#2b303b;--dsw-glass-input:#0f111a;--dsw-glass-modal:#2b303b;--dsw-accent:#52a8ff;--dsw-accent-soft:#52a8ff26}@keyframes piweb-spin-kf{to{transform:rotate(1turn)}}.piweb-spin{animation:piweb-spin-kf .8s linear infinite}@keyframes piweb-pulse-kf{0%,to{opacity:1}50%{opacity:.3}}.piweb-pulse{animation:piweb-pulse-kf 1.1s ease-in-out infinite}@keyframes stream-cursor-kf{0%,49%{opacity:1}50%,to{opacity:0}}.stream-cursor{vertical-align:-.15em;background:var(--dsw-accent);width:2px;height:1em;margin-left:2px;animation:stream-cursor-kf 1s step-end infinite;display:inline-block}.pw-seg{background:var(--dsw-hover);border-radius:8px;flex:none;gap:1px;padding:2px;display:inline-flex}.pw-seg>button{height:20px;color:var(--dsw-label-tertiary);white-space:nowrap;transition:background var(--ds-duration-fast) var(--ds-ease-in-out),color var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:6px;padding:0 8px;font-size:11.5px;line-height:20px}.pw-seg>button[data-active=true]{background:var(--dsw-bg-elevated);color:var(--dsw-label-primary);box-shadow:0 0 0 .5px var(--dsw-border-l2),0 1px 2px #0000000f}.pw-chip{border:.5px solid var(--dsw-border-l3);height:20px;color:var(--dsw-label-tertiary);white-space:nowrap;transition:background var(--ds-duration-fast) var(--ds-ease-in-out),color var(--ds-duration-fast) var(--ds-ease-in-out),border-color var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:999px;flex:none;padding:0 8px;font-size:11px;line-height:19px}.pw-chip:hover{background:var(--dsw-hover);color:var(--dsw-label-primary)}.pw-chip[data-active=true]{background:var(--dsw-accent-soft);color:var(--dsw-accent);border-color:#0000}.pw-chip:disabled{opacity:.4;pointer-events:none}.pw-project{background:var(--dsw-sidebar-fill);border-right:.5px solid var(--dsw-border-l2)}.pw-project input,.pw-viewer input{color:var(--dsw-label-primary);outline:none}.pw-tree{-webkit-user-select:none;user-select:none;font-size:12.5px;line-height:22px}.pw-tree-row{cursor:pointer;white-space:nowrap;height:22px;color:var(--dsw-label-primary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:6px;align-items:center;gap:4px;padding-right:8px;display:flex;position:absolute;left:0;right:0}.pw-tree-row:hover{background:var(--dsw-hover)}.pw-tree-row[data-focused],.pw-tree-row[data-selected]{background:var(--dsw-active)}.pw-tree:focus-visible .pw-tree-row[data-focused]{box-shadow:inset 0 0 0 1px var(--dsw-accent)}.pw-tree-chevron,.pw-tree-row[data-lazy]>.pw-tree-name{color:var(--dsw-label-caption)}.pw-tree-chevron{width:12px;flex:none;justify-content:center;align-items:center;display:inline-flex}.pw-tree-icon{object-fit:contain;flex:none;width:16px;height:16px}.pw-tree-row>.pw-tree-icon{filter:saturate(.78);transition:filter var(--ds-duration-fast) var(--ds-ease-in-out),opacity var(--ds-duration-fast) var(--ds-ease-in-out)}.pw-tree-row>.pw-tree-icon[data-kind=dir]{opacity:.88;transform:translateY(-.25px)scale(.87)}.pw-tree-row>.pw-tree-icon[data-kind=file]{opacity:.93;transform:scale(.91)}.pw-tree-row:hover>.pw-tree-icon{filter:saturate(.95);opacity:1}.pw-viewer .pw-tree-icon{filter:saturate(.82);opacity:.92;transform:scale(.9)}.pw-tree-name{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.pw-tree-from{color:var(--dsw-label-caption);margin-left:6px;font-size:11px}.pw-tree-stat{font-variant-numeric:tabular-nums;color:var(--dsw-label-caption);flex:none;gap:4px;font-size:10.5px;display:inline-flex}.pw-tree-badge{text-align:center;font-variant-numeric:tabular-nums;border:.5px solid;border-radius:4px;flex:none;min-width:14px;height:14px;padding:0 3px;font-size:10px;font-weight:600;line-height:13px}.pw-tree-actions{flex:none;align-items:center;gap:1px;display:none}.pw-tree-row:hover .pw-tree-actions{display:inline-flex}.pw-tree-row:hover .pw-tree-stat{display:none}.pw-tree-pulse{background:var(--dsw-accent);border-radius:999px;flex:none;width:6px;height:6px;animation:dot-breathe 1s ease-in-out infinite}.pw-tree-row[data-pending]>.pw-tree-name{color:var(--dsw-accent)}@keyframes pw-tree-in{0%{opacity:0;transform:translate(-6px)}to{opacity:1;transform:none}}@keyframes pw-tree-glow-add{0%{background:#22c55e47}to{background:0 0}}@keyframes pw-tree-glow-mod{0%{background:#f59e0b42}to{background:0 0}}@keyframes pw-tree-glow-del{0%{background:#ec13133d}to{background:0 0}}.pw-tree-row[data-fresh]{animation:pw-tree-in .25s var(--ds-ease-in-out) both,pw-tree-glow-mod 2.2s ease-out both}.pw-tree-row[data-fresh=A]{animation-name:pw-tree-in,pw-tree-glow-add}.pw-tree-row[data-fresh=D]{animation-name:pw-tree-in,pw-tree-glow-del}.pw-timeline{flex:none}.pw-viewer{width:80vw;height:80vh;box-shadow:var(--dsw-elevation-prominent);animation:pw-in .16s var(--ds-ease-in-out) both;transition:width var(--ds-duration-normal) var(--ds-ease-in-out),height var(--ds-duration-normal) var(--ds-ease-in-out),border-radius var(--ds-duration-normal) var(--ds-ease-in-out);border-radius:20px}.pw-viewer[data-fullscreen]{border-radius:0;width:100vw;height:100vh}.pw-viewer-tabs{flex:none;height:40px;padding:0 8px 0 10px}.pw-viewer-tabs,.pw-viewer-tabstrip{align-items:center;gap:2px;display:flex}.pw-viewer-tabstrip{scrollbar-width:none;min-width:0;overflow-x:auto}.pw-viewer-tabstrip::-webkit-scrollbar{display:none}.pw-viewer-tab{max-width:200px;height:28px;color:var(--dsw-label-tertiary);transition:background var(--ds-duration-fast) var(--ds-ease-in-out),color var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:8px;flex:none;align-items:center;gap:6px;padding:0 8px;font-size:12.5px;display:inline-flex}.pw-viewer-tab:hover{background:var(--dsw-hover);color:var(--dsw-label-primary)}.pw-viewer-tab[data-active=true]{background:var(--dsw-active);color:var(--dsw-label-primary)}.pw-viewer-tab-main{min-width:0;color:inherit;align-items:center;gap:6px;display:inline-flex}.pw-viewer-tab-close{width:16px;height:16px;color:var(--dsw-label-caption);opacity:0;transition:opacity var(--ds-duration-fast) var(--ds-ease-in-out);border-radius:4px;justify-content:center;align-items:center;margin-right:-4px;display:inline-flex}.pw-viewer-tab-close:focus-visible,.pw-viewer-tab:hover .pw-viewer-tab-close,.pw-viewer-tab[data-active=true] .pw-viewer-tab-close{opacity:1}.pw-viewer-tab-close:hover{background:var(--dsw-hover);color:var(--dsw-label-primary)}.pw-viewer-toolbar{flex:none;gap:6px;height:36px;padding:0 12px}.pw-crumb,.pw-viewer-toolbar{align-items:center;display:flex}.pw-crumb{color:var(--dsw-label-tertiary);white-space:nowrap;flex-wrap:nowrap;font-size:12.5px;overflow:hidden}.pw-crumb button:hover{color:var(--dsw-accent)}.pw-crumb-sep{color:var(--dsw-label-caption);margin:0 4px}.pw-viewer-notice{color:var(--dsw-label-secondary);background:var(--dsw-hover);border-bottom:.5px solid var(--dsw-border-l1);flex:none;padding:4px 14px;font-size:11.5px}.pw-viewer-notice[data-kind=deleted]{color:var(--dsw-danger);background:#ec131314}.pw-viewer-notice[data-kind=warn]{color:var(--dsw-warn)}.pw-viewer .md pre{overflow-x:auto}.pw-quick{z-index:5;background:#00000014;justify-content:center;align-items:flex-start;padding-top:56px;display:flex;position:absolute;inset:0}.pw-quick-box{border-radius:14px;width:min(560px,90%);padding:4px}.pw-quick-list{max-height:50vh;padding:2px;overflow-y:auto}.pw-quick-item{width:100%;height:30px;color:var(--dsw-label-primary);text-align:left;border-radius:8px;align-items:center;gap:8px;padding:0 8px;font-size:12.5px;display:flex}.pw-quick-item[data-active=true]{background:var(--dsw-active)}.pw-fdiff{font-family:var(--font-mono);font-size:12px;line-height:20px}.pw-fdiff-row{white-space:pre;height:20px;color:var(--dsw-label-secondary);align-items:stretch;display:flex;position:absolute;left:0;right:0}.pw-fdiff-row[data-kind=add]{box-shadow:inset 2px 0 0 var(--dsw-success);background:#22c55e24}.pw-fdiff-row[data-kind=del]{box-shadow:inset 2px 0 0 var(--dsw-danger);background:#ec13131f}.pw-fdiff-gutter{color:var(--dsw-label-caption);-webkit-user-select:none;user-select:none;font-variant-numeric:tabular-nums;background:inherit;flex:none;gap:1ch;padding:0 8px 0 10px;display:inline-flex;position:sticky;left:0}.pw-fdiff-gutter>span{display:inline-block}.pw-fdiff-sign{-webkit-user-select:none;user-select:none;width:1.5ch;color:var(--dsw-label-caption);flex:none}.pw-fdiff-row[data-kind=add] .pw-fdiff-sign{color:var(--dsw-success)}.pw-fdiff-row[data-kind=del] .pw-fdiff-sign{color:var(--dsw-danger)}.pw-fdiff-text{background:0 0;flex:1;padding-right:24px}.pw-fdiff-fold{height:20px;color:var(--dsw-label-caption);background:var(--dsw-hover);text-align:center;letter-spacing:.02em;font-size:11px;position:absolute;left:0;right:0}.pw-fdiff-fold:hover{color:var(--dsw-accent)}.pw-fdiff-ticks{pointer-events:none;width:8px;position:absolute;top:0;bottom:0;right:2px}.pw-fdiff-tick{pointer-events:auto;cursor:pointer;background:var(--dsw-success);opacity:.6;border-radius:1px;width:8px;height:3px;position:absolute;right:0}.pw-fdiff-tick[data-kind=del]{background:var(--dsw-danger)}.pw-fdiff-tick[data-current]{opacity:1;box-shadow:0 0 0 1px var(--dsw-label-tertiary)}@media (max-width:840px){.pw-viewer{border-radius:0;width:100vw;height:100vh}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(1turn)}}
|
package/README.md
CHANGED
|
@@ -66,9 +66,10 @@ npm install -g @rexvane/piweb@latest
|
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
npm replaces the installed version in place, so there is nothing to uninstall first. This is
|
|
69
|
-
the same command the in-app "Check for updates" button runs
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
the same command the in-app "Check for updates" button runs. `latest` is npm's default tag,
|
|
70
|
+
so `npm install -g @rexvane/piweb` behaves identically; if your registry is a mirror that
|
|
71
|
+
syncs with a delay, add `--registry=https://registry.npmjs.org` to get a release the moment
|
|
72
|
+
it is published. Restart `piweb` afterwards to serve the new version.
|
|
72
73
|
|
|
73
74
|
Or install a specific version: `npm install -g @rexvane/piweb@0.3.4`.
|
|
74
75
|
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
setAlias,
|
|
12
12
|
} from "@/lib/workspace-store";
|
|
13
13
|
import { encodeSessionId } from "@/lib/pi";
|
|
14
|
+
import { disposeSessionPath } from "@/lib/agent-manager";
|
|
14
15
|
import { BoundaryError, resolveSessionPath, resolveWorkspacePath } from "@/lib/path-security";
|
|
15
16
|
|
|
16
17
|
export const dynamic = "force-dynamic";
|
|
@@ -53,6 +54,9 @@ export async function POST(req: Request) {
|
|
|
53
54
|
if (body.action === "archiveSession") {
|
|
54
55
|
if (!body.path) return NextResponse.json({ success: false, error: "missing path" }, { status: 400 });
|
|
55
56
|
const sessionPath = await resolveSessionPath(encodeSessionId(body.path), { allowPending: true });
|
|
57
|
+
// 归档即收工:会话随即从工作区消失,不能让它继续在后台跑(看不见却仍消耗 token)。
|
|
58
|
+
// 只影响这一个会话,其它运行中的会话不受影响。
|
|
59
|
+
await disposeSessionPath(sessionPath);
|
|
56
60
|
const archivedSessions = await archiveSession(sessionPath);
|
|
57
61
|
return NextResponse.json({ success: true, data: { archivedSessions } });
|
|
58
62
|
}
|
|
@@ -14,8 +14,11 @@ import { SessionSidebar } from "@/components/SessionSidebar";
|
|
|
14
14
|
|
|
15
15
|
// 首屏不需要的重组件按需加载(设置面板含供应商配置与代码高亮,轨迹/文件只在打开时才用)
|
|
16
16
|
const SettingsPanel = dynamic(() => import("@/components/SettingsPanel").then((m) => m.SettingsPanel), { ssr: false });
|
|
17
|
-
const TrajectoryView = dynamic(() => import("@/components/TrajectoryView").then((m) => m.TrajectoryView), { ssr: false });
|
|
18
17
|
const TrajInspector = dynamic(() => import("@/components/TrajectoryView").then((m) => m.TrajInspector), { ssr: false });
|
|
18
|
+
|
|
19
|
+
/** 会话/工作区路径比较(Windows 大小写与分隔符差异不该影响判断) */
|
|
20
|
+
const samePath = (a: string, b: string): boolean =>
|
|
21
|
+
a.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase() === b.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
19
22
|
const ProjectPanel = dynamic(() => import("@/components/ProjectPanel").then((m) => m.ProjectPanel), { ssr: false });
|
|
20
23
|
const FileViewer = dynamic(() => import("@/components/FileViewer").then((m) => m.FileViewer), { ssr: false });
|
|
21
24
|
const GitPanel = dynamic(() => import("@/components/GitPanel").then((m) => m.GitPanel), { ssr: false });
|
|
@@ -132,7 +135,6 @@ export function AppShell() {
|
|
|
132
135
|
const saveHeroDraft = useCallback((update: ChatDraftUpdate) => updateDraft(heroDraftKey, update), [heroDraftKey, updateDraft]);
|
|
133
136
|
const [dragging, setDragging] = useState<"sidebar" | "details" | "project" | null>(null);
|
|
134
137
|
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
135
|
-
const [tab, setTab] = useState<"chat" | "traj">("chat");
|
|
136
138
|
const [selected, setSelected] = useState<TrajEntry | null>(null);
|
|
137
139
|
const [gitDetailsOpen, setGitDetailsOpen] = useState(false);
|
|
138
140
|
const [heroCwd, setHeroCwd] = useState("");
|
|
@@ -342,17 +344,49 @@ export function AppShell() {
|
|
|
342
344
|
[currentPath, currentId, sendCommand, openSession, resync],
|
|
343
345
|
);
|
|
344
346
|
|
|
347
|
+
/**
|
|
348
|
+
* 原地编辑用户消息后重新发送:服务端在同一会话文件内回到该消息(navigateTree),
|
|
349
|
+
* 再用新文本重新提问——被编辑消息之后的分支作废,模型从这里重新回答。
|
|
350
|
+
* 旧的插话(steering)撤回/编辑/发送不变。
|
|
351
|
+
*/
|
|
352
|
+
const doEditMessage = useCallback(
|
|
353
|
+
async (entryId: string, text: string) => {
|
|
354
|
+
if (!currentId) return;
|
|
355
|
+
if (isStreaming) {
|
|
356
|
+
setError(t.editWhileRunning);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const moved = await sendCommand({ cmd: "navigate", entryId });
|
|
360
|
+
if (!moved?.success || moved.data?.cancelled) {
|
|
361
|
+
setError(moved?.error ?? t.editMessageFailed);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
// 被编辑消息之后的尾部要立刻从视图里消失,再发新提问
|
|
365
|
+
resync();
|
|
366
|
+
const sent = await sendCommand({ cmd: "prompt", text });
|
|
367
|
+
if (!sent?.success) setError(sent?.error ?? t.editMessageFailed);
|
|
368
|
+
},
|
|
369
|
+
[currentId, isStreaming, resync, sendCommand, setError, t.editMessageFailed, t.editWhileRunning],
|
|
370
|
+
);
|
|
371
|
+
|
|
345
372
|
const doArchiveSession = useCallback(
|
|
346
373
|
async (path: string) => {
|
|
347
374
|
try {
|
|
348
|
-
// dsh 合同:归档不关会话——当前会话被归档后保持打开且在主列表可见
|
|
349
|
-
// (usePiWeb 的可见性规则对 currentPath 豁免),继续可聊。
|
|
350
375
|
await archiveSession(path);
|
|
351
376
|
} catch {
|
|
352
377
|
// usePiWeb exposes the request failure in the shared error banner.
|
|
353
378
|
}
|
|
379
|
+
// 归档后会话立刻离开工作区列表(服务端同时让它收工)。正在看的会话被归档时
|
|
380
|
+
// 必须切走:先取同一工作区的另一个可见会话,没有就回到空态(工作区仍留在侧栏)。
|
|
381
|
+
if (!currentPath || !samePath(path, currentPath)) return;
|
|
382
|
+
const cwd = sessions.find((s) => samePath(s.path, currentPath))?.cwd;
|
|
383
|
+
const next =
|
|
384
|
+
sessions.find((s) => !samePath(s.path, path) && cwd && samePath(s.cwd, cwd)) ??
|
|
385
|
+
sessions.find((s) => !samePath(s.path, path));
|
|
386
|
+
if (next) openSession(next.path);
|
|
387
|
+
else closeSession();
|
|
354
388
|
},
|
|
355
|
-
[archiveSession],
|
|
389
|
+
[archiveSession, closeSession, currentPath, openSession, sessions],
|
|
356
390
|
);
|
|
357
391
|
const doUnarchiveSession = useCallback(
|
|
358
392
|
async (path: string) => {
|
|
@@ -444,7 +478,6 @@ export function AppShell() {
|
|
|
444
478
|
if (name === "compact") void sendCommand({ cmd: "compact", instructions: args || undefined });
|
|
445
479
|
else if (name === "export" && currentId) window.open(`/api/sessions/${currentId}/export?format=jsonl`, "_blank");
|
|
446
480
|
else if (name === "new") {
|
|
447
|
-
setTab("chat");
|
|
448
481
|
closeSession();
|
|
449
482
|
} else if (name === "fork" && currentId) {
|
|
450
483
|
void sendCommand({ cmd: "fork" }).then((r) => {
|
|
@@ -646,14 +679,12 @@ export function AppShell() {
|
|
|
646
679
|
}}
|
|
647
680
|
onNew={() => {
|
|
648
681
|
// 对齐 dsh startSession:新会话默认落在当前会话的工作区(无则取最近工作区)
|
|
649
|
-
setTab("chat");
|
|
650
682
|
const cur = sessions.find((s) => s.path === currentPath)?.cwd;
|
|
651
683
|
setHeroCwd(cur || heroCwd || knownCwds[0] || "");
|
|
652
684
|
closeSession();
|
|
653
685
|
}}
|
|
654
686
|
onNewInWorkspace={(cwd) => {
|
|
655
687
|
// dsh 惰性新建:只预选工作区进草稿态,发送第一条消息才真正创建会话
|
|
656
|
-
setTab("chat");
|
|
657
688
|
setHeroCwd(cwd);
|
|
658
689
|
closeSession();
|
|
659
690
|
}}
|
|
@@ -757,26 +788,11 @@ export function AppShell() {
|
|
|
757
788
|
)}
|
|
758
789
|
</div>
|
|
759
790
|
)}
|
|
760
|
-
|
|
761
|
-
<button className="tab-underline" data-active={tab === "chat"} onClick={() => setTab("chat")}>
|
|
762
|
-
{t.tabChat}
|
|
763
|
-
</button>
|
|
764
|
-
<button
|
|
765
|
-
className="tab-underline"
|
|
766
|
-
data-active={tab === "traj"}
|
|
767
|
-
onClick={() => {
|
|
768
|
-
setTab("traj");
|
|
769
|
-
void sendCommand({ cmd: "prepare" });
|
|
770
|
-
}}
|
|
771
|
-
>
|
|
772
|
-
{t.tabTrajectory}
|
|
773
|
-
</button>
|
|
774
|
-
</div>
|
|
791
|
+
{/* 轨迹页签已移除:轨迹按需查看(对话里的工具行 → 轨迹详情),不再占用主区域 */}
|
|
775
792
|
</div>
|
|
776
793
|
|
|
777
|
-
{/*
|
|
778
|
-
{
|
|
779
|
-
!snapshot ? (
|
|
794
|
+
{/* 内容:只有对话区,轨迹按需在详情栏查看 */}
|
|
795
|
+
{!snapshot ? (
|
|
780
796
|
// 切换会话时快照未到:居中加载指示,避免空白/占位符闪现
|
|
781
797
|
<div className="flex min-h-0 flex-1 items-center justify-center">
|
|
782
798
|
<span
|
|
@@ -813,6 +829,7 @@ export function AppShell() {
|
|
|
813
829
|
onOpenTrajectory={openToolTrajectory}
|
|
814
830
|
onOpenFile={openInEditor}
|
|
815
831
|
onFork={handleFork}
|
|
832
|
+
onEditMessage={doEditMessage}
|
|
816
833
|
/>
|
|
817
834
|
<div className="px-4 pb-3 pt-2">
|
|
818
835
|
<div className="mx-auto w-full" style={{ maxWidth: "var(--dsh-composer-card-max-width)" }}>
|
|
@@ -861,22 +878,11 @@ export function AppShell() {
|
|
|
861
878
|
{Object.entries(state.extensionStatuses).map(([k, v]) => <span key={k}>{v}</span>)}
|
|
862
879
|
</div>
|
|
863
880
|
)}
|
|
864
|
-
|
|
881
|
+
</div>
|
|
865
882
|
</div>
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
<div className="min-h-0 flex-1">
|
|
870
|
-
<TrajectoryView
|
|
871
|
-
entries={snapshot?.trajectory ?? []}
|
|
872
|
-
selected={selected}
|
|
873
|
-
onSelect={(entry) => {
|
|
874
|
-
setSelected(entry);
|
|
875
|
-
setGitDetailsOpen(false);
|
|
876
|
-
}}
|
|
877
|
-
/>
|
|
878
|
-
</div>
|
|
879
|
-
)}
|
|
883
|
+
</div>
|
|
884
|
+
)
|
|
885
|
+
}
|
|
880
886
|
</>
|
|
881
887
|
)}
|
|
882
888
|
</div>
|
|
@@ -193,6 +193,7 @@ function TurnMetaPill({ durationMs, usage, t }: { durationMs: number; usage?: Tr
|
|
|
193
193
|
function MessageActions({
|
|
194
194
|
text,
|
|
195
195
|
onFork,
|
|
196
|
+
onEdit,
|
|
196
197
|
durationMs,
|
|
197
198
|
usage,
|
|
198
199
|
time,
|
|
@@ -200,6 +201,8 @@ function MessageActions({
|
|
|
200
201
|
}: {
|
|
201
202
|
text: string;
|
|
202
203
|
onFork?: () => void | Promise<unknown>;
|
|
204
|
+
/** 用户消息:原地编辑后重新发送(模型从这条消息重新回答) */
|
|
205
|
+
onEdit?: () => void;
|
|
203
206
|
/** 回合墙钟耗时(最终回答时间戳 − 回合首条用户消息时间戳),dsh runMs 的对应物 */
|
|
204
207
|
durationMs?: number;
|
|
205
208
|
usage?: TrajTokens & { cost?: { total?: number } };
|
|
@@ -235,6 +238,11 @@ function MessageActions({
|
|
|
235
238
|
{copied ? <IconCheckOutline14 size={14} /> : <IconCopyOutline16 size={14} />}
|
|
236
239
|
</button>
|
|
237
240
|
{copyFailed && <span role="alert" style={{ fontSize: 11, color: "var(--dsw-danger)" }}>{t.copyFailed}</span>}
|
|
241
|
+
{onEdit && (
|
|
242
|
+
<button className="icon-btn" title={t.editMessage} aria-label={t.editMessage} onClick={onEdit}>
|
|
243
|
+
<IconEditOutline16 size={14} />
|
|
244
|
+
</button>
|
|
245
|
+
)}
|
|
238
246
|
{onFork && (
|
|
239
247
|
<button
|
|
240
248
|
className="icon-btn"
|
|
@@ -456,7 +464,8 @@ const ToolStep = memo(function ToolStep({
|
|
|
456
464
|
const output = state?.result ?? state?.partialResult ?? "";
|
|
457
465
|
const isWriteTool = name.toLowerCase() === "write";
|
|
458
466
|
const diff = useMemo(() => {
|
|
459
|
-
|
|
467
|
+
// 失败的编辑同样渲染 diff:出错信息单独展示,不因为失败就把红绿藏掉
|
|
468
|
+
if (kind !== "write" || !state || state.state !== "done") return null;
|
|
460
469
|
const parsed = parseUnifiedDiff(state.patch || output);
|
|
461
470
|
if (parsed) return parsed;
|
|
462
471
|
// pi 的 write 工具不带 patch:新文件按新增行展示(全绿 +),与 edit 的 diff 口径一致
|
|
@@ -483,11 +492,13 @@ const ToolStep = memo(function ToolStep({
|
|
|
483
492
|
// 删除文件的命令:完成后给红色标记(内容级 diff 不强求)
|
|
484
493
|
const deletedTarget = kind === "cmd" && !running && !failed && isDeleteCommand(fullCommand) ? deleteTargetOf(fullCommand) : "";
|
|
485
494
|
const preview = state ? toolPreview(kind, state, tt, cwd) : { lines: [], hidden: 0 };
|
|
486
|
-
const hasDiff = Boolean(diff && diff.length)
|
|
495
|
+
const hasDiff = Boolean(diff && diff.length);
|
|
487
496
|
const hasOutput = Boolean(output || state?.encodingLoss);
|
|
488
497
|
const canExpand = hasOutput && !hasDiff && !running;
|
|
489
498
|
const duration = state?.startedAt && state?.endedAt ? state.endedAt - state.startedAt : 0;
|
|
490
499
|
const showPreview = Boolean(preview.head) || preview.lines.length > 0;
|
|
500
|
+
// 失败且有 diff 时,出错信息要完整可见(此时 diff 占用了展开位)
|
|
501
|
+
const showFullOutput = hasOutput && (open && canExpand || (failed && hasDiff));
|
|
491
502
|
const diffLines = hasDiff && diff ? (open ? diff : diff.slice(0, DIFF_PREVIEW)) : null;
|
|
492
503
|
const diffHidden = diff && diffLines && !open ? diff.length - diffLines.length : 0;
|
|
493
504
|
const diffStat = hasDiff && diff
|
|
@@ -586,7 +597,7 @@ const ToolStep = memo(function ToolStep({
|
|
|
586
597
|
)}
|
|
587
598
|
</div>
|
|
588
599
|
)}
|
|
589
|
-
{
|
|
600
|
+
{showFullOutput && (
|
|
590
601
|
<div className="pw-output">
|
|
591
602
|
{fullCommand && <div className="cmdline">{fullCommand}</div>}
|
|
592
603
|
{state?.encodingLoss && <p className="mb-2 whitespace-normal" style={{ color: "var(--dsw-warn)" }}>{t.encodingLossWarning}</p>}
|
|
@@ -892,12 +903,56 @@ const FinalAnswer = memo(function FinalAnswer({
|
|
|
892
903
|
});
|
|
893
904
|
|
|
894
905
|
/** 行级 memo:流式期间只有最后一条消息变化,历史行全部跳过重渲染 */
|
|
895
|
-
const UserMessage = memo(function UserMessage({ message }: { message: WebMessage }) {
|
|
906
|
+
const UserMessage = memo(function UserMessage({ message, onEditMessage }: { message: WebMessage; onEditMessage?: (entryId: string, text: string) => void }) {
|
|
907
|
+
const { t } = useI18n();
|
|
896
908
|
const text = message.content
|
|
897
909
|
.filter((c): c is { type: "text"; text: string } => c.type === "text")
|
|
898
910
|
.map((c) => c.text)
|
|
899
911
|
.join("\n");
|
|
900
912
|
const images = message.content.filter((content) => content.type === "image");
|
|
913
|
+
const [editing, setEditing] = useState(false);
|
|
914
|
+
const [draft, setDraft] = useState(text);
|
|
915
|
+
const canEdit = Boolean(onEditMessage && message.id && text);
|
|
916
|
+
if (editing) {
|
|
917
|
+
return (
|
|
918
|
+
<div className="group mt-7 flex w-full flex-col items-end first:mt-0" data-role="user">
|
|
919
|
+
<div className="msg-user-bubble w-full">
|
|
920
|
+
<textarea
|
|
921
|
+
className="w-full resize-none bg-transparent outline-none"
|
|
922
|
+
style={{ minHeight: 68, font: "inherit", color: "inherit" }}
|
|
923
|
+
value={draft}
|
|
924
|
+
aria-label={t.editMessage}
|
|
925
|
+
autoFocus
|
|
926
|
+
onChange={(e) => setDraft(e.target.value)}
|
|
927
|
+
onKeyDown={(e) => {
|
|
928
|
+
if (e.key === "Escape") { e.preventDefault(); setEditing(false); }
|
|
929
|
+
if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); submit(); }
|
|
930
|
+
}}
|
|
931
|
+
/>
|
|
932
|
+
</div>
|
|
933
|
+
<div className="mt-1.5 flex items-center gap-2">
|
|
934
|
+
<button type="button" className="btn-outline" style={{ height: 26, padding: "0 10px", fontSize: 12 }} onClick={() => setEditing(false)}>
|
|
935
|
+
{t.cancel}
|
|
936
|
+
</button>
|
|
937
|
+
<button
|
|
938
|
+
type="button"
|
|
939
|
+
className="btn-primary-white"
|
|
940
|
+
style={{ height: 26, padding: "0 12px", fontSize: 12 }}
|
|
941
|
+
disabled={!draft.trim() || draft.trim() === text.trim()}
|
|
942
|
+
onClick={submit}
|
|
943
|
+
>
|
|
944
|
+
{t.sendEdit}
|
|
945
|
+
</button>
|
|
946
|
+
</div>
|
|
947
|
+
</div>
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
function submit() {
|
|
951
|
+
const next = draft.trim();
|
|
952
|
+
if (!next || next === text.trim() || !onEditMessage || !message.id) return;
|
|
953
|
+
setEditing(false);
|
|
954
|
+
onEditMessage(message.id, next);
|
|
955
|
+
}
|
|
901
956
|
return (
|
|
902
957
|
// 回合边界:用户消息前留 28px(比回合内 8px 大得多),长对话里一眼找到“这一轮从哪开始”
|
|
903
958
|
<div className="group mt-7 flex w-full flex-col items-end first:mt-0" data-role="user">
|
|
@@ -916,8 +971,15 @@ const UserMessage = memo(function UserMessage({ message }: { message: WebMessage
|
|
|
916
971
|
)}
|
|
917
972
|
{/* 用户消息也走 Markdown:贴进来的代码块/列表不再是一坨纯文本 */}
|
|
918
973
|
{text && <div className="msg-user-bubble"><Markdown text={text} /></div>}
|
|
919
|
-
{/*
|
|
920
|
-
{text &&
|
|
974
|
+
{/* 用户消息:复制 + 原地编辑重发(编辑后模型从这条消息重新回答);时钟在图标左侧(dsh clock=start) */}
|
|
975
|
+
{text && (
|
|
976
|
+
<MessageActions
|
|
977
|
+
text={text}
|
|
978
|
+
clockStart={message.timestamp !== undefined}
|
|
979
|
+
time={message.timestamp}
|
|
980
|
+
onEdit={canEdit ? () => { setDraft(text); setEditing(true); } : undefined}
|
|
981
|
+
/>
|
|
982
|
+
)}
|
|
921
983
|
</div>
|
|
922
984
|
);
|
|
923
985
|
});
|
|
@@ -952,7 +1014,7 @@ function isFinalAnswer(m: WebMessage): boolean {
|
|
|
952
1014
|
return hasText && !hasTool && sr !== "toolUse" && sr !== "pending" && sr !== "error" && sr !== "aborted";
|
|
953
1015
|
}
|
|
954
1016
|
|
|
955
|
-
function
|
|
1017
|
+
function TurnBlockView({
|
|
956
1018
|
turn,
|
|
957
1019
|
messages,
|
|
958
1020
|
tools,
|
|
@@ -967,6 +1029,7 @@ function TurnBlock({
|
|
|
967
1029
|
outputTokens,
|
|
968
1030
|
workingMessage,
|
|
969
1031
|
onFork,
|
|
1032
|
+
onEditMessage,
|
|
970
1033
|
onInspectTool,
|
|
971
1034
|
onOpenFile,
|
|
972
1035
|
}: {
|
|
@@ -984,6 +1047,8 @@ function TurnBlock({
|
|
|
984
1047
|
outputTokens?: number;
|
|
985
1048
|
workingMessage?: string | null;
|
|
986
1049
|
onFork?: (entryId: string) => void;
|
|
1050
|
+
/** 编辑该回合的用户消息并重新发送(模型从这条消息重新回答) */
|
|
1051
|
+
onEditMessage?: (entryId: string, text: string) => void;
|
|
987
1052
|
onInspectTool?: (toolCallId: string) => void;
|
|
988
1053
|
onOpenFile?: (path: string) => void;
|
|
989
1054
|
}) {
|
|
@@ -1035,7 +1100,7 @@ function TurnBlock({
|
|
|
1035
1100
|
|
|
1036
1101
|
return (
|
|
1037
1102
|
<>
|
|
1038
|
-
{user && <UserMessage message={user} />}
|
|
1103
|
+
{user && <UserMessage message={user} onEditMessage={onEditMessage} />}
|
|
1039
1104
|
{showProcess && (
|
|
1040
1105
|
<div className="pw-turn">
|
|
1041
1106
|
{contextFiles?.map((resource, i) => (
|
|
@@ -1130,12 +1195,55 @@ export function SessionStatsBar({
|
|
|
1130
1195
|
);
|
|
1131
1196
|
}
|
|
1132
1197
|
|
|
1198
|
+
type TurnBlockProps = Parameters<typeof TurnBlockView>[0];
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* 回合级跳过:流式期间只有正在变的那条消息身份会变(foldPiWebEvent 只替换被改动的消息),
|
|
1202
|
+
* 历史回合用到的数据全部保持身份,于是 200 条消息的会话里历史回合可以整体跳过重渲染。
|
|
1203
|
+
*/
|
|
1204
|
+
function turnBlockPropsEqual(prev: TurnBlockProps, next: TurnBlockProps): boolean {
|
|
1205
|
+
if (
|
|
1206
|
+
prev.cwd !== next.cwd ||
|
|
1207
|
+
prev.isStreaming !== next.isStreaming ||
|
|
1208
|
+
prev.isLast !== next.isLast ||
|
|
1209
|
+
prev.lastAssistantIndex !== next.lastAssistantIndex ||
|
|
1210
|
+
prev.retryNotice !== next.retryNotice ||
|
|
1211
|
+
prev.streamStartedAt !== next.streamStartedAt ||
|
|
1212
|
+
prev.runningTool !== next.runningTool ||
|
|
1213
|
+
prev.outputTokens !== next.outputTokens ||
|
|
1214
|
+
prev.workingMessage !== next.workingMessage ||
|
|
1215
|
+
prev.contextFiles !== next.contextFiles ||
|
|
1216
|
+
prev.onFork !== next.onFork ||
|
|
1217
|
+
prev.onEditMessage !== next.onEditMessage ||
|
|
1218
|
+
prev.onInspectTool !== next.onInspectTool ||
|
|
1219
|
+
prev.onOpenFile !== next.onOpenFile
|
|
1220
|
+
) return false;
|
|
1221
|
+
// buildTurns 每次重建回合对象:比较内容而不是引用
|
|
1222
|
+
if (prev.turn.key !== next.turn.key || prev.turn.userIndex !== next.turn.userIndex) return false;
|
|
1223
|
+
if (prev.turn.assistantIndexes.length !== next.turn.assistantIndexes.length) return false;
|
|
1224
|
+
if (!prev.turn.assistantIndexes.every((value, index) => value === next.turn.assistantIndexes[index])) return false;
|
|
1225
|
+
const indexes = [prev.turn.userIndex, ...prev.turn.assistantIndexes].filter((index) => index >= 0);
|
|
1226
|
+
if (!indexes.every((index) => prev.messages[index] === next.messages[index])) return false;
|
|
1227
|
+
// 只比较本回合引用到的工具条目:tools 外层容器每次工具事件都会换身份
|
|
1228
|
+
const toolIds: string[] = [];
|
|
1229
|
+
for (const index of indexes) {
|
|
1230
|
+
for (const part of prev.messages[index]?.content ?? []) {
|
|
1231
|
+
const call = part as { type?: string; id?: string };
|
|
1232
|
+
if (call.type === "toolCall" && call.id) toolIds.push(call.id);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return toolIds.every((id) => prev.tools[id] === next.tools[id]);
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
const TurnBlock = memo(TurnBlockView, turnBlockPropsEqual);
|
|
1239
|
+
|
|
1133
1240
|
export function ChatWindow({
|
|
1134
1241
|
messages,
|
|
1135
1242
|
tools,
|
|
1136
1243
|
contextFiles,
|
|
1137
1244
|
cwd,
|
|
1138
1245
|
onFork,
|
|
1246
|
+
onEditMessage,
|
|
1139
1247
|
isStreaming = false,
|
|
1140
1248
|
error,
|
|
1141
1249
|
connected = true,
|
|
@@ -1171,6 +1279,7 @@ export function ChatWindow({
|
|
|
1171
1279
|
trajectory?: TrajEntry[];
|
|
1172
1280
|
onOpenTrajectory?: (toolCallId: string) => void;
|
|
1173
1281
|
/** 在本机编辑器打开工具行涉及的文件 */
|
|
1282
|
+
onEditMessage?: (entryId: string, text: string) => void;
|
|
1174
1283
|
onOpenFile?: (path: string) => void;
|
|
1175
1284
|
}) {
|
|
1176
1285
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
@@ -1254,6 +1363,7 @@ export function ChatWindow({
|
|
|
1254
1363
|
outputTokens={stats?.tokens.output}
|
|
1255
1364
|
workingMessage={workingMessage}
|
|
1256
1365
|
onFork={onFork}
|
|
1366
|
+
onEditMessage={onEditMessage}
|
|
1257
1367
|
onInspectTool={onOpenTrajectory}
|
|
1258
1368
|
onOpenFile={onOpenFile}
|
|
1259
1369
|
/>
|