dsh-model-flatmap 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,6 +31,13 @@ levels, and the agent-teamwork switch on one surface:
31
31
 
32
32
  - **One surface.** The trigger opens one panel capped at 320 px. The header, the
33
33
  reasoning row, and the teamwork row stay fixed; only the model grid scrolls.
34
+ - **The trigger shows what the native one shows.** Its name is not a string: the
35
+ shipped picker renders the `conversation.input.model.label` slot there, and
36
+ connection plugins use it for a promotion chip (`夜间折扣`), a rate, or a load
37
+ mark (queued people / a rocket when idle). This plugin renders that slot's
38
+ winning component on its own trigger — with the seats it needs — so the two
39
+ modes show the same content. A missing or crashing occupant falls back to the
40
+ catalog name plus the model's own `model.badge`.
34
41
  - **Global search.** Filters across every provider and every family. Groups that
35
42
  end up empty are dropped, so no stray headings remain. Prefix matches rank
36
43
  first, then substring matches, then ordered character matches.
@@ -48,31 +55,59 @@ levels, and the agent-teamwork switch on one surface:
48
55
  `hy3` → `HY3`, `hy4free` / `hy4std` / `hy4-preview` → `HY4`, and a bare `hy`
49
56
  → `HY`. Unregistered brands still collapse their version tail; dictionary
50
57
  families (GPT, Qwen…) are never split.
51
- - **Rows are provider-primary.** Under *By family* the heading already says what
52
- the models are, so the family name is not repeated in a row: the primary text
53
- is the provider name. Only when one provider offers several models in the same
54
- family does a short id-derived suffix differentiate them. The full model name
55
- and description stay in the tooltip.
58
+ - **Rows are provider-primary, with a model tail.** Under *By family* the
59
+ heading already says what the models are, so the family name is not repeated
60
+ in a row: the primary text is the provider name. What neither the heading nor
61
+ the provider can say — which model of the family the row is, and the
62
+ catalog's rate multiplier — is drawn as a quieter tail at the end of the row,
63
+ with the family name and the provider label stripped off: `Qwen3.8-27B` under
64
+ *Qwen* reads `3.8-27B`, and `Deepseek-V4.1-Flash · x0.01` under *DeepSeek*
65
+ reads `V4.1-Flash · x0.01` with the multiplier drawn smaller and bold. The
66
+ full model name and description stay in the tooltip.
67
+ - **Long names and long headings scroll on hover.** A clipped model name or
68
+ group heading runs one eased leftward pass after the pointer rests on it for
69
+ 0.5 s, then stops; leaving resets it instantly. Only the text moves — the
70
+ trailing badge and the title's band never do.
56
71
  - **Reasoning effort.** The current model's reasoning levels render as an
57
72
  exclusive, keyboard-operable group ("推理等级" / "Reasoning effort") driven by
58
73
  radio semantics.
59
74
  - **Adaptive footer.** The reasoning group and the agent-teamwork switch share
60
75
  one line when both fit with a 30 px gap, and fall back to one row each when
61
76
  they do not — the break is CSS flex wrapping, not a measured pixel threshold.
62
- - **Out-of-balance models are struck through — everywhere.** A model whose last
63
- call failed on insufficient balance, credit, or quota keeps a `line-through`
64
- on its row text until a later call for that same model returns anything else.
65
- The strike follows the model the failing request actually used, and covers the
77
+ - **Out-of-balance models are struck through — everywhere, for a day.** A model
78
+ whose last call failed on insufficient balance, credit, or quota keeps a
79
+ `line-through` on its row text until a later call for that same model returns
80
+ anything else, and until the mark has gone 24 hours without a new attempt —
81
+ whichever comes first. A balance is topped up and a quota resets daily, so a
82
+ strike nobody refreshed is stale information, not a warning. The strike
83
+ follows the model the failing request actually used, and covers the
66
84
  Host's `QUOTA` / `ACCOUNT_QUOTA` codes, a provider `402`, and a `429` whose
67
85
  body reports an empty balance — a plain rate limit never strikes. Because a
68
86
  balance is a property of the model route and its account, **not of one
69
87
  conversation**, the marks are global: a failure seen in any Session strikes
70
88
  the model in every Session and workspace, and the state is persisted to
