tavern-harness 0.1.24 → 0.1.25

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/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
3
3
  > 本文件由 `scripts/changelog.mjs` 从 `changelog/v*.md` 生成,请勿手动编辑。
4
4
  > 版本对比链接见文末;源文件位于仓库 `changelog/` 目录。
5
5
 
6
+ ## [0.1.25] - 2026-10-27
7
+
8
+ ### 中文
9
+
10
+ #### 内部
11
+
12
+ - 统一了标题、对话框、工具卡片等界面文字的字体排印样式,改用共享的排版类和样式变量,后续调整字号、字重只需改一处
13
+
14
+ ### English
15
+
16
+ #### Internal
17
+
18
+ - Unified the typography of headings, dialogs, tool cards, and other interface text behind shared classes and style variables, so future font-size and weight changes happen in one place
19
+
6
20
  ## [0.1.24] - 2026-10-27
7
21
 
8
22
  ### 中文
@@ -702,6 +716,7 @@
702
716
  - Vite + React + Zustand + Dexie stack with browser-side local persistence
703
717
 
704
718
  <!-- 链接定义 -->
719
+ [0.1.25]: https://github.com/Rannichan/Tavern-Harness/compare/v0.1.24...v0.1.25
705
720
  [0.1.24]: https://github.com/Rannichan/Tavern-Harness/compare/v0.1.23...v0.1.24
706
721
  [0.1.23]: https://github.com/Rannichan/Tavern-Harness/compare/v0.1.22...v0.1.23
707
722
  [0.1.22]: https://github.com/Rannichan/Tavern-Harness/compare/v0.1.21...v0.1.22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tavern-harness",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "Tavern Harness — 酒馆模式下的本地 AI Agent 助手(MyAgent-Android 的 Web 复刻)",
5
5
  "private": false,
6
6
  "type": "module",
