dsh-cait 1.0.4 → 1.1.3

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/client/entry.js CHANGED
@@ -14,6 +14,19 @@
14
14
  scaleCssLengths,
15
15
  caitUiBase: CAIT_UI_BASE,
16
16
  caitUiMaxUnit: CAIT_UI_MAX_UNIT,
17
+ caitUiDamping: CAIT_UI_DAMPING,
18
+ caitThemeAttr: CAIT_THEME_ATTR,
19
+ detectThemeKind,
20
+ normalizePageSize,
21
+ readPageSize,
22
+ writePageSize,
23
+ pageCount,
24
+ clampPage,
25
+ pageBounds,
26
+ segmentNumberToIndex,
27
+ pageSizeOptions: PAGE_SIZE_OPTIONS,
28
+ pageSizeDefault: PAGE_SIZE_DEFAULT,
29
+ pageSizeStorageKey: PAGE_SIZE_STORAGE_KEY,
17
30
  cssScaled: CSS_SCALED,
18
31
  syncPollMs: SYNC_POLL_MS,
19
32
  makeTranslator,
package/client/helpers.js CHANGED
@@ -1,5 +1,8 @@
1
1
  const API = '/cait/api/rpc'
2
2
  const SYNC_POLL_MS = 2000
3
+ // One pre-translation block is 8 segments: roughly 12 s with a warm route,
4
+ // short enough that one cancellation click never waits long.
5
+ const PRETRANSLATE_BLOCK_SIZE = 8
3
6
 
4
7
  // Only the last mounted library must stay checked. An unmounted library is
5
8
  // always selectable, even when that is currently the only mounted one.
@@ -43,14 +46,16 @@
43
46
  // --cait-u (a unitless multiplier) tracks the size of the panel.
44
47
  const CAIT_UNIT_VAR = '--cait-u'
45
48
  // The designed density is calibrated for roughly 1100×680 CSS pixels of
46
- // DSH panel area. Fullscreen/maximized windows on 1440p and 4K displays
47
- // were still capped at 1.4× with the old 1320×800 base, which left the
48
- // 13px body type visibly smaller than the surrounding DSH UI. Keep the
49
- // height term so short windows do not overflow, but let large panels grow
50
- // to 1.8× before the cap applies.
49
+ // DSH panel area. Growing linearly from there made a maximized window reach
50
+ // 1.8×, which is visibly larger than the surrounding DSH chrome and eats the
51
+ // horizontal budget the three-column review view needs. Growth is therefore
52
+ // damped and capped well below the old ceiling.
51
53
  const CAIT_UI_BASE = { width: 1100, height: 680 }
52
54
  const CAIT_UI_MIN_UNIT = 1
53
- const CAIT_UI_MAX_UNIT = 1.8
55
+ const CAIT_UI_MAX_UNIT = 1.45
56
+ // 0.6 keeps small and mid panels close to the old density while flattening
57
+ // the large end: 1920×1080 lands at 1.353, 2560 and 4K stop at 1.45.
58
+ const CAIT_UI_DAMPING = 0.6
54
59
 
55
60
  // Grow with the panel but never shrink below the designed size; the height
56
61
  // term keeps short/wide windows from becoming oversized and scroll-heavy.
@@ -59,7 +64,8 @@
59
64
  const h = Number(height)
60
65
  if (!(w > 0) || !(h > 0)) return CAIT_UI_MIN_UNIT
61
66
  const fit = Math.min(w / CAIT_UI_BASE.width, h / CAIT_UI_BASE.height)
62
- const unit = Math.min(CAIT_UI_MAX_UNIT, Math.max(CAIT_UI_MIN_UNIT, fit))
67
+ const grown = 1 + (fit - 1) * CAIT_UI_DAMPING
68
+ const unit = Math.min(CAIT_UI_MAX_UNIT, Math.max(CAIT_UI_MIN_UNIT, grown))
63
69
  return Math.round(unit * 1000) / 1000
64
70
  }
65
71
 
@@ -101,3 +107,100 @@
101
107
  }
102
108
  return output
103
109
  }
