【免费下载链接】pomotroid
:tomato: Simple and visually-pleasing Pomodoro timer
Pomotroid 是一款基于 Tauri + Svelte 的番茄钟桌面应用,其计时器窗口大量使用纯图标按钮、设置窗口使用极简的开关标签,很多控件的具体行为与平台注意事项仅靠图标难以自明。本指南以 openspec/specs/tooltips/spec.md 为核心,完整讲解 Pomotroid 的 Tooltip 组件契约、延迟与即时两种交互模型、视图边缘翻转定位算法、主题适配与无障碍实现,并给出计时器窗口与设置窗口的完整 tooltip 挂载清单(含 i18n key 与触发延迟),使读者既能直接复用这套组件模式,也能对照源码理解其底层原理。
一、为什么需要一套 Tooltip 体系:背景与设计动机
Pomotroid 的 UI 设计遵循"极简"原则:计时器窗口中的设置、统计、重开本轮、跳过、重置、静音等控件全部是图标按钮,其aria-label虽可供屏幕阅读器使用,但对普通用户来说,控件的精确行为(如"重置"会清空整个会话进度、"跳过"会进入下一轮)并不可直接发现;设置窗口中则以"短标签 + 开关"为主,诸如 Verbose Logging、WebSocket Server 这类需要额外说明的开关,也没有空间在标签旁展开解释。
原提案 openspec/changes/archive/2026-03-26-ui-tooltips/proposal.md 明确指出两个核心诉求:
- 即时上下文(just-in-time context):在不增加 UI 密度(不 clutter)的前提下,让用户在悬停的瞬间获得控件用途与后果说明;
- 平台相关提示的精确投放:GNOME 下托盘图标需要安装 AppIndicator 扩展这一提示,此前以静态
.note段落显示给所有平台用户,属于信息噪音。改用 info 图标 + 即时 tooltip 后,该提示只在 Linux 平台出现,恰好出现在用户即将使用该开关的时刻。
设计目标与非目标(摘自 design.md)也值得注意:
- 目标:可复用的
Tooltip.svelte组件;延迟(按钮/开关)与即时(info 图标)两种交互模式;CSS 驱动定位 + JS 在视图边缘翻转;全部文案走 Paraglide i18n;用 info 图标替换 GNOME 托盘提示段落。 - 非目标:不支持富内容(HTML/图片,仅纯文本);不针对触屏(桌面应用);不引入第三方 tooltip 库(保持零依赖)。
从仓库结构看,最终实现完全落在这套约束内:src/lib/components/Tooltip.svelte与src/lib/components/TooltipInfo.svelte是两个新增组件,i18n key 全部集中在src/messages/*.json,未在package.json引入任何定位库(该功能为纯前端实现,后端 Rust 侧无改动)。
二、Tooltip.svelte 组件契约
原规格文档定义了Tooltip.svelte的完整组件契约,实现位于 src/lib/components/Tooltip.svelte。
2.1 Props 与插槽
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
text | string | (必填) | tooltip 文案,由调用方通过 Paraglide 翻译后的消息函数传入(如m.tooltip_reset()) |
delay | number | 600 | 悬停多少毫秒后显示 tooltip |
placement | 'above' \| 'below' | 'above' | 首选显示方位 |
触发元素通过默认插槽(default slot)传入,即插槽内容可以是任意元素(按钮、span、甚至一组元素)。源码中的 Props 定义与解构如下:
interface Props { text: string; delay?: number; placement?: 'above' | 'below'; children: Snippet; // Svelte 5 的 Snippet 类型,对应默认插槽 } let { text, delay = 600, placement = 'above', children }: Props = $props();在模板中使用时,调用方只需要这样写(以 src/lib/components/Timer.svelte 的 Restart Round 按钮为例):
<Tooltip text={m.tooltip_restart_round()}> <button class="btn-side" onclick={timerRestartRound} aria-label="Restart round"> <svg width="18" height="18" viewBox="0 0 16 16">…</svg> </button> </Tooltip>2.2 触发与隐藏的交互模型
规格文档用四个场景定义了交互模型,源码实现(show()/hide()函数)逐一对应:
| 场景 | 规格要求 | 源码实现 |
|---|---|---|
| 延迟出现 | 悬停达到delay毫秒后显示 | show()中若delay > 0则setTimeout(run, delay),且visible || timer !== undefined时直接返回,避免重复计时 |
| 即时出现 | delay={0}时悬停立即显示 | delay === 0分支直接执行run(),不走定时器 |
| 鼠标离开立即消失 | 移出触发元素即刻隐藏 | hide()中clearTimeout(timer)取消未触发的定时器,并将visible、positioned、flipped、tooltipStyle全部复位 |
| 视图边缘翻转 | 首选方向会溢出视口时反向渲染 | updatePosition()中计算flipped(见下节) |
触发事件绑定在包装层span.tooltip-wrapper上:onpointerenter={show}、onpointermove={show}、onpointerleave={hide}。使用 Pointer Events 而非 mouseenter/mouseleave 是合理的——onpointermove的重复触发会被show()顶部的守卫条件(visible || timer !== undefined)拦截,不会重复创建定时器。
三、定位算法:CSS 定位 + 视口边缘翻转
规格文档要求"触发元素位于会使 tooltip 在首选方向溢出视口的位置时,tooltip 应在相反方向渲染"。原设计文档 openspec/changes/archive/2026-03-26-ui-tooltips/design.md 记录了决策过程:曾考虑use:action 与第三方定位库(Floating UI / Popper.js),最终选择Svelte 组件包装 +position: fixed+ 数行 JS 翻转逻辑,理由有二:
use:action 难以在元素外渲染 DOM 节点,且会让无障碍接线(aria-describedby)更复杂;- Pomotroid 窗口尺寸固定且较小,纯 CSS 无法读取视口位置,但几行
getBoundingClientRect()检查足以覆盖边界情况,无需引入完整定位库。
3.1 核心实现(源码级)
updatePosition()的实现要点(src/lib/components/Tooltip.svelte):
function updatePosition() { if (!wrapper || !tooltipEl) return; const wRect = wrapper.getBoundingClientRect(); const tRect = tooltipEl.getBoundingClientRect(); const centerX = wRect.left + wRect.width / 2; const pad = 8; // 垂直翻转:触发元素距视口顶部不足 70px 且首选方位为 above 时,翻转到下方 flipped = wRect.top < 70 && placement === 'above'; let top: number; if (actualPlacement === 'below') { top = wRect.bottom + 8; } else { top = wRect.top - 8 - tRect.height; } // 水平方向:以触发元素中心为基准,夹取在视口内(左右各留 8px 内边距) let left = centerX - tRect.width / 2; if (left < pad) left = pad; if (left + tRect.width > window.innerWidth - pad) { left = window.innerWidth - pad - tRect.width; } // 箭头偏移:始终指向触发元素的水平中心 const arrowLeft = centerX - left; tooltipStyle = `top:${top}px;left:${left}px;--arrow-left:${arrowLeft}px`; positioned = true; }几个值得注意的实现细节:
- 翻转阈值 70px:当触发元素顶部距视口顶部不足 70px 且首选方位为
above时翻转为below——这覆盖了标题栏(Titlebar)等高居窗口顶部的按钮场景; - 实际方位由
$derived计算:actualPlacement = flipped ? 'below' : placement,并在模板中通过class:below切换箭头方向; position: fixed规避裁剪:规格要求 tooltip 使用position: fixed,确保其不被任何overflow容器裁剪。这一点在统计页的年度热力图(src/lib/components/stats/YearlyView.svelte)中体现得尤为明显——SVG 内部的单元格 tooltip 同样采用"视口固定 + 手动算坐标"的模式(注释明确写着 "viewport-fixed so it escapes SVG/overflow clipping");- 防闪现:tooltip 默认
visibility: hidden,直到 JS 完成定位后才通过.positioned类显示,避免在左上角闪现一帧; - 箭头随触发元素移动:通过 CSS 变量
--arrow-left传入,箭头始终对准触发元素的水平中心,即使 tooltip 被水平夹取偏移后也是如此。
四、TooltipInfo 组件:设置窗口的即时说明图标
规格文档要求系统提供TooltipInfo.svelte:渲染一个样式化的信息图标,悬停时立即显示 tooltip(delay={0}),仅接受text: string一个 prop。它被设计为放置在设置窗口的开关标签旁,用于承载上下文说明而不使布局拥挤。
实现位于 src/lib/components/TooltipInfo.svelte,源码极简:
<Tooltip {text} delay={0}> <span class="info-icon" role="img" aria-label="More information" onclick={(e) => e.stopPropagation()} > <svg width="13" height="13" viewBox="0 0 13 13" fill="none" aria-hidden="true">…</svg> </span> </Tooltip>两个与规格呼应的实现要点:
delay={0}即时显示:与按钮的 600ms 延迟形成对比。设计文档的 rationale 是:info 图标存在的唯一目的就是被悬停以获取信息,用户期待的是即时反馈;而 600ms 延迟是为了防止 tooltip 在鼠标随意划过按钮时频繁弹出造成噪音;stopPropagation防误触:注释说明必须使用<span>而非<button>,因为 info 图标可能位于<button>标签内部(见 SettingsToggle.svelte 的行结构),span 是合法的 phrasing content;onclick中stopPropagation阻止 info 图标的点击冒泡触发父级按钮的切换逻辑。
4.1 视觉层级
规格要求 info 图标静止时使用--color-foreground-darker、悬停时使用--color-foreground,保持相对相邻标签的次要地位。最终实现通过opacity达成同等的"次要但可发现"效果:静止时opacity: 0.55,悬停时opacity: 1,配合transition: opacity 0.15s平滑过渡,且设置cursor: default、user-select: none避免误导用户点击。
4.2 在 SettingsToggle 中的集成
设置窗口的开关行统一由 src/lib/components/settings/SettingsToggle.svelte 渲染,它新增了可选的tooltip?: stringprop:
<span class="label" >{label}{#if tooltip}<TooltipInfo text={tooltip} />{/if}</span >调用方(如 SystemSection 的 WebSocket 开关)只需传入tooltip={m.tooltip_websocket()},info 图标即自动出现在标签旁。这种"一行接入"的设计让设置窗口的说明性提示实现了零样板(boilerplate-free)挂载。
五、计时器窗口:Tooltip 挂载全量清单
规格文档给出了计时器窗口的完整 tooltip 清单,以下表格完整继承(i18n key 与延迟均可在 src/messages/en.json 与对应组件中逐一核对):
| 元素 | 位置 | i18n key | 延迟 |
|---|---|---|---|
| Settings 按钮 | Titlebar | tooltip_settings | 600 ms |
| Statistics 按钮 | Titlebar | tooltip_statistics | 600 ms |
| Restart Round 按钮 | Timer controls | tooltip_restart_round | 600 ms |
| Skip 按钮 | Timer controls | tooltip_skip | 600 ms |
| Reset 按钮 | Footer | tooltip_reset | 600 ms |
| Mute/Unmute 按钮 | Footer | tooltip_mute/tooltip_unmute | 600 ms |
| Round indicator | Footer | tooltip_round_counter/tooltip_round_counter_session | 600 ms |
5.1 标题栏:Settings 与 Statistics
在 src/lib/components/Titlebar.svelte 中,两个按钮以{#snippet}定义并通过<Tooltip>包装,且会根据平台摆放在标题栏左/右侧(macOS 的交通灯按钮占位左侧,非 macOS 在左侧显示这两个按钮)。这是规格中 600ms 延迟的标准用法。
5.2 计时控制区:Restart Round 与 Skip
src/lib/components/Timer.svelte 中,重开本轮(回退图标)与跳过(前进图标)按钮分别包裹在<Tooltip text={m.tooltip_restart_round()}>与<Tooltip text={m.tooltip_skip()}>中。
规格为这两个按钮定义了明确的场景语义:
- Skip tooltip 描述动作:悬停 600ms 后显示
tooltip_skip,说明"跳过当前轮次并开始下一轮"("Skip to the next round."); - 值得注意的是,规格与设计文档对Play/Pause 按钮有意不加 tooltip——其功能被普遍理解,避免对明显控件过度 tooltip(over-tooltipping),design.md 的 Open Questions 中明确记录了这一取舍并留待反馈复审。
5.3 底部栏:Reset、Mute/Unmute 与 Round indicator
src/lib/components/TimerFooter.svelte 集中了三类工具提示,其中两类是状态条件化的:
Reset(规格场景:传达后果):悬停 600ms 显示tooltip_reset,文案明确告知破坏性后果——"Reset the timer to the first work round. Current session progress will be cleared."(计时器回到第一个工作轮,当前会话进度将被清除)。这类"后果前置"的 tooltip 文案设计,正是 tooltip 体系价值的典型体现。
Mute/Unmute(规格场景:反映当前状态):文案随音量状态动态切换:
<Tooltip text={localVolume === 0 ? m.tooltip_unmute() : m.tooltip_mute()}>即音量大于 0 显示tooltip_mute("Mute alert sounds"),音量为 0 显示tooltip_unmute("Unmute alert sounds")。配合premuteVolume记住静音前的音量,取消静音时能恢复原音量而非固定回 1.0。
Round indicator(规格场景:反映长休息模式):同一触发元素按long_breaks_enabled状态切换两个 key:
<Tooltip text={$settings.long_breaks_enabled ? m.tooltip_round_counter() : m.tooltip_round_counter_session()} > <span class="rounds"> {#if $settings.long_breaks_enabled} {snap.work_round_number} | {snap.work_rounds_total} {:else} {m.timer_session_round({ n: snap.session_work_count })} {/if} </span> </Tooltip>- 启用长休息时,显示形如 "2 / 4",tooltip 为
tooltip_round_counter——"Current work round out of the total rounds before a long break"(当前工作轮在长休息前总轮次中的位置); - 禁用长休息时,显示形如 "Round 3",tooltip 为
tooltip_round_counter_session——"Continuous session round count. Resets only when the timer is reset."(连续会话轮次计数,仅重置计时器时归零)。
六、设置窗口:TooltipInfo 挂载清单与平台守卫
规格文档(合并版)给出设置窗口的 info 图标清单。结合当前仓库源码,实际生效的挂载点如下:
| 设置项 | 所属分区 | i18n key | 说明 |
|---|---|---|---|
| Show in System Tray | System(托盘组) | system_tray_gnome_hint | 仅 Linux 渲染(isLinux守卫),复用既有 key |
| Verbose Logging | System(Advanced 日志组) | tooltip_verbose_logging | 说明开启详细调试日志的用途与日志位置 |
| WebSocket Server | System(集成组) | tooltip_websocket | 说明本地 WebSocket 服务的用途(如直播覆盖层) |
注:归档版 spec 还包含 Dial Countdown、Auto-start Work、Auto-start Break 三项(tooltip_dial_countdown、tooltip_auto_start_work、tooltip_auto_start_break),对应 key 在 src/messages/en.json 中仍存在,但在当前 src/lib/components/settings/sections/TimerSection.svelte 中尚未通过tooltip=prop 挂载——这些 key 属于既有 i18n 资产,可为后续 UI 复用。
6.1 GNOME 提示的 Linux-only 守卫
规格场景对 "Show in System Tray" 的守卫有明确要求:
- 在 Linux 上:
TooltipInfo图标出现在托盘开关标签旁; - 在 macOS / Windows 上:该开关不显示 info 图标。
源码实现位于 src/lib/components/settings/sections/SystemSection.svelte:
<SettingsToggle label={m.system_toggle_show_tray()} description={m.system_toggle_show_tray_desc()} tooltip={isLinux ? m.system_tray_gnome_hint() : undefined} checked={$settings.tray_icon_enabled} onclick={() => toggle('tray_icon_enabled', $settings.tray_icon_enabled)} />isLinux来自 src/lib/utils/platform.ts(/Linux/.test(navigator.userAgent))。当tooltip为undefined时,SettingsToggle 的{#if tooltip}分支不渲染 info 图标,从而实现平台条件化。
GNOME 提示的文案本身(en.json)是操作性的:提醒 GNOME 下托盘图标需要 AppIndicator 扩展,可通过包管理器安装gnome-shell-extension-appindicator或从 extensions.gnome.org 安装,然后重新登录生效。同时,规格要求移除原先对所有平台显示该文本的静态.note段落——当前 SystemSection 中已无该段落,仅剩 WebSocket 端口说明仍使用.note,印证了迁移完成。
6.2 设置窗口 info 图标即时显示
所有设置窗口的TooltipInfo都继承delay={0}行为——悬停即显,无延迟,与规格场景"Settings info icons show tooltip immediately"一致。
七、样式规范与主题适配
规格文档定义了 tooltip 的样式契约,最终实现(src/lib/components/Tooltip.svelte)逐项落实:
| 规格要求 | 实现值 |
|---|---|
| 背景色 | --color-background-light,未定义时使用计算回退:color-mix(in oklch, var(--color-foreground) 10%, var(--color-background)) |
| 文字颜色 | var(--color-foreground) |
| 字号 | 0.72rem,行高 1.4 |
| 圆角 | 4px |
| 最大宽度 | 240px,white-space: normal自动换行 |
| 视觉分隔 | box-shadow: 0 2px 8px color-mix(in oklch, black 30%, transparent)+ 1px 边框(前景色 12% 透明度混合) |
| 小箭头 | ::after伪元素实现,5px 三角,颜色取var(--tooltip-bg),--arrow-left控制水平位置 |
| 定位 | position: fixed,规避 overflow 容器裁剪;z-index: 9999 |
| 防遮挡 | pointer-events: none,tooltip 不拦截鼠标事件 |
7.1 主题联动机制
规格场景要求"用户切换主题时,任何可见 tooltip 立即反映新主题的 CSS 自定义属性"。这一目标通过两层设计达成:
- tooltip 的样式完全基于 CSS 自定义属性(
--color-background-light、--color-foreground等)构建,这些变量由主题系统注入到:root(例如 src/app.css 中定义--color-background-light: #3e4a5d等),主题切换本质上是替换这些变量; - 由于 tooltip 是内联渲染在触发元素旁的(sibling 节点),而非通过 portal 挂到
document.body之外,它天然继承当前作用域内的主题变量——切换主题后 CSS 变量重新求值,无需任何额外逻辑即可即时生效。
从源码结构看,主题适配还包含一层健壮性设计:--color-background-light未定义时,回退值color-mix(in oklch, var(--color-foreground) 10%, var(--color-background))基于前景色 10% 混合背景色计算,保证任何主题下 tooltip 背景与前景都有足够对比度。
八、无障碍:aria-describedby 接线
规格文档对无障碍有明确要求:
- 可见时:触发元素包装层设置
aria-describedby指向 tooltip 元素的id,屏幕阅读器在播报控件标签的同时播报 tooltip 文本; - 隐藏时:
aria-describedby属性必须不存在。
源码实现(src/lib/components/Tooltip.svelte):
<span class="tooltip-wrapper" bind:this={wrapper} onpointerenter={show} onpointermove={show} onpointerleave={hide} aria-describedby={visible ? tooltipId : undefined} > {@render children()} {#if visible} <span bind:this={tooltipEl} class="tooltip" class:below={actualPlacement === 'below'} class:positioned style={tooltipStyle} id={tooltipId} role="tooltip">{text}</span > {/if} </span>实现细节:
tooltipId使用运行时生成的唯一 ID(tooltip-${Math.random().toString(36).slice(2, 9)}),避免多实例 ID 冲突;aria-describedby仅在visible为 true 时设置(visible ? tooltipId : undefined),隐藏时属性被移除——与规格"隐藏时属性应不存在"完全一致;- tooltip 元素标注
role="tooltip",与aria-describedby配对构成标准的 ARIA tooltip 模式; - 模板中
<!-- svelte-ignore a11y_no_static_element_interactions -->注释用于豁免对静态 span 上 pointer 事件的 Svelte a11y 检查(触发通过指针事件而非键盘事件,这是 tooltip 的固有交互模型)。
九、i18n:全量文案与多语言覆盖
规格文档要求所有 tooltip 文案通过 Paraglide 翻译并由调用方传入,组件本身只接受翻译后的字符串。仓库的 8 个语言文件(src/messages/下的en.json、zh.json、fr.json、de.json、es.json、pt.json、ja.json、tr.json)均包含全部 tooltip key。
以 src/messages/en.json 为例,核心 tooltip key 全集如下(中文对照取自 src/messages/zh.json):
| key | 英文文案 | 中文文案 |
|---|---|---|
tooltip_settings | Open Settings | 打开设置 |
tooltip_statistics | Open Statistics | 打开统计 |
tooltip_restart_round | Restart the current round from the beginning. | 从头开始重启当前轮次。 |
tooltip_skip | Skip to the next round. | 跳过到下一轮。 |
tooltip_reset | Reset the timer to the first work round. Current session progress will be cleared. | 将计时器重置到第一个工作轮次。当前会话进度将被清除。 |
tooltip_mute | Mute alert sounds | 静音提示音 |
tooltip_unmute | Unmute alert sounds | 取消静音 |
tooltip_round_counter | Current work round out of the total rounds before a long break. | 当前工作轮次,显示在长休息前的总轮次中的位置。 |
tooltip_round_counter_session | Continuous session round count. Resets only when the timer is reset. | 连续会话轮次计数。仅在重置计时器时重置。 |
tooltip_verbose_logging | Enables detailed debug logging. Use when reporting issues. Log files are accessible via Open Log Folder in Settings → About. | 启用详细调试日志。用于报告问题时使用。日志文件可通过设置 → 关于中的"打开日志文件夹"访问。 |
tooltip_websocket | Enables a local WebSocket server for external integrations such as stream overlays. Disabled by default. | 启用本地 WebSocket 服务器,用于直播覆盖层等外部集成。默认禁用。 |
system_tray_gnome_hint | On GNOME, tray icons require the AppIndicator extension… | 在 GNOME 中,托盘图标需要安装 AppIndicator 扩展… |
设计文档(design.md)的 Risks 一节也坦诚记录了翻译质量风险:7 个非英语 locale 的 tooltip 字符串初期由机器翻译,可接受后续由社区改进。这是事实层面的诚实表述——tooltip 体系的价值上限直接取决于文案质量,长字符串在 240px 最大宽度下的换行效果也是未来可优化的点(design.md 的 Open Questions 提及此点)。
十、实现验证与工作流对照
归档任务清单 openspec/changes/archive/2026-03-26-ui-tooltips/tasks.md 完整记录了落地过程,可作为对照验证清单:
- 组件层:创建
Tooltip.svelte(props、slot、延迟定时器、视口翻转、aria 接线、主题样式)与TooltipInfo.svelte(delay={0}+ 样式化图标)——均已完成; - i18n 层:14 个 tooltip key 加入 en.json 并翻译到 7 个其他 locale——已完成;
- 计时器窗口:Restart Round、Skip、Reset、Mute/Unmute(状态条件)、Round indicator(模式条件)、Settings、Statistics 全部完成包裹;
- 设置窗口:GNOME 提示(Linux-only + 移除旧
.note)、Verbose Logging、WebSocket 均完成接入; - 验证项:两窗口 tooltip 显示/消失、窗口缩小后顶部翻转、Linux 构建显示 GNOME 提示而 macOS/Windows 不显示、
npm run check无 TypeScript 错误——均已勾选。
十一、设计取舍总结:这套 Tooltip 体系的可复用要点
从 design.md 的决策记录中,可以提炼出这套体系可复用的核心取舍:
- 组件包装优于
use:action:插槽模式让触发元素与 tooltip 的从属关系在模板中显式可见,且无障碍接线(aria-describedby指向动态 id)更易维护; - 双延迟模式:600ms 防止噪音、0ms 服务信息图标,两种交互语义清晰;
- 零依赖定位:
position: fixed+getBoundingClientRect()翻转检查,避开 Popper/Floating UI 这类库——对固定尺寸的小窗口足够,且保持依赖面扁平; - 内联定位而非 portal:避免 DOM 传送带来的 scoped CSS 复杂度;在 Pomotroid 的紧凑窗口下,配合 overflow 容器审查即可可靠工作(YearlyView 的热力图是唯一需要手写视口定位的特殊场景);
- 状态条件文案:Mute 与 Round indicator 的 tooltip 文本随应用状态动态切换,使 tooltip 始终描述"当前会发生的动作",而非静态功能描述——这是 tooltip 文案设计的高级实践;
- 后果前置文案:Reset 这类破坏性操作的 tooltip 明确告知"会清除会话进度",在用户点击前完成风险沟通。
如需在自有 Svelte 应用中复现这套模式,可直接参照 src/lib/components/Tooltip.svelte 与 src/lib/components/TooltipInfo.svelte 两个文件,配合 openspec/specs/tooltips/spec.md 的场景化契约即可逐条验证实现是否完备。
【免费下载链接】pomotroid
:tomato: Simple and visually-pleasing Pomodoro timer
相关推荐
OpenDesign Skeumorphism 设计系统包实战指南:契约文件、Token 体系与组件清单
OpenDesign Skeumorphism 设计系统包实战指南:契约文件、Token 体系与组件清单 导读 :本文以 design systems/skeu
AI 应用人工智能AI 技能设计系统媒体生成A2UI WebAppFrame 组件规范(v0.9)深度解读:沙箱化富交互组件的运行时契约与安全模型
A2UI WebAppFrame 组件规范(v0.9)深度解读:沙箱化富交互组件的运行时契约与安全模型 导读 本文基于仓库中的 WebAppFrame 组件规范
人工智能AI AgentAI 应用前端UI组件3步解决Milkdown编辑器Tooltip组件挂载难题:从配置到交互全攻略
3步解决Milkdown编辑器Tooltip组件挂载难题:从配置到交互全攻略 Milkdown作为一款插件驱动的所见即所得Markdown编辑器框架,其Tool
前端富文本插件系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考