@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.
Files changed (96) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +10 -10
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +3 -3
  5. package/.next/react-loadable-manifest.json +2 -2
  6. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  7. package/.next/server/app/_global-error.html +1 -1
  8. package/.next/server/app/_global-error.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. package/.next/server/app/_not-found.html +1 -1
  14. package/.next/server/app/_not-found.rsc +2 -2
  15. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  16. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  17. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  18. package/.next/server/app/api/agent/[id]/events/route.js +2 -2
  19. package/.next/server/app/api/agent/[id]/events/route.js.nft.json +1 -1
  20. package/.next/server/app/api/agent/[id]/route.js +1 -1
  21. package/.next/server/app/api/agent/[id]/route.js.nft.json +1 -1
  22. package/.next/server/app/api/agent/[id]/tree/route.js +1 -1
  23. package/.next/server/app/api/agent/[id]/tree/route.js.nft.json +1 -1
  24. package/.next/server/app/api/agent/new/route.js +1 -1
  25. package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
  26. package/.next/server/app/api/growth/route.js +1 -1
  27. package/.next/server/app/api/growth/route.js.nft.json +1 -1
  28. package/.next/server/app/api/models/route.js +1 -1
  29. package/.next/server/app/api/models/route.js.nft.json +1 -1
  30. package/.next/server/app/api/plugins/route.js +1 -1
  31. package/.next/server/app/api/plugins/route.js.nft.json +1 -1
  32. package/.next/server/app/api/security/route.js +1 -1
  33. package/.next/server/app/api/security/route.js.nft.json +1 -1
  34. package/.next/server/app/api/sessions/[id]/export/route.js +1 -1
  35. package/.next/server/app/api/sessions/[id]/export/route.js.nft.json +1 -1
  36. package/.next/server/app/api/sessions/[id]/route.js +1 -1
  37. package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  38. package/.next/server/app/api/sessions/route.js +1 -1
  39. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  40. package/.next/server/app/api/skills/route.js +2 -2
  41. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  42. package/.next/server/app/api/update/route.js +2 -2
  43. package/.next/server/app/api/update/route.js.nft.json +1 -1
  44. package/.next/server/app/api/version/route.js +1 -1
  45. package/.next/server/app/api/workspaces/route.js +1 -1
  46. package/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  47. package/.next/server/app/index.html +1 -1
  48. package/.next/server/app/index.rsc +4 -4
  49. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  50. package/.next/server/app/index.segments/_full.segment.rsc +4 -4
  51. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  52. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  53. package/.next/server/app/login.html +1 -1
  54. package/.next/server/app/login.rsc +3 -3
  55. package/.next/server/app/login.segments/_full.segment.rsc +3 -3
  56. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  57. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +3 -3
  58. package/.next/server/app/page.js +1 -1
  59. package/.next/server/app/page_client-reference-manifest.js +1 -1
  60. package/.next/server/app-paths-manifest.json +10 -10
  61. package/.next/server/chunks/229.js +1 -0
  62. package/.next/server/chunks/496.js +1 -1
  63. package/.next/server/chunks/686.js +2 -2
  64. package/.next/server/middleware-build-manifest.js +1 -1
  65. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  66. package/.next/server/pages/404.html +1 -1
  67. package/.next/server/pages/500.html +1 -1
  68. package/.next/server/server-reference-manifest.json +1 -1
  69. package/.next/static/chunks/127.c31e688e156605e3.js +1 -0
  70. package/.next/static/chunks/686-9f15e4e370d26815.js +1 -0
  71. package/.next/static/chunks/app/page-708cd3ee4cfe8af5.js +1 -0
  72. package/.next/static/chunks/webpack-9d84b382d4dec27a.js +1 -0
  73. package/.next/static/css/b2d35abc828d4713.css +1 -0
  74. package/README.md +4 -3
  75. package/package.json +1 -1
  76. package/src/app/api/workspaces/route.ts +4 -0
  77. package/src/components/AppShell.tsx +47 -41
  78. package/src/components/ChatWindow.tsx +118 -8
  79. package/src/components/SettingsPanel.tsx +127 -2
  80. package/src/hooks/usePiWeb.ts +27 -11
  81. package/src/i18n.tsx +52 -0
  82. package/src/lib/agent-manager.ts +77 -3
  83. package/src/lib/event-batcher.ts +62 -0
  84. package/src/lib/models-service.ts +430 -19
  85. package/src/lib/process-format.ts +27 -10
  86. package/src/lib/provider-usage.ts +91 -0
  87. package/src/lib/session-retention.ts +119 -0
  88. package/src/lib/types.ts +2 -0
  89. package/.next/server/chunks/780.js +0 -1
  90. package/.next/static/chunks/18.8b025ee3303c3dc7.js +0 -1
  91. package/.next/static/chunks/686-7861c776412c335c.js +0 -1
  92. package/.next/static/chunks/app/page-8ef773ce62ce3aaa.js +0 -1
  93. package/.next/static/chunks/webpack-eebc3d300017fa47.js +0 -1
  94. package/.next/static/css/080ec3dba8907e83.css +0 -1
  95. /package/.next/static/{tU0v6KuZoq5gHtJ8zOyb1 → 222KLUIvefiGf7M0Ov-Qz}/_buildManifest.js +0 -0
  96. /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, and pinning `@latest` avoids
70
- reinstalling an older build that the local cache still holds. Restart `piweb` afterwards to
71
- serve the new version.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rexvane/piweb",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Modern web UI for the pi coding agent, driven by the official pi SDK without modifying pi",
@@ -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
- <div className="mt-1.5 flex items-center gap-5">
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
- {tab === "chat" ? (
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
- </div>
881
+ </div>
865
882
  </div>
866
- </div>
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
- if (kind !== "write" || !state || state.state !== "done" || state.isError) return null;
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) && !failed;
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
- {open && canExpand && (
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
- {/* 用户消息只有复制操作,不提供分支;时钟在图标左侧(dsh clock=start) */}
920
- {text && <MessageActions text={text} clockStart={message.timestamp !== undefined} time={message.timestamp} />}
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 TurnBlock({
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
  />