71
- `localStorage` so it survives a reload. Observations are ordered by event
72
- time, so the newest one wins and replaying older history cannot resurrect a
73
- strike that a later call already cleared.
89
+ `localStorage` so it survives a reload. Every mark keeps the time of the
90
+ observation that made it, so opening the list a day later draws that row
91
+ plain. Observations are ordered by that same time, so the newest one wins and
92
+ replaying older history cannot resurrect a strike that has already ended —
93
+ by a later call or by age.
74
94
  - **Theme-aware.** Styled with host theme tokens only; looks correct in both
75
95
  light and dark themes.
96
+ - **Native mode.** The panel's bottom-right `Default style` button hands the
97
+ composer's model seat back to the **host's own** picker — the real shipped
98
+ `ModelSelect`, not a reproduction of it. The plugin does this by *stepping
99
+ aside*: it disposes its own `conversation.input.model` registration, so the
100
+ host's entry underneath wins the slot and renders itself. Nothing is
101
+ imported, copied, or re-mounted, which means an upgraded DSH shows its own
102
+ newest picker and this mode can never drift out of date. The way back is a
103
+ separated action band at the bottom of that picker's own menu — it reuses the
104
+ `conversation.input.model.options` child slot the shipped `ModelSelect`
105
+ declares, so it belongs to the panel it acts on and renders nothing at all
106
+ while the flat panel is shown. It is drawn as an outlined button, not as one
107
+ more menu row: the rows above it pick a value, this one performs an action.
108
+ The choice is remembered across reloads. If the host ships no picker to hand
109
+ over to, the button stays disabled and the panel stays — the composer never
110
+ ends up without a model control.
76
111
 
77
112
  See [`docs/DETAIL.md`](./docs/DETAIL.md) for the full behaviour spec and
78
113
  [`DECISIONS.md`](./DECISIONS.md) for the decision log behind the design.
package/README.zh.md CHANGED
@@ -27,6 +27,11 @@
27
27
 
28
28
  - **一块平面。** 触发器打开一个高度上限 320 px 的面板。头部、推理等级行与协作行
29
29
  固定常驻,只有模型网格可滚动。
30
+ - **触发器显示的内容与原生一致。** 触发器上的模型名不是一个字符串:出厂 picker 在那里
31
+ 渲染 `conversation.input.model.label` 插槽,连接器插件用它在名字旁放促销 chip
32
+ (如 `夜间折扣`)、倍率,或负载标记(排队人数的小人图标 / 空闲时的火箭)。本插件
33
+ 在自己的触发器上渲染该插槽的胜出组件(并补齐它需要的座位),因此两种模式显示同样的
34
+ 内容。占用者缺失或崩溃时回退到目录名 + 模型自带的 `model.badge`。
30
35
  - **全局搜索。** 跨所有 provider 与 family 过滤。过滤后为空的组会被丢弃,不会留下
31
36
  空标题。命中排序:前缀优先,其次子串,最后有序字符匹配。
32
37
  - **分词搜索。** 空白把查询拆成多个 token,每个 token 都要在名称中命中;一次命中的
@@ -41,22 +46,39 @@
41
46
  - **generation 要区分开。** 已注册品牌按 generation 分节:`hy3` 归 `HY3`、`hy4free` /
42
47
  `hy4std` / `hy4-preview` 归 `HY4`,裸 `hy` 仍是 `HY`;未注册品牌仍折叠版本后缀。
43
48
  词典家族(GPT、Qwen…)不拆 generation。
44
- - **row content 以 provider 为主。** *按 family* 下标题已经说明模型是什么,所以行里
45
- 不再重复家族名,主文本改为 provider name;仅当同一 provider 在同一家族下有多个
46
- 模型时,才补一个由 id 推出的短后缀区分。完整模型名/描述保留在 tooltip。
49
+ - **row content 以 provider 为主,末尾补型号尾标。** *按 family* 下标题已经说明模型
50
+ 是什么,所以行里不再重复家族名,主文本改为 provider name。标题和 provider 都说不出
51
+ 的信息——这一行究竟是家族里的哪个型号、以及目录给的倍数——放在行**末尾**一个更安静的
52
+ 尾标里:把家族名和 provider label 从模型名里剥掉,`Qwen` 下的 `Qwen3.8-27B` 显示为
53
+ `3.8-27B`,`DeepSeek` 下的 `Deepseek-V4.1-Flash · x0.01` 显示为
54
+ `V4.1-Flash · x0.01`,其中倍数照旧用更小更粗的字重。完整模型名/描述保留在 tooltip。
55
+ - **长名称与长标题 hover 跑马灯。** 显示不全的模型名或分组标题,在鼠标停留 0.5 s 后
56
+ 向左跑**一趟**(ease-in-out)即停,移开立刻复位。只有文字在动——右侧的 badge 和
57
+ 标题所在的色带本身都不动。
47
58
  - **推理等级。** 当前模型的推理等级渲染为一个互斥、可键盘操作的分组("推理等级",
48
59
  基于 radio 语义)。
