API v1
Documentation/开发指南

超级块响应式布局最佳实践

以实际内容区计算比例和空间预算,按信息优先级切换布局,并验证文字、进度与操作区域不会重叠。

Plugin API v1

Documentation content is currently available in Simplified Chinese.

超级块的布局应遵循三个步骤:测量 iframe 的实际内容区,按宽高分配空间,再按信息优先级选择布局。 比例计算适合字号和间距的小范围调整;信息数量和排列方向需要根据内容预算切换。

#1. 用页面内容区计算,不用网格占位或屏幕分辨率

在清单中声明的 cols × rows 是网格占位,包含宿主保留的间隔和标题区域,并不等于页面可以使用的宽高。当前桌面画布与移动设备采用 160 CSS 像素的逻辑网格;已放置组件的页面尺寸为:

text
W = cols × 160 − 2 × 24
H = rows × 160 − 2 × 12 − 24
网格占位 已放置组件的页面内容区
1×1 112×112
2×1 272×112
2×2 272×272
4×1 592×112
4×2 592×272
4×4 592×592

组件目录卡片可能使用未扣除间隔的展示尺寸,例如 160×160。上述数字用于理解当前宿主和建立测试用例,不要把它们写成插件的运行时尺寸协议。实际排版应始终读取页面根元素的 clientWidth、clientHeight,并用 ResizeObserver 监听变化。

宿主会缩放整张逻辑画布。因此,屏幕截图中的像素大小、screen.width、devicePixelRatio、外层 iframe 的变换后矩形,都不适合决定页面布局。不要再按屏幕缩放一次页面。context.surface 用来对齐内缩描边和圆角,不是需要从页面宽高中再次扣除的网格间隔,详见外轮廓几何。

#2. 先做空间预算,再做有限的比例缩放

推荐以下起点,具体比例和上下限需要结合组件内容验证:

text
D = min(W, H)
P = clamp(8, 0.07 × D, 14)       // 内边距,CSS px
G = clamp(4, 0.035 × D, 8)       // 区域间距,CSS px
B = H − 2P − T − F − 2G         // 头部、主体、底部三行时的主体高度

这里 T、F 是头部与底部实际预留高度。例如 272×112 的活动卡片取 P=8、G=4、T=24、F=24,主体只剩 40px。两行 12px 标题约占 32.4px,不能再叠加一行预览。此时应收起预览,或者移除冗余底部操作并重新计算预算。

字号也要同时满足两条约束:

  • 高度约束:主数值字号不得超过主体扣除标签、进度条、间距后的剩余高度。
  • 宽度约束:为 100%、负数、单位、长模型名等内容预留空间。对于可变字符串,限制行数或省略;必须完整显示时,测量文本并切换布局。

例如主数值使用 line-height: 1,标签占 14px,进度条与间距共占 8px,则可以先计算 fontSize ≤ B − 22。再根据数值列宽度和最坏字符串约束字号,最后限制最大字号。不能只用 vw 放大字体:592×112 的长条高度仍然只有 112px。

当预算小于最小可读字号时,应减少信息、换行布局、提供详情入口,或缩小清单中的 allowedSizes 范围。不要持续压缩字号到无法阅读。

#3. 以内容容量切换布局

为每项信息规定最小空间,再确定展示优先级。推荐从最小布局向外增加内容:

组件 最小布局保留 空间增加后展示
活动 状态、当前标题、主要操作 消息预览、模型时间、最近对话列表
用量 窗口名称、百分比、进度、重要警告 重置时间、积分、次窗口、Token 统计
运行配置 模型入口、当前推理强度、速度开关 标题栏、完整选项、说明

横向布局至少需要满足 可用宽度 ≥ 模型最小列宽 + 两个操作列宽 + 间距;纵向布局至少需要满足 主体高度 ≥ 标题区域 + 操作行 + 间距。长宽比只能帮助选择方向,不能代替这两个条件。

在 112×112 的运行配置中,可以让模型独占上半区,推理强度和速度各占下方一列;将完整推理选项收起,由当前强度按钮承担循环切换。模型名最多两行,“标准”等短标签保持不换行。不要把横向三个完整面板直接缩进小方块。