package/src/App.tsx CHANGED
@@ -165,7 +165,8 @@ export default function App() {
165
165
  type="button"
166
166
  disabled={takingControl}
167
167
  onClick={() => void handleTakeOverControl()}
168
- style={{ padding: '10px 18px', border: 0, borderRadius: 8, cursor: takingControl ? 'wait' : 'pointer', fontWeight: 700, background: 'color-mix(in srgb, var(--primary) 25%, transparent)', color: 'var(--primary)' }}
168
+ className="control-takeover-btn"
169
+ style={{ cursor: takingControl ? 'wait' : 'pointer' }}
169
170
  >
170
171
  {t('control.takeOverHere')}
171
172
  </button>
@@ -448,7 +449,7 @@ function SessionHeader({
448
449
  {confirmReset && (
449
450
  <Modal onClose={() => setConfirmReset(false)} size="compact">
450
451
  <div className="modal-head">
451
- <span style={{ fontWeight: 800, fontSize: 15 }}>{t('header.resetSessionTitle')}</span>
452
+ <span className="typo-dialog-title">{t('header.resetSessionTitle')}</span>
452
453
  <button className="icon-btn" onClick={() => setConfirmReset(false)}><Icon name="x" /></button>
453
454
  </div>
454
455
  <div className="modal-body">
@@ -473,7 +474,7 @@ function SessionHeader({
473
474
  {confirmDelete && (
474
475
  <Modal onClose={() => setConfirmDelete(false)} size="compact">
475
476
  <div className="modal-head">
476
- <span style={{ fontWeight: 800, fontSize: 15 }}>{t('header.deleteTitle')}</span>
477
+ <span className="typo-dialog-title">{t('header.deleteTitle')}</span>
477
478
  <button className="icon-btn" onClick={() => setConfirmDelete(false)}><Icon name="x" /></button>
478
479
  </div>
479
480
  <div className="modal-body">
@@ -316,7 +316,7 @@ function CharacterEditorModal({ npc, isNew, onClose, onSaved }: { npc: NpcCharac
316
316
  return (
317
317
  <Modal onClose={onClose} className="modal-editor editor-modal-with-tabs character-editor-modal">
318
318
  <div className="modal-head">
319
- <span style={{ fontWeight: 800, fontSize: 15 }}>{isNew ? t('workshop.newCharTitle') : t('workshop.editCharTitle')}</span>
319
+ <span className="typo-dialog-title">{isNew ? t('workshop.newCharTitle') : t('workshop.editCharTitle')}</span>
320
320
  <div className="raw-tabs">
321
321
  <button className={`raw-tab ${editorTab === 'character' ? 'active' : ''}`} onClick={() => setEditorTab('character')}>{t('workshop.characterTab')}</button>
322
322
  <button
@@ -371,7 +371,7 @@ function CharacterEditorModal({ npc, isNew, onClose, onSaved }: { npc: NpcCharac
371
371
  <textarea className="textarea grow-textarea" value={prompt} onChange={(e) => setPrompt(e.target.value)} placeholder={t('workshop.promptPh')} />
372
372
  ) : (
373
373
  <div className="bubble preview-scroll wb-render-pane char-prompt-render-pane">
374
- {prompt ? <Markdown text={prompt} /> : <span style={{ color: 'var(--text-faint)', fontSize: 13 }}>{t('workshop.promptPh')}</span>}
374
+ {prompt ? <Markdown text={prompt} /> : <span className="typo-label text-muted">{t('workshop.promptPh')}</span>}
375
375
  </div>
376
376
  )}
377
377
  </div>
@@ -391,7 +391,7 @@ function CharacterEditorModal({ npc, isNew, onClose, onSaved }: { npc: NpcCharac
391
391
  />
392
392
  ) : (
393
393
  <div className="bubble preview-scroll wb-render-pane greeting-render-pane">
394
- {g ? <Markdown text={g} /> : <span style={{ color: 'var(--text-faint)', fontSize: 13 }}>{t('workshop.greetingPh')}</span>}
394
+ {g ? <Markdown text={g} /> : <span className="typo-label text-muted">{t('workshop.greetingPh')}</span>}
395
395
  </div>
396
396
  )}
397
397
  {!renderMarkdown && (
@@ -415,7 +415,7 @@ function CharacterEditorModal({ npc, isNew, onClose, onSaved }: { npc: NpcCharac
415
415
  <div className="field" style={{ flexShrink: 0 }}>
416
416
  <label>{t('workshop.enableSkills', { a: enabledToolNames.filter((s) => tools.some((t) => t.name === s)).length, b: tools.length })}</label>
417
417
  {tools.length === 0 ? (
418
- <div style={{ fontSize: 12, color: 'var(--text-faint)' }}>{t('workshop.noSkills')}</div>
418
+ <div className="typo-label text-muted">{t('workshop.noSkills')}</div>
419
419
  ) : (
420
420
  <div className="skill-list-scroll">
421
421
  {tools.map((t) => (
@@ -595,7 +595,7 @@ function WorldBookList({ books, onChanged }: {
595
595
  return (
596
596
  <div key={b.id} className="char-card wb-card fade-up">
597
597
  <div className="cname">
598
- <span style={{ fontSize: 16 }}>📖</span>
598
+ <span className="character-book-icon">📖</span>
599
599
  <ScrollingCardName name={b.name} />
600
600
  {isBuiltin && <span className="tag">{t('common.builtin')}</span>}
601
601
  </div>
@@ -712,7 +712,7 @@ function WorldBookForm({
712
712
  return (
713
713
  <>
714
714
  <div className="modal-head">
715
- <span style={{ fontWeight: 800, fontSize: 15 }}>
715
+ <span className="typo-dialog-title">
716
716
  {isEditing ? t('workshop.editWbTitle') : t('workshop.newWbTitle')}
717
717
  </span>
718
718
  <div className="raw-tabs">
@@ -755,7 +755,7 @@ function WorldBookForm({
755
755
  <textarea className="textarea grow-textarea" value={content} onChange={(e) => setContent(e.target.value)} placeholder={t('workshop.wbContentPh')} />
756
756
  ) : (
757
757
  <div className="bubble preview-scroll wb-render-pane">
758
- {content ? <Markdown text={content} /> : <span style={{ color: 'var(--text-faint)', fontSize: 13 }}>{t('workshop.wbContentPh')}</span>}
758
+ {content ? <Markdown text={content} /> : <span className="typo-label text-muted">{t('workshop.wbContentPh')}</span>}
759
759
  </div>
760
760
  )}
761
761
  </div>
@@ -960,14 +960,14 @@ function SkillSortItem({
960
960
  {...attributes}
961
961
  {...listeners}
962
962
  >
963
- <div style={{ fontSize: 18 }}>{tool.isBuiltIn ? '🧰' : '⚙️'}</div>
963
+ <div className="tool-type-icon">{tool.isBuiltIn ? '🧰' : '⚙️'}</div>
964
964
  <div className="l-main">
965
965
  <div className="l-title">
966
966
  <span className="skill-card-name">{tool.name}</span>
967
967
  {tool.isBuiltIn && <span className="tag">{t('common.builtin')}</span>}
968
968
  </div>
969
969
  <div className="l-sub">{desc}</div>
970
- {tool.isBuiltIn && impl !== 'native' && <div style={{ fontSize: 10.5, marginTop: 2, color: 'var(--warn)' }}>{t('workshop.implType', { t: impl })}</div>}
970
+ {tool.isBuiltIn && impl !== 'native' && <div className="tool-implementation-type">{t('workshop.implType', { t: impl })}</div>}
971
971
  </div>
972
972
  <div className="skill-item-actions">
973
973
  <button className="btn btn-sm" onClick={(e) => { e.stopPropagation(); onDetail(); }} title={t('workshop.viewDetails')}>
@@ -1063,7 +1063,7 @@ function SkillDetailModal({ tool, onClose }: { tool: McpTool; onClose: () => voi
1063
1063
  return (
1064
1064
  <Modal onClose={onClose}>
1065
1065
  <div className="modal-head">
1066
- <span style={{ fontWeight: 800, fontSize: 15 }}>
1066
+ <span className="typo-dialog-title">
1067
1067
  {t('workshop.skillDetails')}
1068
1068
  {tool.isBuiltIn && <span className="tag" style={{ marginLeft: 8 }}>{t('common.builtin')}</span>}
1069
1069
  {!tool.isBuiltIn && execType !== 'native' && <span className="tag" style={{ marginLeft: 8 }}>{t('workshop.implType', { t: execType })}</span>}
@@ -1081,17 +1081,17 @@ function SkillDetailModal({ tool, onClose }: { tool: McpTool; onClose: () => voi
1081
1081
  </div>
1082
1082
  <div className="field">
1083
1083
  <label>{t('workshop.skillParameters')}</label>
1084
- {editable ? <textarea className="textarea mono skill-detail-textarea" value={parametersText} wrap="soft" spellCheck={false} onChange={(e) => setParametersText(e.target.value)} /> : parametersText ? <pre className="skill-detail-json">{parametersText}</pre> : <div className="skill-detail-text" style={{ color: 'var(--text-faint)' }}>{t('workshop.skillNoParams')}</div>}
1084
+ {editable ? <textarea className="textarea mono skill-detail-textarea" value={parametersText} wrap="soft" spellCheck={false} onChange={(e) => setParametersText(e.target.value)} /> : parametersText ? <pre className="skill-detail-json">{parametersText}</pre> : <div className="skill-detail-text text-muted">{t('workshop.skillNoParams')}</div>}
1085
1085
  </div>
1086
1086
  <div className="field">
1087
1087
  <label>{t('workshop.skillExecution')}</label>
1088
- {exec ? <pre className="skill-detail-json">{JSON.stringify(exec, null, 2)}</pre> : <div className="skill-detail-text" style={{ color: 'var(--text-faint)' }}>{t('workshop.skillNoExecution')}</div>}
1088
+ {exec ? <pre className="skill-detail-json">{JSON.stringify(exec, null, 2)}</pre> : <div className="skill-detail-text text-muted">{t('workshop.skillNoExecution')}</div>}
1089
1089
  </div>
1090
1090
  <div className="field">
1091
1091
  <label>{t('workshop.skillRawJson')}</label>
1092
1092
  <pre className={`skill-detail-json${rawJson ? '' : ' err'}`}>{rawJson ?? schema.error}</pre>
1093
1093
  </div>
1094
- {error && <div className="field-hint" style={{ color: 'var(--danger)' }}>{error}</div>}
1094
+ {error && <div className="field-hint text-danger">{error}</div>}
1095
1095
  </div>
1096
1096
  <div className="modal-foot">
1097
1097
  <button className="btn" onClick={onClose}>{t('common.cancel')}</button>
@@ -1465,7 +1465,7 @@ export function MessageMenu() {
1465
1465
  <Icon name={a.icon} size={13} /> {a.label}
1466
1466
  </button>
1467
1467
  ))}
1468
- {actions.length === 0 && <div style={{ padding: 10, fontSize: 12, color: 'var(--text-faint)' }}>{t('chat.noActions')}</div>}
1468
+ {actions.length === 0 && <div className="chat-no-actions">{t('chat.noActions')}</div>}
1469
1469
  </PopoverMenu>
1470
1470
  );
1471
1471
  }
@@ -23,16 +23,16 @@ export function ConfirmationDialog() {
23
23
  <div className="modal-root">
24
24
  <div className="modal card confirm fade-up">
25
25
  <div className="modal-head">
26
- <span style={{ fontWeight: 800, fontSize: 14, display: 'flex', alignItems: 'center', gap: 8 }}>
26
+ <span className="typo-dialog-title dialog-title-inline dialog-title-compact">
27
27
  <Icon name="settings" size={15} /> {isLimit ? pending.title : t('confirm.title')}
28
28
  </span>
29
29
  </div>
30
30
  <div className="modal-body" style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
31
- {!isLimit && <div style={{ fontSize: 13, fontWeight: 700 }}>{t('confirm.tool')}<span className="mono" style={{ color: 'var(--primary)' }}>{pending.toolName}</span></div>}
31
+ {!isLimit && <div className="confirmation-tool-name">{t('confirm.tool')}<span className="mono text-accent">{pending.toolName}</span></div>}
32
32
  {!pending.inlineCode && <pre className="confirm-args mono">{argsText}</pre>}
33
33
  <div className={isLimit ? undefined : 'confirm-reason'}>
34
34
  {!isLimit && <div className="confirm-reason-title">{pending.title}</div>}
35
- <div style={{ fontSize: 13, color: 'var(--text-dim)', lineHeight: 1.6, whiteSpace: 'pre-line' }}>
35
+ <div className="typo-confirm-reason">
36
36
  {pending.inlineCode && <code className="confirm-inline-code">{pending.inlineCode}</code>}
37
37
  {pending.message || (isLimit ? '' : t('confirm.modify'))}
38
38
  </div>
@@ -25,7 +25,7 @@ export function DeleteConfirmDialog({
25
25
  return (
26
26
  <Modal onClose={onCancel} size="compact">
27
27
  <div className="modal-head">
28
- <span style={{ fontWeight: 800, fontSize: 15 }}>{title}</span>
28
+ <span className="typo-dialog-title">{title}</span>
29
29
  <button className="icon-btn" onClick={onCancel}><Icon name="x" /></button>
30
30
  </div>
31
31
  <div className="modal-body">
@@ -466,7 +466,7 @@ export function FileDisplayModal() {
466
466
  >
467
467
  <span className="display-modal-title">
468
468
  <Icon name="file" size={15} />
469
- <span className="mono" style={{ fontSize: 12.5, color: 'var(--text-dim)' }}>{active.path}</span>
469
+ <span className="mono typo-body-sm text-secondary">{active.path}</span>
470
470
  <span className="display-title-text">{title}</span>
471
471
  </span>
472
472
  <div className="display-head-actions">
@@ -50,7 +50,7 @@ export function GameplayExportModal({
50
50
  return (
51
51
  <Modal onClose={onClose} size="compact">
52
52
  <div className="modal-head">
53
- <span style={{ fontWeight: 800, fontSize: 15, display: 'flex', alignItems: 'center', gap: 8 }}>
53
+ <span className="typo-dialog-title dialog-title-inline">
54
54
  <Icon name="share" size={15} /> {t('gameplay.exportTitle')}
55
55
  </span>
56
56
  <button className="icon-btn" onClick={onClose}><Icon name="x" /></button>
@@ -155,7 +155,7 @@ export function GameplayImportModal({
155
155
  return (
156
156
  <Modal onClose={onClose} size="compact">
157
157
  <div className="modal-head">
158
- <span style={{ fontWeight: 800, fontSize: 15, display: 'flex', alignItems: 'center', gap: 8 }}>
158
+ <span className="typo-dialog-title dialog-title-inline">
159
159
  <Icon name="import" size={15} /> {t('gameplay.importTitle')}
160
160
  </span>
161
161
  <button className="icon-btn" onClick={onClose}><Icon name="x" /></button>
@@ -160,7 +160,7 @@ export function NewSessionMenu({ onClose, editingSession }: { onClose: () => voi
160
160
  return (
161
161
  <Modal onClose={onClose} className="new-session-modal">
162
162
  <div className="modal-head">
163
- <span style={{ fontWeight: 800, fontSize: 15 }}>{isEditing ? t('nav.editSession') : t('newSession.title')}</span>
163
+ <span className="typo-dialog-title">{isEditing ? t('nav.editSession') : t('newSession.title')}</span>
164
164
  <button className="icon-btn" onClick={onClose}><Icon name="x" /></button>
165
165
  </div>
166
166
  <div className="modal-body" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
@@ -203,7 +203,7 @@ export function NewSessionMenu({ onClose, editingSession }: { onClose: () => voi
203
203
  </SortableContext>
204
204
  </DndContext>
205
205
  {npcs.length === 0 ? (
206
- <div style={{ fontSize: 12.5, color: 'var(--warn)', background: 'var(--warn-soft)', padding: '8px 12px', borderRadius: 9 }}>
206
+ <div className="new-session-warning">
207
207
  {t('newSession.noNpcWarn')}
208
208
  </div>
209
209
  ) : (
@@ -323,7 +323,7 @@ export const SettingsView = memo(function SettingsView() {
323
323
  {showResetConfirm && (
324
324
  <Modal onClose={() => !resetting && setShowResetConfirm(false)} size="compact">
325
325
  <div className="modal-head">
326
- <span style={{ fontWeight: 800, fontSize: 15 }}>{t('settings.resetConfirmTitle')}</span>
326
+ <span className="typo-dialog-title">{t('settings.resetConfirmTitle')}</span>
327
327
  <button className="icon-btn" disabled={resetting} onClick={() => setShowResetConfirm(false)}><Icon name="x" /></button>
328
328
  </div>
329
329
  <div className="modal-body">{t('settings.resetConfirm')}</div>
@@ -435,7 +435,7 @@ function ProviderManager({ providers, onChanged }: { providers: ApiProvider[]; o
435
435
  <div className="set-section">
436
436
  <h3>{t('settings.providerTitle')}</h3>
437
437
  <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
438
- {providers.length === 0 && <div style={{ color: 'var(--text-faint)', fontSize: 12.5 }}>{t('settings.noProvider')}</div>}
438
+ {providers.length === 0 && <div className="typo-body-sm text-muted">{t('settings.noProvider')}</div>}
439
439
  {providers.map((p) => {
440
440
  const aliases = modelAliases(p);
441
441
  const enabledModels = enabledModelIds(p);
@@ -457,7 +457,7 @@ function ProviderManager({ providers, onChanged }: { providers: ApiProvider[]; o
457
457
  </div>
458
458
  </div>
459
459
  <div className="p-url">{p.baseUrl}</div>
460
- <div style={{ fontSize: 11.5, color: 'var(--text-faint)', maxHeight: 60, overflow: 'auto' }}>
460
+ <div className="provider-inline-detail">
461
461
  <b>{t('settings.models')}</b>
462
462
  {visibleModels.length > 0
463
463
  ? visibleModels.slice(0, 12).map((model) => aliases[model]?.trim() || model).join(', ')
@@ -475,7 +475,7 @@ function ProviderManager({ providers, onChanged }: { providers: ApiProvider[]; o
475
475
  {editing && (
476
476
  <Modal onClose={() => setEditing(null)}>
477
477
  <div className="modal-head">
478
- <span style={{ fontWeight: 800, fontSize: 15 }}>{editing.id != null ? t('settings.editProvider') : t('settings.addProvider')}</span>
478
+ <span className="typo-dialog-title">{editing.id != null ? t('settings.editProvider') : t('settings.addProvider')}</span>
479
479
  <button className="icon-btn" onClick={() => setEditing(null)}><Icon name="x" /></button>
480
480
  </div>
481
481
  <ProviderForm
@@ -527,13 +527,13 @@ export function Sidebar() {
527
527
  {confirmDelete && (
528
528
  <Modal onClose={() => setConfirmDelete(null)} size="compact">
529
529
  <div className="modal-head">
530
- <span style={{ fontWeight: 800, fontSize: 15, display: 'flex', alignItems: 'center', gap: 8 }}>
530
+ <span className="typo-dialog-title dialog-title-inline">
531
531
  <Icon name="trash" size={15} /> {t('nav.deleteSession')}
532
532
  </span>
533
533
  <button className="icon-btn" onClick={() => setConfirmDelete(null)}><Icon name="x" /></button>
534
534
  </div>
535
535
  <div className="modal-body">
536
- <div style={{ fontSize: 13.5, lineHeight: 1.7 }}>
536
+ <div className="sidebar-import-description">
537
537
  {t('nav.deleteSessionConfirm', { title: confirmDelete.title })}
538
538
  </div>
539
539
  </div>
@@ -134,18 +134,18 @@ function StatCards({ data }: { data: StatsData }) {
134
134
  {(() => {
135
135
  const mostActive = [...data.npcStats].sort((a, b) => b.rounds - a.rounds)[0];
136
136
  return (
137
- <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
138
- <div style={{ fontSize: 26 }}>🎭</div>
137
+ <div className="stats-most-active">
138
+ <div className="stats-most-active-icon">🎭</div>
139
139
  <div>
140
- <div style={{ fontWeight: 800 }}>{mostActive.npcName}</div>
141
- <div style={{ fontSize: 12, color: 'var(--text-faint)' }}>{t('stats.roundsCount', { n: mostActive.rounds })}</div>
140
+ <div className="stats-most-active-name">{mostActive.npcName}</div>
141
+ <div className="stats-most-active-rounds">{t('stats.roundsCount', { n: mostActive.rounds })}</div>
142
142
  </div>
143
143
  </div>
144
144
  );
145
145
  })()}
146
146
  </>
147
147
  ) : (
148
- <div style={{ color: 'var(--text-faint)', fontSize: 13 }}>{t('stats.noData')}</div>
148
+ <div className="stats-no-data">{t('stats.noData')}</div>
149
149
  )}
150
150
  </div>
151
151
 
@@ -93,7 +93,7 @@ export function VersionHistoryModal({ onClose }: { onClose: () => void }) {
93
93
  return (
94
94
  <Modal onClose={onClose} size="wide">
95
95
  <div className="modal-head">
96
- <span style={{ fontWeight: 800, fontSize: 15 }}>{t('changelog.title')}</span>
96
+ <span className="typo-dialog-title">{t('changelog.title')}</span>
97
97
  <button className="icon-btn" onClick={onClose} aria-label={t('common.close')}>
98
98
  <Icon name="x" />
99
99
  </button>
@@ -241,7 +241,7 @@ export function WorkspaceFileManagerModal({ sessionId, onClose }: { sessionId: n
241
241
  <div className="ws-fm-delete-layer" role="dialog" aria-modal="true" aria-labelledby="ws-fm-delete-title">
242
242
  <div className="ws-fm-delete-dialog card fade-up">
243
243
  <div className="modal-head">
244
- <span id="ws-fm-delete-title" style={{ fontWeight: 800, fontSize: 15 }}>{t('header.fileManagerDeleteTitle')}</span>
244
+ <span id="ws-fm-delete-title" className="typo-dialog-title">{t('header.fileManagerDeleteTitle')}</span>
245
245
  <button className="icon-btn" onClick={() => setPendingDelete(null)} disabled={deleting} title={t('common.close')}>
246
246
  <Icon name="x" />
247
247
  </button>
@@ -1,7 +1,31 @@
1
1
  {
2
- "latest": "0.1.24",
2
+ "latest": "0.1.25",
3
3
  "repo": "https://github.com/Rannichan/Tavern-Harness",
4
4
  "entries": [
5
+ {
6
+ "version": "0.1.25",
7
+ "date": "2026-10-27",
8
+ "breaking": false,
9
+ "compare": "v0.1.24...v0.1.25",
10
+ "groups": {
11
+ "zh-CN": [
12
+ {
13
+ "kind": "internal",
14
+ "items": [
15
+ "统一了标题、对话框、工具卡片等界面文字的字体排印样式,改用共享的排版类和样式变量,后续调整字号、字重只需改一处"
16
+ ]
17
+ }
18
+ ],
19
+ "en": [
20
+ {
21
+ "kind": "internal",
22
+ "items": [
23
+ "Unified the typography of headings, dialogs, tool cards, and other interface text behind shared classes and style variables, so future font-size and weight changes happen in one place"
24
+ ]
25
+ }
26
+ ]
27
+ }
28
+ },
5
29
  {
6
30
  "version": "0.1.24",
7
31
  "date": "2026-10-27",