110
+
111
+ // --- theme --------------------------------------------------------------
112
+ // DSH marks the dark theme on <body>; the tokens handle every authored
113
+ // colour, but native widgets (select popups, checkboxes, scrollbars, caret)
114
+ // follow `color-scheme`, which tokens cannot reach. Mirror the host's theme
115
+ // onto our own root attribute and let CSS set the scheme from it.
116
+ const CAIT_THEME_ATTR = 'data-cait-theme'
117
+
118
+ function detectThemeKind(doc) {
119
+ const root = doc?.documentElement
120
+ const body = doc?.body
121
+ if (body?.hasAttribute?.('data-ds-dark-theme')) return 'dark'
122
+ const explicit = root?.getAttribute?.('data-theme') || body?.getAttribute?.('data-theme')
123
+ if (explicit === 'dark') return 'dark'
124
+ return 'light'
125
+ }
126
+
127
+ // --- paging -------------------------------------------------------------
128
+ // Both the segment rail and the library page page through server-side
129
+ // windows, so the arithmetic lives here: one place to clamp, one place to
130
+ // validate a typed segment number, one place to remember the page size.
131
+ const PAGE_SIZE_OPTIONS = [20, 50, 100, 200]
132
+ const PAGE_SIZE_DEFAULT = 50
133
+ const PAGE_SIZE_STORAGE_KEY = 'cait:pageSize'
134
+
135
+ function normalizePageSize(value) {
136
+ // A missing setting arrives as null (localStorage) or undefined, and
137
+ // Number(null) is 0, which would snap to the smallest page instead of the
138
+ // documented default.
139
+ if (value === null || value === undefined || value === '') return PAGE_SIZE_DEFAULT
140
+ const size = Number(value)
141
+ if (!Number.isFinite(size)) return PAGE_SIZE_DEFAULT
142
+ let best = PAGE_SIZE_OPTIONS[0]
143
+ let bestGap = Infinity
144
+ for (const option of PAGE_SIZE_OPTIONS) {
145
+ const gap = Math.abs(option - size)
146
+ if (gap < bestGap) {
147
+ best = option
148
+ bestGap = gap
149
+ }
150
+ }
151
+ return best
152
+ }
153
+
154
+ function readPageSize(storage) {
155
+ try {
156
+ return normalizePageSize(storage?.getItem?.(PAGE_SIZE_STORAGE_KEY))
157
+ } catch {
158
+ return PAGE_SIZE_DEFAULT
159
+ }
160
+ }
161
+
162
+ function writePageSize(storage, value) {
163
+ const size = normalizePageSize(value)
164
+ try {
165
+ storage?.setItem?.(PAGE_SIZE_STORAGE_KEY, String(size))
166
+ } catch {
167
+ // A blocked storage must not break paging; the in-memory value wins.
168
+ }
169
+ return size
170
+ }
171
+
172
+ function pageCount(total, pageSize) {
173
+ const size = normalizePageSize(pageSize)
174
+ const count = Number(total)
175
+ if (!Number.isFinite(count) || count <= 0) return 1
176
+ return Math.max(1, Math.ceil(count / size))
177
+ }
178
+
179
+ function clampPage(page, total, pageSize) {
180
+ const last = pageCount(total, pageSize)
181
+ const wanted = Number(page)
182
+ if (!Number.isFinite(wanted)) return 1
183
+ return Math.min(last, Math.max(1, Math.trunc(wanted)))
184
+ }
185
+
186
+ // Returns 1-based inclusive bounds plus the row count, so callers can print
187
+ // "x–y of n" without repeating the arithmetic.
188
+ function pageBounds(page, total, pageSize) {
189
+ const size = normalizePageSize(pageSize)
190
+ const count = Math.max(0, Number(total) || 0)
191
+ const current = clampPage(page, count, size)
192
+ const start = count === 0 ? 0 : (current - 1) * size + 1
193
+ const end = Math.min(count, current * size)
194
+ return { page: current, pageSize: size, start, end, total: count, pages: pageCount(count, size) }
195
+ }
196
+
197
+ // A translator types "42" meaning segment 42, so the input is 1-based; the
198
+ // return value is the 0-based index the store uses.
199
+ function segmentNumberToIndex(value, total) {
200
+ const text = String(value ?? '').trim()
201
+ if (!/^\d+$/.test(text)) return null
202
+ const number = Number(text)
203
+ const count = Number(total)
204
+ if (!Number.isFinite(count) || number < 1 || number > count) return null
205
+ return number - 1
206
+ }