49
60
  - **自适应底栏。** 推理等级组与智能体协作开关在放得下(中间至少 30 px)时共用一行,
50
61
  放不下时各自占一行回到原来的两行布局——换行由 CSS flex 换行决定,不靠 JS 量像素。
51
- - **额度不足的模型加删除线,且全局生效。** 某个模型的调用因额度/余额/信用不足而失败后,
52
- 该模型的行文字会带 `line-through` 删除线,直到同一个模型的下一次调用返回非额度不足的
53
- 结果才去掉。标记跟随失败那次请求实际使用的模型;覆盖宿主的 `QUOTA` / `ACCOUNT_QUOTA`
54
- 代码、provider 的 `402`,以及**正文说明余额不足的 `429`**——纯限流的 429 不会划线。
55
- 因为「还有没有额度」是模型路由(及其账号)的属性、**与单个会话无关**,所以标记是全局的:
56
- 在任意会话里观察到的失败,会在所有会话与工作区里同样划线;状态持久化在 `localStorage`
57
- 中,重载后仍然保留。观察按事件时间排序,最新的一条说了算,因此回放更早的历史不会让一条
58
- 已被后续调用清除的划线复活。
62
+ - **额度不足的模型加删除线,全局生效,且只生效一天。** 某个模型的调用因额度/余额/信用不足
63
+ 而失败后,该模型的行文字会带 `line-through` 删除线,直到同一个模型的下一次调用返回非额度
64
+ 不足的结果,或者这条标记已经 24 小时没有新尝试——以先到者为准。余额会被充值、额度会按日
65
+ 重置,所以一条没人更新的划线是**过时的信息**,不是警告。标记跟随失败那次请求实际使用的
66
+ 模型;覆盖宿主的 `QUOTA` / `ACCOUNT_QUOTA` 代码、provider 的 `402`,以及**正文说明余额
67
+ 不足的 `429`**——纯限流的 429 不会划线。因为「还有没有额度」是模型路由(及其账号)的属性、
68
+ **与单个会话无关**,所以标记是全局的:在任意会话里观察到的失败,会在所有会话与工作区里
69
+ 同样划线;状态持久化在 `localStorage` 中,重载后仍然保留。每条标记都记下「这次判定发生的
70
+ 时间」,因此一天之后再打开列表,那一行就是干净的。观察按同一个时间排序,最新的一条说了算,
71
+ 所以回放更早的历史既不能让一条已被后续调用清除的划线复活,也不能让一条已经过期的复活。
59
72
  - **跟随主题。** 仅使用宿主主题令牌着色,在浅色与深色主题下都正确。
73
+ - **原生模式。** 面板右下角的 `默认样式` 按钮把输入框的模型座位交还给**宿主自己的**选择器——真正
74
+ 出厂的 `ModelSelect`,不是它的复制品。做法是**让位**:本插件 dispose 掉自己在
75
+ `conversation.input.model` 的注册,槽于是选中下方那条出厂条目,由宿主渲染它自己的组件。没有
76
+ import、没有拷贝、没有 re-mount,所以 DSH 升级后出现的自然是它最新的 picker,这个模式**结构上
77
+ 不可能过期**。切回面板的入口长在**原生选择器自己的菜单底部**(复用出厂 `ModelSelect` 声明的
78
+ `conversation.input.model.options` 子插槽),所以它属于那个面板、而不是输入框工具栏;平铺模式下
79
+ 它渲染 `null`,原生菜单与出厂状态完全一致。它**不模仿菜单行**:上方一条分隔线,下面是一个带描边
80
+ 的整宽按钮——上面的行是「选一个值」,这个是「执行一个动作」。选择跨重载保留。若宿主没有可交出的选择器,按钮会
81
+ 置灰、面板保持显示——composer 永远不会失去模型控件。
60
82
 
61
83
  完整行为规格见 [`docs/DETAIL.md`](./docs/DETAIL.md),设计决策见
62
84
  [`DECISIONS.md`](./DECISIONS.md)。