Skip to content

Releases: AL-Yichen/dsh-fullscreen-input

dsh-fullscreen-input 1.2.4

Choose a tag to compare

@AL-Yichen AL-Yichen released this 07 Oct 12:23

Two defects the portal fix introduced, and a way out when the header is gone.

Fixed

  • The image preview was drawn under the panel, stayed open after its attachment was
    removed, and cost the panel its input after a single character.
    Three symptoms, one
    cause — and all of them a side effect of the previous release. To keep the mask's blur
    from being cut short by an ancestor's containment, the layer portals itself to body
    — and that move lifted its z-index: 2147483000 out of the slot's stacking context and
    into the root one, where it competed with the host's own overlays for the first time.
    The host's lightbox portals to body too, at a lower level, so it lost.
    • While a preview is open the layer steps below the lightbox; the lightbox is what
      the user is looking at, and the mask only has to cover the page. It steps down only
      during a preview.
    • Removing an attachment closes its preview. The host lightbox listens for keys in
      the capture phase and pulls focus back to its own close button whenever its effect
      re-runs — and its onClose is a fresh function on every render of this panel. So an
      orphaned lightbox did not merely linger: typing one character into the panel made it
      re-render, stole focus, and the panel stopped accepting input. That is what "one
      character and then nothing" was.
  • The host's confirmation and approval dialogs were buried under the panel. The other
    face of the same portal: once the layer escapes to body, its z-index joins the root
    context, and at 2147483000 every host modal sat underneath it. On a real machine this
    read as "the agent is waiting for your confirmation and all you can see is the panel" —
    and under the ask policy those confirmations are frequent, each one costing a step of
    closing or dragging the panel aside first.
    • The layer is 999 now, whether or not it escaped. That number is measured, not
      chosen: the host modal stack sits at 1000 and the page furniture tops out at
      996, so the layer has to be above the latter (or the mask stops covering the
      sidebar — the first symptom ever reported) and below the former. The usable band is
      996–999.
  • "Remember panel size" off plus "remember panel position" on could push the panel out of
    the viewport, with no way to drag it back.
    The two switches are independent, so a stored
    position could belong to a size that will never be used again — and the clamp was applied
    against the stored size while the panel was drawn at the default one. Judging a
    small panel's offset and then drawing a large one put top below zero.
    • The clamp now uses the size the panel will actually be drawn at, and the top edge
      keeps a MIN_TOP_INSET of 32px to clear the desktop shell's invisible window drag
      strip at the top of the viewport — grabbing the header there moves the whole window
      instead of the panel.
  • Switching back to frosted glass left the panel looking solid. Turning the solid pane
    on pushed the shared opacity to 100 (deliberately — the default 25% does not read as
    solid), and switching back left that 100 in place: the frosted pane inherited it and
    looked solid, with the value the user had chosen for frosted glass silently gone.
    • The two backgrounds now keep their own opacity (solidOpacity default 100,
      glassOpacity default 25); the slider edits whichever pane is on, and each returns to
      its own value when you switch back. The solid switch no longer rewrites the other
      pane's setting.

Added

  • Ctrl+0 restores the panel to its default position and size, the same as
    double-clicking the header. The reason is not convenience but rescue: the header is
    the only control that can drag the panel back, so once it is off the viewport — or under
    the desktop shell's invisible window drag strip — the mouse has no way left. It
    deliberately does not require focus to be inside the panel, because that is exactly
    the case where the panel cannot be clicked. It is the second key the panel claims, after
    Esc, and only while the panel is open.

Compatibility

The bundle patch and the dsh manifest are untouched, so existing profiles keep working and a
disable row written against row id fullscreen-input still applies. Settings stored by
earlier versions read back unchanged — a stored glassOpacity keeps its value and
solidOpacity starts at 100, which is what the old behaviour produced anyway.

npm shasum (SHA1)  40e2b69537aa063d42f6792c228354c9b92f5adc
install            dsh plugin --profile web add dsh-fullscreen-input