错误提示同样需要预算。紧凑布局可用错误区域替换主体,保留刷新或重试操作;长错误文本在该区域内部滚动。不要把错误作为没有高度预留的额外一行追加。

#4. 可直接使用的布局骨架

下面的示例显示一个标签、数值、进度条和重要提示,按主体剩余空间决定数值字号。它采用保守的四字符宽度上限,适合 100%;换成金额或任意文本时,需要重新计算宽度预算。

html
<style>
  * { box-sizing: border-box; }
  html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; }
  .card {
    width: 100%; height: 100%; min-width: 0;
    display: grid;
    grid-template-rows: minmax(0, 1fr) 14px;
    padding: var(--pad, 8px); gap: var(--gap, 4px);
    background: #101010; color: #eee; font-family: system-ui;
  }
  .primary {
    min-height: 0; min-width: 0;
    display: grid; grid-template-rows: 14px minmax(0, 1fr) 4px;
    gap: 4px;
  }
  .label, .notice {
    min-width: 0; font-size: 10px; line-height: 14px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .value { min-width: 0; align-self: center; font-size: var(--value-size, 24px); line-height: 1; white-space: nowrap; }
  .track { height: 4px; background: #333; }
  .track > span { display: block; height: 100%; width: 100%; background: #e18d7b; }
  .notice { color: #dd955b; }
</style>
<div class="card" id="card">
  <main class="primary">
    <span class="label">每周用量</span>
    <strong class="value">100%</strong>
    <div class="track"><span></span></div>
  </main>
  <span class="notice">用量暂不可用</span>
</div>
<script>
  const card = document.querySelector('#card');
  const clamp = (min, value, max) => Math.max(min, Math.min(value, max));
  function layout() {
    const W = card.clientWidth, H = card.clientHeight;
    if (!W || !H) return;
    const D = Math.min(W, H);
    const P = clamp(8, D * .07, 14);
    const G = clamp(4, D * .035, 8);
    const B = H - 2 * P - 14 - G;
    // Label 14 + two gaps 8 + track 4. Width allows four full ems.
    const fontSize = Math.min(56, B - 26, (W - 2 * P) / 4);
    card.style.setProperty('--pad', P + 'px');
    card.style.setProperty('--gap', G + 'px');
    card.style.setProperty('--value-size', Math.max(16, fontSize) + 'px');
  }
  new ResizeObserver(layout).observe(card);
  layout();
</script>

此骨架按不小于 112×112 的内容区设计。若需要支持更小尺寸,应另设最小布局并验证,不能仅把公式的最小字号调低。实际插件中的数值仍应来自真实状态。

使用 Grid 或 Flex 时,主体设置 min-height: 0、文本列设置 min-width: 0,让浏览器能够分配有限空间;同时确保子内容确实适合该空间。min-height: 0 和根元素的 overflow: hidden 都不会自动解决重叠,后者只会把超出部分裁掉。

#5. 验证的是区域关系,不只是页面溢出

发布前,至少完成以下验证:

  1. 从清单的 allowedSizes 枚举全部规格,检查当前宿主对应的真实内容区;同时检查目录卡片和布局断点前后 1px。默认规格通过不代表其它规格可用。
  2. 覆盖加载、空数据、长中文和无空格英文、0%、100%、长模型名、最多选项、警告、长错误文本,以及隐藏可选字段后的状态。
  3. 在浏览器中测量关键区域的 getBoundingClientRect():兄弟区域不能相交,文本和进度条不能越过各自分配的区域,按钮不能互相覆盖。允许滚动的列表只在自己的容器内滚动。
  4. 同时检查 scrollWidth、scrollHeight。它们只能发现越界,发现不了发生在根元素内部的文字、进度条和底部操作重叠。仅用模拟 DOM 的业务测试也无法验证 CSS 排版。
  5. 对最小方块、最矮长条和最大规格保存截图,检查字号、截断、圆角边缘和实际可点击区域;最后在桌面预览与设备页面中验证宿主缩放效果。

这些公式是可验证的设计起点,不是统一的强制断点。每个插件应根据实际文本、操作数量和展示优先级建立自己的尺寸预算,并把最小支持规格纳入回归检查。