portal 修复带来的两个缺陷,以及一条「标题栏不见了」时的出路。

修复

  • 打开图片预览后,预览图被压在面板下面;关掉附件后预览仍在,而且面板只打得进一个字符就失焦。 三个现象、一个根因,都是上一次修复的副作用。为了让遮罩雾化不被祖先的 containment 截断,层会 portal 到 body —— 而这一搬动把它的 z-index: 2147483000 从「槽位 stacking context 内部」提升到了根 context。它这才第一次真正跟宿主的模态比大小,而宿主的灯箱(ImageLightbox)自己也 portal 到 body,层级比它低,于是被盖住。
    • 预览打开时,层降到灯箱之下:灯箱才是用户正在看的东西,遮罩只需要压住页面。只在预览期间降级,其余时间不变。
    • 移除附件时连同预览一起关闭。 宿主灯箱在捕获阶段监听键盘,并在 effect 重跑时把焦点拉回自己的关闭按钮——而它的 onClose 在本面板每次渲染时都是新函数。所以一个失去所属附件的灯箱不只是"留在那里":在面板里敲下一个字符就会让它重渲染、抢走焦点,面板随即失去输入能力。这正是「输入任意一个字符就失焦」的成因。
  • 宿主的确认框与审批弹窗被面板整块压住。 这是同一个 portal 的另一面:层一旦逃逸到 body,它的 z-index 就进入根 context 参与比较,而它的值是 2147483000 —— 宿主所有模态都被埋在下面。真机上的表现是「agent 在等你确认,你只看到面板」,而 ask 策略下这类确认很频繁,每次都得多一步「先关掉或拖开面板」。
    • 层改用 999,无论是否逃逸。这个值不是拍的,是在真机上量出来的区间:宿主模态栈是 1000,而页面家具最高到 996,层必须同时高于后者(否则遮罩盖不住侧边栏——那正是最早报过的那个现象)与低于前者,可用区间只有 996–999。
  • 关掉「记住面板大小」+ 打开「记住面板位置」,会把面板顶出屏幕并且拖不回来。 两个开关是独立的,于是存下来的位置可能属于一个再也不会用到的尺寸——而夹紧用的正是存储的尺寸,渲染用的却是默认尺寸。等于拿小面板判断偏移合法、再画出一个大面板,top 因此可以是负的。
    • 夹紧改用面板实际将要渲染的尺寸;顶边不再允许贴到 0,保留 MIN_TOP_INSET = 32 以避开桌面壳在视口顶部那条不可见的窗口拖动条——在那里抓住 header 拖动会拖动整个窗口,而不是面板。
  • 切回毛玻璃之后,面板看起来仍然是纯色的。 「纯色面板」开启时会把共用的那份不透明度推到 100(这是刻意的:底色默认只有 25%,不推满就不像纯色),但切回毛玻璃时那份 100 还留着——毛玻璃继承了它,于是看起来和纯色一样,而用户先前为毛玻璃调好的浓度已经悄悄丢了。
    • 现在两种底色各记一份(solidOpacity 默认 100、glassOpacity 默认 25),滑块编辑的是当前这一种的那一份,来回切换时两边各自回到自己调过的值。开关也不再改写另一种模式的数值。

新增

  • Ctrl+0 把面板恢复到默认位置与默认大小,与双击标题栏等价。加它的理由不是方便,而是自救:header 是唯一能用鼠标把面板拖回来的控件,一旦它跑到视口之外、或者恰好落在桌面壳那条不可见的窗口拖动条下面,鼠标就没有任何办法了。为此它不要求焦点在面板内 —— 只要面板开着就生效,这正是"点不到面板"时唯一还能用的路径。它是面板继 Esc 之后抢占的第二个键,且只在面板打开期间监听。

兼容性

组合包补丁与 dsh manifest 均未改动,所以现有 profile 照常工作,针对 row id fullscreen-input 写的禁用行仍然有效。更早版本存下的设置读回来行为不变:glassOpacity 保持原值,solidOpacity 从 100 开始——那本来就是旧行为会得到的值。

dsh-fullscreen-input 1.2.3

Choose a tag to compare

@AL-Yichen AL-Yichen released this 07 Oct 09:24

The mask's blur stopping at a host container, a white-on-white switch on the light theme, and AGENTS.md out of the package.

Fixed

  • The mask covered the conversation column but not the sidebar, on one machine. The
    first reading of the report was a geometry problem, and the fix was built for that.
    Field data said otherwise: the layer and the scrim both measured the full viewport at
    0,0
    with a zero delta, and a hit test in the sidebar landed on the layer — so size
    and stacking were never wrong. What was wrong is where the effect appeared: the mask
    fill is transparent by default, so what the eye reads as the mask is the
    backdrop-filter blur, and the blur stopped at a host row box while the layer still
    measured the whole screen.
    • The cause is container-type: inline-size on a host element between the slot and
      body. Its implied contain: layout makes it a containing-block candidate and cuts
      backdrop-filter sampling short. It also explains why the symptom tracked the machine
      rather than the browser: that viewport made the host lay the column out with that row.
    • The layer now probes its ancestors once mounted and, on a hit, portals itself to
      body
      — above which fixed necessarily means the viewport and backdrop-filter is
      no longer truncated.
    • The probe covers seven causes: transform, filter, backdrop-filter, perspective,
      contain, container-type, will-change. It counts a value only when the engine
      actually returned one — none, auto, normal and the empty string all count as
      unset. (container-type defaults to normal, so missing that check would make the
      probe permanently true.)
    • It escapes only on a hit: with no hit, the DOM position and structure are unchanged.
      If react-dom cannot be resolved, it does not throw — it simply stays put, which is the
      previous behaviour rather than a failed load.
  • On the light theme, a switch in its off state was a solid white block. The dark theme
    was fine. The neutral tint under our own controls came from --dsw-alias-bg-layer-2, a
    surface token, which resolves to white on the light theme — so a white knob on a white
    track on a white pane had no readable state at all. It is now derived from
    --dsw-alias-label-primary (color-mix at 18%): a neutral built from the label
    colour inverts with the theme by construction, so there is no second set of literals
    to maintain. The slider track, hover fills and reference chips share it.
    • Scoped to the panel and the settings dialog, so the host's own use of that token is
      untouched. Each rule still names the same token, so where color-mix is unsupported the
      old appearance returns.

Packaging

  • AGENTS.md is no longer published. It is maintainer instruction ("the seven
    constraints you must not break", "rebuild after touching src/"), and once the package
    sits in node_modules, DSH's dsh-agent-instructions folds it into sessions as
    workspace instruction as soon as an agent touches that directory — its candidate file
    names are literally ['AGENTS.md', 'CLAUDE.md']. So every user who merely installed the
    plugin was having a few KB of "how to modify this library" injected into their sessions.
    • The fix is to remove it from package.json's files, not to rename or delete it:
      in the repository it is wanted, because an agent working there really is modifying the
      plugin. That is why the file stays.
    • The published package is 11 files now instead of 12. src/ and build-client.mjs
      remain, because link: installs rebuild from them.

Compatibility

The bundle patch and the dsh manifest are untouched, so existing profiles keep working and a
disable row written against row id fullscreen-input still applies. Settings stored by
earlier versions read back with identical behaviour.

npm shasum (SHA1)  70947b1a47a02e80e339871cec87df351a07d857
install            dsh plugin --profile web add dsh-fullscreen-input

遮罩的模糊止步于宿主容器、浅色主题下白底白开关,以及把 AGENTS.md 移出发布包。

修复

  • 在一台机器上,遮罩盖住了对话区,却没盖住侧边栏。 报告最初读起来像几何问题,修复也是照着那个方向做的。现场数据推翻了它:.dshfs-layer 与 .dshfs-scrim 都量到完整视口、位于 0,0,与视口差值全为 0;在侧边栏位置做指针命中,命中的正是这一层——所以尺寸与层叠从来没错。错的是效果出现的位置:遮罩底色默认全透明,肉眼读作「遮罩」的其实是 backdrop-filter 的模糊,而模糊止步于宿主某个行的盒子,层本身仍然量到整屏。
    • 成因是槽位与 body 之间一个宿主元素上的 container-type: inline-size。它隐含的 contain: layout 既让它成为包含块候选,又截断了 backdrop-filter 的采样。这也解释了为什么症状跟着机器走而不是跟着浏览器走:那台机器的视口让宿主用上了带这一行的那种布局。
    • 现在挂载后向上探测一次祖先链,命中就把整层 portal 到 body —— body 之上 fixed 必然等于视口,backdrop-filter 也不再被截断。
    • 探测覆盖七类成因:transform、filter、backdrop-filter、perspective、contain、container-type、will-change。判定只认引擎真的返回了值:none、auto、normal 与空串一律算未设置。(container-type 的默认值正是 normal,漏掉这一条探测会恒为真。)
    • 只有命中才逃逸:没有命中时 DOM 位置与结构完全不变。react-dom 取不到时不抛错,只是原地不动 —— 那是旧行为,而不是加载失败。
  • 浅色主题下,关闭状态的开关是一整块白色。 深色主题正常。我们自绘控件下面的那层中性底色取自 --dsw-alias-bg-layer-2,而它是表面 token,浅色主题下解析成白色 —— 于是「白色滑块 + 白色轨道 + 白色面板」糊成一块,既看不出是开关、也看不出当前是开还是关。现在改为从 --dsw-alias-label-primary 派生(color-mix 取 18%):由文字色推导的中性底色,会按构造随主题翻转,不必再维护两套字面量。滑条轨道、hover 底色、引用 chip 底色共用它。
    • 作用域限定在面板与设置对话框,宿主自己对同一 token 的使用不受影响。每条规则写的仍然是那个 token,所以 color-mix 不被支持时旧外观照旧回来。

打包

  • AGENTS.md 不再随包发布。 它是给维护者的指令("七条不能碰的约束""改完 src/ 必须重建");而一旦这个包躺在 node_modules 里,DSH 的 dsh-agent-instructions 就会在 agent 触碰该目录时把它当作工作区指令折进会话 —— 它的候选文件名正是 ['AGENTS.md', 'CLAUDE.md']。于是每一个只是安装了插件的人,会话里都会被塞进几 KB 的"怎么改这个库"。
    • 做法是把它移出 package.json 的 files,不是改名也不是删除:在仓库里它是需要的,因为在那里工作的 agent 确实是在改这个插件。所以文件留着。
    • 发布包因此从 12 个文件变成 11 个。src/ 与 build-client.mjs 继续保留 —— link: 安装要用它们重建。

兼容性

组合包补丁与 dsh manifest 均未改动,所以现有 profile 照常工作,针对 row id fullscreen-input 写的禁用行仍然有效。更早版本存下的设置读回来行为完全一致。

dsh-fullscreen-input 1.2.0

Choose a tag to compare

@AL-Yichen AL-Yichen released this 06 Oct 18:38

One-click removal for file references, and a fix for the references that were quietly not coming back.

Added

  • File references can be removed with one click. Reference entries (@path) now carry
    the same "×" the attachments do. Understanding what it does means knowing that a
    reference is not an attachment
    : a non-image file lives in the host as @path text
    inside the draft document plus one occurrences record — it is not in attachmentIds
    at all — so the attachment removal API does nothing for it and the only way to delete it
    is to edit the draft text, which is exactly what the button does. Both shapes have to
    be handled or it destroys content: a reference alone on its line is removed together
    with its newline (leaving no blank line); one mid-sentence removes only itself and
    collapses the two spaces it frees into one (see @a.ts here → see here). After a
    removal, the remaining references are still references — the chips are rebuilt as usual.
    • One deliberate behaviour: no button is drawn when there is no matching text to
      delete.
      If the draft has moved on and that text can no longer be found, there is
      nothing to remove — and a control that silently does nothing is the very thing this
      feature exists to eliminate. It also keeps an image draft from growing a second remove
      control beside the one on its attachment.

Fixed

  • Removing one reference made every other reference lose its identity. This is the most
    serious one, and the reason it only showed up on a real machine: the host takes
    insertReference spans in detect coordinates — a chip is one character
    (U+FFFC) in the detect projection — while the plan is built in clipboard
    coordinates, where a chip is its whole @path. The first chip happens to start at offset
    0, so it lined up; every later one was off by the accumulated "restored path length − 1",
    the host judged the span out of range and refused it, and the restore pass ended — so
    only the first reference came back as a chip and every later one stayed plain text.
    That drift is now accumulated per insertion (the host cancels one unit of it by appending
    a separating space after a chip) and every span is converted to detect coordinates before
    it is handed over.
    • This defect was invisible to the offline tests: the test double treated spans as
      clipboard coordinates and sliced them directly, and returned success unconditionally. It
      now models the real semantics — a document is a sequence of text and chip fragments,
      and a chip is one detect character — so the old code genuinely fails against it
      (applied=[true,false]) and the fix has regression cover.
  • Clicking "×" threw the caret somewhere else, so the next keystroke landed where you
    never pointed.
    Replacing the textarea's value programmatically lets the browser choose
    the caret position: one that was at the end ended up in the middle of the draft, and the
    very next backspace deleted two entirely unrelated references. The caret is now mapped
    through the edit before the write — unmoved before the change, shifted by the length
    difference after it, and parked at the start of the changed range when it was inside —
    and restored once the write has committed. setSelectionRange does not steal focus, so
    clicking through several removals keeps whatever focus you had.
  • A reference alone on its line left a blank line behind when removed. The test only
    looked at the one character either side, and the host appends a separating space after
    a chip — so @a.ts ␠ (common, because the reference had been restored) was read as an
    inline reference, only the reference itself was deleted, and that space stayed behind as
    the blank line the user sees. It now looks at the whole line: if nothing but the
    reference and whitespace shares it, the line goes together with a newline (for the last
    line of the draft, the newline before it).
  • The remove button on a non-image file was invisible. The reference rail is a scroll
    container (overflow-y:auto) and the entry's "×" is absolutely positioned, overflowing
    5px up and to the right; a 32px image card has slack inside its row, but a 22px text chip
    rests flush against the content box, so the button was clipped away — it was in the DOM,
    focusable, and clickable, just not visible
    , which reads as "this entry has no remove
    control". The rail's padding now leaves room for that overflow.

Compatibility

The bundle patch and the dsh manifest are untouched, so existing profiles keep working and a
disable row written against row id fullscreen-input still applies. Settings stored by
earlier versions read back with identical behaviour.

npm shasum (SHA1)  61cb26b6b4ab954650ad00610540f5ea148f1cf4
npm integrity      sha512-ellxdnICAITaq[...]gOSIpNKqz4J6g==
install            dsh plugin --profile web add dsh-fullscreen-input

文件引用可以一键移除了,并修掉了一个「引用悄悄回不来」的问题。

新增

  • 文件引用也能一键移除。 引用条目(@路径)现在和附件一样有「×」。要理解它的行为,得先知道引用不是附件:非图片文件在宿主那里是草稿文档里的 @路径 文本,外加一条 occurrences 记录,attachmentIds 里根本没有它——所以附件的移除接口对它无效,唯一能删掉它的方式就是改草稿文本,按钮做的正是这件事。两种形态都得处理对,否则会毁内容:独占一行的引用连同换行一起删(不留空行),句子中间的引用只删自己,并把让出来的两个空格收成一个(看看 @a.ts 这个文件 → 看看 这个文件)。删掉一个引用之后,其余的引用仍然是引用——chip 会照常重建。
    • 一个刻意保留的行为:引用在这条栏上没有可删的文本时,不会长出按钮。 草稿已经变过、那段文字找不到了,就没有什么可删;一个点了没反应的控件,恰恰是这个功能要消灭的东西。这同时避免了图片草稿多出一个移除入口(图片走的是附件那条路)。

修复

  • 移除一个引用会让其余引用一起失去「引用」身份。 这是本版最严重的一处,也是它只在真机上暴露的原因:宿主给 insertReference 的 span 用的是 detect 坐标——一个 chip 在 detect 投影里只占 1 个字符(U+FFFC)——而计划里的偏移是 clipboard 坐标(一个 chip 是它完整的 @路径)。第一个 chip 恰好从偏移 0 开始,所以对得上;之后每一个都偏大了「已恢复路径长度 − 1」的累加值,宿主判定越界直接拒绝,恢复流程就此中止——于是只有第一个引用变回 chip,其余全部退化成纯文本。现在逐项累加这份漂移(宿主在引用后补分隔空格时反向抵消 1),每个 span 都换算到 detect 坐标再交给宿主。
    • 这个缺陷离线测试原本抓不到:测试替身把 span 当成 clipboard 坐标直接切片,还无条件返回成功。现在替身按真实语义建模——文档是一串「文本片段 / chip 片段」,chip 在 detect 里只占 1 个字符——于是旧代码在它上面会真的失败(applied=[true,false]),这条修复也就有了回归保护。
  • 点「×」会把光标扔到别处,下一次按键因此落在你没指过的地方。 程序化写入替换输入框的值之后,光标位置由浏览器随意决定:光标原本停在末尾,删除后却跑到了草稿中间,于是紧接着的一次退格删掉了两个毫不相干的引用。现在写入前按本次编辑把光标映射好——改动点之前的原地不动、之后的按长度差平移、落在改动区间内的停到区间起点——写入提交后再设回去。setSelectionRange 不抢焦点,连续删几个不会被夺走焦点。
  • 独占一行的引用删掉后会留下一个「空行」。 判据原本只看引用前后的那一个字符,而宿主会主动在 chip 后面补一个分隔空格——于是 @a.ts ␠ 这种(很常见,因为引用被恢复过)被当成了行内引用,只删掉引用本身,那个空格留在行里就成了用户看到的空行。现在改成看整行:只要这一行除了引用与空白没有别的内容,就连同一个换行一起整行删掉(末行则删它前面那个换行,避免留下一行空白)。
  • 非图片文件的移除按钮看不见。 引用条是滚动容器(overflow-y:auto),而条目上的「×」绝对定位、向左上各溢出 5px;32px 的图片卡在行内本来就有余量,22px 的文字 chip 却紧贴内容盒顶边,按钮于是被裁掉——它在 DOM 里、可聚焦、也能点击,就是看不见,看起来像「这个条目没有移除入口」。现在引用条的内边距留出了足够容纳这点溢出的空间。

兼容性

组合包补丁与 dsh manifest 均未改动,所以现有 profile 照常工作,针对 row id fullscreen-input 写的禁用行仍然有效。更早版本存下的设置读回来行为完全一致。

dsh-fullscreen-input 1.1.0

Choose a tag to compare

@AL-Yichen AL-Yichen released this 06 Oct 17:13

Panel resizing, a three-way pane background, and a summon hotkey.

Added

  • The panel resizes from any edge or corner — eight directions, with the cursor as
    the only affordance and no visible grips. Minimum 360×240, never larger than the
    viewport. A corner drag changes the panel's shape, not merely its size. The
    size is remembered; double-clicking the header restores position and size together.
  • A solid pane switch, mutually exclusive with the frosted one, so the pane
    background is now one of three states: both off = fully transparent, solid only =
    colour without blur, glass only = colour plus blur. Turning solid on pushes the
    pane opacity to 100% once, so it looks like the switch it is named after.
  • A switch that removes the mask entirely. The page behind becomes clickable and
    scrollable. The cost is that click-outside-to-close no longer applies — use Esc
    or the summon key.
  • A configurable summon hotkey, F2 by default: tap to open, tap again to close.
    Function keys only (F1–F12), because the listener sits on the window and a bare
    character key would fire while you type. F1 is deliberately not the default —
    Chrome opens a help page for it.
  • A one-time intro dialog explaining that double-clicking the header resets the
    panel, and where the settings live. "Do not show again" makes that permanent;
    merely closing it brings the dialog back on the next launch, which is the point.
    Settings has a button to bring it back on demand.
  • Wheels outside the pane are forwarded to whatever is under the pointer, so you
    can consult the conversation while writing. Scrolling over the message list scrolls
    the messages, not the sidebar. Wheels inside the pane still belong to the pane.
  • Independent remember-size and remember-position switches. Both default to
    size-on / position-off.

Fixed

  • Turning off "remember panel size" now actually stops the size being remembered.
    The switch was bypassed in three separate places — the mount read the stored size
    unconditionally, every resize wrote it unconditionally, and the render fell back to
    the stored value when the in-memory one was empty. The last one was the quietest:
    fixing only the first two still applied the size. The panel's size now comes from a
    single in-memory value, seeded from storage only while the switch is on. Closing the
    switch deliberately does not delete the stored size, so turning it back on returns
    you to the size you had set.
  • The panel can no longer be dragged or resized off-screen, and a position read
    back from storage is clamped too — so anyone already stuck off-screen recovers the
    moment they update, without clearing settings. A strip of the panel always stays
    reachable (120px horizontally, 48px vertically, enough to grab the header), and the
    drag stops at the edge instead of snapping back afterwards.
  • The "panel behaviour" settings no longer fall out of the settings dialog's scroll
    area.
    The cause was a JSX nesting slip, not styling: a closing bracket sat one
    level early, so those groups became siblings of the scroll area rather than
    children — they did not scroll with the rest and were clipped by the dialog. The
    code was perfectly valid syntax, which is why a syntax check passed it; it took
    looking at the DOM to find. Two structural checks now cover the shape: the dialog
    may have only two direct children (header and scroll area), and every group must be
    inside that scroll area.
  • The settings dialog no longer overflows when its content is taller than the
    pane.
    A flexbox default, not a missing rule: a flex item's min-height is auto,
    which refuses to shrink below its content and silently overrides max-height.
    min-height: 0 restores scrolling.

Changed

  • "Glass opacity" is now "panel opacity". The old name said the opposite of what
    the value does — it is the fill alpha, so "25% transparency" was 25% opaque, and
    smaller numbers were more transparent. Read the new name as strength: 100% is
    fully opaque. For the same reason "glass colour" is now "panel colour", and the
    header icon is called a sun, which is what the eight-toothed shape looks like
    at 16px.
  • "Both background switches off" now means no background at all. Before, turning
    glass off left the pane still tinted at the panel-opacity value, so "off" produced a
    faint wash rather than a transparent pane. The fill is now written as a literal
    transparent when neither switch is on — the stylesheet's fallback covers a value
    that fails to resolve, not one that was never set.

Compatibility

The bundle patch and the dsh manifest are untouched, so existing profiles keep working
and a disable row written against row id fullscreen-input still applies. Settings
stored by 1.0.0 read back with identical behaviour.

npm shasum (SHA1)  da4a6fd873b870c79a2efb29a3d52201c96fc987
npm integrity      sha512-NHHc7WJo62WcXjXHV1xwVVLHkhY/Qjs5K1ESVAFNTb6OPcRaoG3ROFjiYIB4hxcQgseli+y3GerfFTA+JVru2Q==
install            dsh plugin --profile web add dsh-fullscreen-input

面板可调整大小、底色改为三态,并新增唤起快捷键。

新增

  • 面板可拖拽任意边缘或四角调整大小 —— 8 个方向,光标就是唯一的提示,没有额外的可见把手。最小 360×240,最大不超过视口。拖角改变的是面板的形状,而不只是尺寸。尺寸会被记住;双击标题栏可连同位置一起复原。
  • 「纯色面板」开关,与毛玻璃互斥。于是底色共三种状态:两个都关 = 完全透明;只开纯色 = 铺色但不模糊;只开毛玻璃 = 铺色 + 模糊。开启纯色时会把「面板不透明度」一次性推到 100%,让它名副其实。
  • 「启用遮罩层」开关:关掉后遮罩整个不渲染,鼠标可直接点击、滚动背后的页面。代价是「点击面板外关闭」不再生效,请改用 Esc 或唤起快捷键。
  • 唤起快捷键,默认 F2:按一下打开、再按一下关闭。只接受功能键(F1–F12),因为监听挂在窗口上,裸字符键会在你打字时误触发。默认刻意不用 F1 —— Chrome 会为它打开帮助页。
  • 首次打开面板时弹一次使用提示,说明双击标题栏可复位、以及设置入口在哪。勾选「不再提示」才会永久关闭;只点关闭,下次启动仍会提示(这正是设计意图)。设置最下方有按钮可随时唤回。
  • 鼠标在面板之外滚动滚轮,会滚动背后的会话 —— 边看 LLM 输出边写提示词时不用来回开关面板。在消息列表上滚就滚消息,不会误滚侧边栏;面板内部的滚轮仍归面板。
  • 「记住面板大小」与「记住面板位置」是两个独立开关(默认记住大小、位置每次居中)。

修复

  • 关掉「记住面板大小」后,面板大小仍然被记住。 这个开关在三处都被绕过了:挂载时无条件读取存下的尺寸、每次调整大小都无条件写入、以及渲染时在内存值为空的情况下又回退去读设置里的值。最后一处最隐蔽 —— 只修前两处,尺寸仍会被偷偷应用回来。现在面板尺寸只由内存中的一个值决定,且仅在开关打开时才从存储播种。关掉开关刻意不删除已存的尺寸,所以重新打开即可回到之前调好的大小。
  • 面板不会再被拖出或拉出屏幕,而且从存储读回的位置也会被夹紧 —— 已经处在这个状态的用户,更新后一打开面板就自动回到可见范围,不需要手动清理设置。面板始终保留一部分在屏幕内(水平 120px、垂直 48px,足以抓住标题栏),拖动过程中就会停住,而不是事后弹回。
  • 「面板行为」及以下的设置项不再跑出设置对话框的滚动区。 真因是 JSX 结构错位而非样式:一个闭合括号早了一位,那几组于是成了滚动区的兄弟而不是子元素 —— 它们不随其余内容滚动,还会被对话框裁掉。这段代码语法完全合法,所以语法检查放过了它,最后是靠看 DOM 才定位。现在有两条结构性检查覆盖这个形状:对话框只能有「标题 + 滚动区」两个直接子元素,且所有分组都必须位于滚动区内部。
  • 设置内容高于面板时不再溢出。 这是 flexbox 的默认值陷阱,不是漏写规则:弹性子项的 min-height 默认为 auto,它拒绝收缩到内容以下,从而静默压过 max-height。补上 min-height: 0 后恢复正常滚动。

变更

  • 「玻璃透明度」更名为「面板不透明度」。 旧名字把含义说反了:这个值直接就是填充的 alpha,所以「透明度 25%」实际是「25% 不透明」,数值越小反而越透明。改名为「面板不透明度」后按「浓度」理解:100% 完全不透明。同一个原因,「玻璃颜色」改为「面板颜色」;面板头部的图标改称小太阳 —— 八齿造型在 16px 下看起来就是太阳而不是齿轮。
  • 「两个底色开关都关」现在等于「没有底色」。 以前关掉毛玻璃后面板仍会按「面板不透明度」继续铺底,所以「关掉」拿到的是一层淡底色而不是透明面板。现在两个开关都关时,填充值显式写入字面量 transparent —— 样式表的回退值兜的是「取值失败」,兜不住「根本没设」。

兼容性

组合包补丁与 dsh manifest 均未改动,所以现有 profile 照常工作,针对 row id fullscreen-input 写的禁用行仍然有效。1.0.0 存下的设置读回来行为完全一致。