dsh-plugin-term-dictionary 0.0.0-stage → 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/CHANGELOG.md +189 -0
- package/LICENSE +21 -0
- package/README.md +776 -2
- package/cordis.patch.yml +14 -0
- package/icon.svg +13 -0
- package/lib/ROADMAP-lexicon.md +52 -0
- package/lib/client.js +13354 -0
- package/lib/core/api.js +278 -0
- package/lib/core/bus.js +98 -0
- package/lib/core/copy.js +614 -0
- package/lib/core/core.js +309 -0
- package/lib/core/dictionary.js +1187 -0
- package/lib/core/entries.js +454 -0
- package/lib/core/highlight.js +282 -0
- package/lib/core/hover.js +470 -0
- package/lib/core/hovercard.js +173 -0
- package/lib/core/interact.js +1802 -0
- package/lib/core/lexicon.en.js +872 -0
- package/lib/core/lexicon.zh.js +249 -0
- package/lib/core/overlay.js +239 -0
- package/lib/core/pack.js +372 -0
- package/lib/core/package.json +4 -0
- package/lib/core/selection.js +83 -0
- package/lib/core/settings.js +366 -0
- package/lib/core/shell.js +1003 -0
- package/lib/core/stopwords.js +147 -0
- package/lib/core/store.js +397 -0
- package/lib/core/styles.js +574 -0
- package/lib/core/terms.js +398 -0
- package/lib/core/transfer.js +382 -0
- package/lib/core/views.js +2428 -0
- package/lib/index.js +1110 -0
- package/lib/pack-code.js +84 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +71 -3
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared inline styles for the panel and the popup.
|
|
5
|
+
*
|
|
6
|
+
* Only `--dsw-alias-*` and `--dsw-radius-*` theme tokens appear as colors, so the
|
|
7
|
+
* plugin follows light and dark themes without a second code path and degrades
|
|
8
|
+
* safely if a token is renamed. Geometry literals (padding, sizes) mirror the
|
|
9
|
+
* host's own management pages.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Font stack inherited from the host shell, with a CJK-capable fallback. */
|
|
13
|
+
const FONT = "inherit";
|
|
14
|
+
|
|
15
|
+
/** The panel and popup share one bordered-surface recipe. */
|
|
16
|
+
const surface = {
|
|
17
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
18
|
+
border: "0.5px solid var(--dsw-alias-border-l3)",
|
|
19
|
+
borderRadius: "var(--dsw-radius-md, 10px)"
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Text input recipe copied from the host's settings controls. */
|
|
23
|
+
const input = {
|
|
24
|
+
width: "100%",
|
|
25
|
+
boxSizing: "border-box",
|
|
26
|
+
font: FONT,
|
|
27
|
+
fontSize: "13px",
|
|
28
|
+
color: "var(--dsw-alias-label-primary)",
|
|
29
|
+
// The platform's own input fill. `--dsw-alias-bg-l1` (what this used to be) does
|
|
30
|
+
// not exist, and a `var()` with no fallback makes the whole declaration invalid at
|
|
31
|
+
// computed-value time — so the fields silently lost their background instead of
|
|
32
|
+
// falling back to anything.
|
|
33
|
+
background: "var(--dsw-specific-input-major, var(--dsw-alias-bg-base))",
|
|
34
|
+
border: "0.5px solid var(--dsw-alias-border-l3)",
|
|
35
|
+
borderRadius: "var(--dsw-radius-sm, 6px)",
|
|
36
|
+
padding: "6px 8px",
|
|
37
|
+
outline: "none"
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** The panel's search field, minus its icon gutter. */
|
|
41
|
+
const SEARCH_INPUT_STYLE = {
|
|
42
|
+
...input,
|
|
43
|
+
paddingLeft: "28px",
|
|
44
|
+
background: "transparent",
|
|
45
|
+
border: "none"
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Style table consumed by the views; kept as one object so tests can assert it. */
|
|
49
|
+
const applyStyles = {
|
|
50
|
+
panel: {
|
|
51
|
+
display: "flex",
|
|
52
|
+
flexDirection: "column",
|
|
53
|
+
height: "100%",
|
|
54
|
+
minHeight: 0,
|
|
55
|
+
boxSizing: "border-box",
|
|
56
|
+
color: "var(--dsw-alias-label-primary)",
|
|
57
|
+
font: FONT,
|
|
58
|
+
background: "var(--dsw-specific-sidebar-fill, transparent)"
|
|
59
|
+
},
|
|
60
|
+
header: {
|
|
61
|
+
display: "flex",
|
|
62
|
+
alignItems: "flex-start",
|
|
63
|
+
gap: "12px",
|
|
64
|
+
padding: "18px 20px 10px"
|
|
65
|
+
},
|
|
66
|
+
headerText: { display: "flex", flexDirection: "column", gap: "2px", minWidth: 0, flex: "1 1 auto" },
|
|
67
|
+
title: { margin: 0, fontSize: "15px", fontWeight: 600, lineHeight: 1.4 },
|
|
68
|
+
subtitle: { margin: 0, fontSize: "12px", color: "var(--dsw-alias-label-tertiary)", lineHeight: 1.4 },
|
|
69
|
+
searchRow: {
|
|
70
|
+
display: "flex",
|
|
71
|
+
alignItems: "center",
|
|
72
|
+
gap: "6px",
|
|
73
|
+
margin: "0 20px 8px",
|
|
74
|
+
padding: "0 8px",
|
|
75
|
+
border: "0.5px solid var(--dsw-alias-border-l3)",
|
|
76
|
+
borderRadius: "var(--dsw-radius-sm, 6px)",
|
|
77
|
+
background: "var(--dsw-specific-input-major, var(--dsw-alias-bg-base))"
|
|
78
|
+
},
|
|
79
|
+
searchIcon: { display: "flex", color: "var(--dsw-alias-label-tertiary)", flex: "none" },
|
|
80
|
+
tabs: { display: "flex", gap: "4px", padding: "0 20px 10px" },
|
|
81
|
+
tab: {
|
|
82
|
+
appearance: "none",
|
|
83
|
+
font: FONT,
|
|
84
|
+
fontSize: "12px",
|
|
85
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
86
|
+
background: "transparent",
|
|
87
|
+
border: "none",
|
|
88
|
+
borderRadius: "var(--dsw-radius-sm, 6px)",
|
|
89
|
+
padding: "3px 8px",
|
|
90
|
+
cursor: "pointer"
|
|
91
|
+
},
|
|
92
|
+
tabActive: {
|
|
93
|
+
color: "var(--dsw-alias-label-primary)",
|
|
94
|
+
background: "var(--dsw-alias-interactive-bg-hover)"
|
|
95
|
+
},
|
|
96
|
+
notice: {
|
|
97
|
+
display: "flex",
|
|
98
|
+
alignItems: "center",
|
|
99
|
+
gap: "8px",
|
|
100
|
+
margin: "0 20px 8px",
|
|
101
|
+
padding: "6px 10px",
|
|
102
|
+
fontSize: "12px",
|
|
103
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
104
|
+
background: "var(--dsw-alias-bg-layer-2, transparent)",
|
|
105
|
+
border: "0.5px solid var(--dsw-alias-border-l3)",
|
|
106
|
+
borderRadius: "var(--dsw-radius-sm, 6px)"
|
|
107
|
+
},
|
|
108
|
+
error: { margin: 0, fontSize: "12px", color: "var(--dsw-alias-state-error-primary)" },
|
|
109
|
+
list: { flex: "1 1 auto", minHeight: 0, overflowY: "auto", padding: "0 20px 16px", display: "flex", flexDirection: "column", gap: "8px" },
|
|
110
|
+
blank: { margin: "8px 0", fontSize: "13px", lineHeight: 1.6, color: "var(--dsw-alias-label-tertiary)" },
|
|
111
|
+
row: {
|
|
112
|
+
...surface,
|
|
113
|
+
display: "flex",
|
|
114
|
+
flexDirection: "column",
|
|
115
|
+
gap: "4px",
|
|
116
|
+
padding: "10px 12px",
|
|
117
|
+
// The whole block opens the entry, so say so before the click: a reader who has to discover
|
|
118
|
+
// that by trial will click the name — which is what they did.
|
|
119
|
+
cursor: "pointer"
|
|
120
|
+
},
|
|
121
|
+
/** The row a transcript click asked to reveal, before the cue fades. */
|
|
122
|
+
rowRevealed: {
|
|
123
|
+
borderColor: "var(--dsw-alias-brand-primary, #4d6bfe)",
|
|
124
|
+
boxShadow: "0 0 0 1px var(--dsw-alias-brand-primary, #4d6bfe)"
|
|
125
|
+
},
|
|
126
|
+
rowHead: { display: "flex", alignItems: "center", gap: "6px", flexWrap: "wrap" },
|
|
127
|
+
/** Where you are, and the way back up: a directory you can only enter is a trap. */
|
|
128
|
+
breadcrumb: { display: "flex", alignItems: "center", gap: "2px", flexWrap: "wrap", padding: "2px 0 6px", fontSize: "12px" },
|
|
129
|
+
breadcrumbPart: { display: "inline-flex", alignItems: "center", gap: "2px" },
|
|
130
|
+
breadcrumbSep: { color: "var(--dsw-alias-label-tertiary)", margin: "0 2px" },
|
|
131
|
+
breadcrumbButton: { appearance: "none", border: "none", background: "none", padding: "2px 4px", font: "inherit", fontSize: "12px", color: "var(--dsw-alias-brand-primary, #4d6bfe)", cursor: "pointer" },
|
|
132
|
+
breadcrumbHere: { appearance: "none", border: "none", background: "none", padding: "2px 4px", font: "inherit", fontSize: "12px", color: "var(--dsw-alias-label-secondary)", cursor: "default" },
|
|
133
|
+
/** The group row's own bits: the mark that says "not an entry", and the chevron that says "opens". */
|
|
134
|
+
groupIcon: { display: "inline-flex", alignItems: "center", color: "var(--dsw-alias-label-secondary)" },
|
|
135
|
+
groupEnter: { color: "var(--dsw-alias-label-tertiary)", fontSize: "14px", lineHeight: 1 },
|
|
136
|
+
/**
|
|
137
|
+
* The row's action toolbar: the second line, always.
|
|
138
|
+
*
|
|
139
|
+
* On the title line the controls moved from row to row with the length of the term and the number of
|
|
140
|
+
* badges; a line of their own puts every row's controls in the same place and keeps them out of the
|
|
141
|
+
* way of the text they act on.
|
|
142
|
+
*/
|
|
143
|
+
rowActions: { display: "flex", alignItems: "center", gap: "6px", marginTop: "2px" },
|
|
144
|
+
termButton: {
|
|
145
|
+
appearance: "none",
|
|
146
|
+
background: "transparent",
|
|
147
|
+
border: "none",
|
|
148
|
+
padding: 0,
|
|
149
|
+
font: FONT,
|
|
150
|
+
fontSize: "14px",
|
|
151
|
+
fontWeight: 600,
|
|
152
|
+
color: "var(--dsw-alias-label-primary)",
|
|
153
|
+
cursor: "pointer",
|
|
154
|
+
textAlign: "left"
|
|
155
|
+
},
|
|
156
|
+
zh: { fontSize: "13px", color: "var(--dsw-alias-label-secondary)" },
|
|
157
|
+
domain: { fontSize: "11px", color: "var(--dsw-alias-label-tertiary)", whiteSpace: "nowrap" },
|
|
158
|
+
spacer: { flex: "1 1 auto" },
|
|
159
|
+
badge: {
|
|
160
|
+
fontSize: "10px",
|
|
161
|
+
lineHeight: 1.6,
|
|
162
|
+
padding: "0 6px",
|
|
163
|
+
borderRadius: "999px",
|
|
164
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
165
|
+
background: "var(--dsw-alias-bg-base)",
|
|
166
|
+
border: "0.5px solid var(--dsw-alias-border-l2)"
|
|
167
|
+
},
|
|
168
|
+
badgeWarn: {
|
|
169
|
+
fontSize: "10px",
|
|
170
|
+
lineHeight: 1.6,
|
|
171
|
+
padding: "0 6px",
|
|
172
|
+
borderRadius: "999px",
|
|
173
|
+
color: "var(--dsw-alias-state-warn-primary, var(--dsw-alias-label-secondary))",
|
|
174
|
+
border: "0.5px dashed var(--dsw-alias-border-l3)"
|
|
175
|
+
},
|
|
176
|
+
gloss: { margin: 0, fontSize: "13px", lineHeight: 1.65, color: "var(--dsw-alias-label-secondary)" },
|
|
177
|
+
glossMuted: { margin: 0, fontSize: "13px", fontStyle: "italic", color: "var(--dsw-alias-label-tertiary)" },
|
|
178
|
+
usage: {
|
|
179
|
+
margin: 0,
|
|
180
|
+
fontSize: "12px",
|
|
181
|
+
lineHeight: 1.6,
|
|
182
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
183
|
+
paddingLeft: "8px",
|
|
184
|
+
borderLeft: "2px solid var(--dsw-alias-border-l2)"
|
|
185
|
+
},
|
|
186
|
+
meta: { margin: 0, fontSize: "11px", color: "var(--dsw-alias-label-caption, var(--dsw-alias-label-tertiary))" },
|
|
187
|
+
/**
|
|
188
|
+
* A context line, clamped to three lines.
|
|
189
|
+
*
|
|
190
|
+
* The editor renders its context directly under the title, so an unbounded context
|
|
191
|
+
* does not merely look untidy — it takes the whole panel and pushes the fields and
|
|
192
|
+
* the Save button out of reach. The collector and the selection path now bound what
|
|
193
|
+
* they store, and this clamp means even a legacy or imported entry with a huge
|
|
194
|
+
* context can never do that again.
|
|
195
|
+
*/
|
|
196
|
+
context: {
|
|
197
|
+
margin: 0,
|
|
198
|
+
fontSize: "12px",
|
|
199
|
+
lineHeight: 1.6,
|
|
200
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
201
|
+
display: "-webkit-box",
|
|
202
|
+
WebkitLineClamp: 3,
|
|
203
|
+
WebkitBoxOrient: "vertical",
|
|
204
|
+
overflow: "hidden",
|
|
205
|
+
overflowWrap: "anywhere"
|
|
206
|
+
},
|
|
207
|
+
storageNote: {
|
|
208
|
+
margin: 0,
|
|
209
|
+
padding: "6px 20px",
|
|
210
|
+
fontSize: "11px",
|
|
211
|
+
color: "var(--dsw-alias-label-caption, var(--dsw-alias-label-tertiary))",
|
|
212
|
+
overflowWrap: "anywhere"
|
|
213
|
+
},
|
|
214
|
+
footer: {
|
|
215
|
+
display: "flex",
|
|
216
|
+
alignItems: "center",
|
|
217
|
+
gap: "12px",
|
|
218
|
+
padding: "8px 20px 12px",
|
|
219
|
+
borderTop: "0.5px solid var(--dsw-alias-border-l2)",
|
|
220
|
+
fontSize: "11px",
|
|
221
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
222
|
+
},
|
|
223
|
+
linkButton: {
|
|
224
|
+
appearance: "none",
|
|
225
|
+
background: "transparent",
|
|
226
|
+
border: "none",
|
|
227
|
+
padding: 0,
|
|
228
|
+
font: FONT,
|
|
229
|
+
fontSize: "11px",
|
|
230
|
+
color: "var(--dsw-alias-brand-primary, var(--dsw-alias-label-secondary))",
|
|
231
|
+
cursor: "pointer"
|
|
232
|
+
},
|
|
233
|
+
iconButton: {
|
|
234
|
+
appearance: "none",
|
|
235
|
+
display: "inline-flex",
|
|
236
|
+
alignItems: "center",
|
|
237
|
+
justifyContent: "center",
|
|
238
|
+
width: "24px",
|
|
239
|
+
height: "24px",
|
|
240
|
+
padding: 0,
|
|
241
|
+
border: "none",
|
|
242
|
+
borderRadius: "var(--dsw-radius-sm, 6px)",
|
|
243
|
+
background: "transparent",
|
|
244
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
245
|
+
cursor: "pointer",
|
|
246
|
+
flex: "none"
|
|
247
|
+
},
|
|
248
|
+
iconButtonActive: {
|
|
249
|
+
color: "var(--dsw-alias-brand-primary, var(--dsw-alias-label-primary))",
|
|
250
|
+
background: "var(--dsw-alias-interactive-bg-hover)"
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
//#region the settings page and batch selection
|
|
254
|
+
/** The settings page: one preference per line, each with its own explanation. */
|
|
255
|
+
settingsPage: { padding: "0 20px 12px", display: "flex", flexDirection: "column", gap: "2px" },
|
|
256
|
+
/**
|
|
257
|
+
* One preference line.
|
|
258
|
+
*
|
|
259
|
+
* Was a wrapping row of chips under the tabs; the page has room to say what each preference
|
|
260
|
+
* DOES, and a row never did.
|
|
261
|
+
*/
|
|
262
|
+
settingRow: {
|
|
263
|
+
display: "flex",
|
|
264
|
+
alignItems: "flex-start",
|
|
265
|
+
justifyContent: "space-between",
|
|
266
|
+
gap: "12px",
|
|
267
|
+
padding: "8px 0",
|
|
268
|
+
borderBottom: "0.5px solid var(--dsw-alias-border-l2)"
|
|
269
|
+
},
|
|
270
|
+
/** The text half of a preference line. */
|
|
271
|
+
settingText: { display: "flex", flexDirection: "column", gap: "2px", minWidth: 0 },
|
|
272
|
+
settingLabel: { margin: 0, font: FONT, fontSize: "12px", color: "var(--dsw-alias-label-primary)" },
|
|
273
|
+
settingHint: { margin: 0, font: FONT, fontSize: "11px", lineHeight: 1.5, color: "var(--dsw-alias-label-tertiary)" },
|
|
274
|
+
/**
|
|
275
|
+
* A two-state preference: a track and a knob.
|
|
276
|
+
*
|
|
277
|
+
* The chip this replaces put its state in its own label ("收代码术语" / "不收代码术语"), so the
|
|
278
|
+
* row's NAME changed as you used it. A switch says the same thing without moving any text,
|
|
279
|
+
* which is what lets a settings row keep one name and one explanation.
|
|
280
|
+
*/
|
|
281
|
+
toggleTrack: {
|
|
282
|
+
appearance: "none",
|
|
283
|
+
position: "relative",
|
|
284
|
+
flex: "0 0 auto",
|
|
285
|
+
width: "34px",
|
|
286
|
+
height: "18px",
|
|
287
|
+
padding: 0,
|
|
288
|
+
border: "0.5px solid var(--dsw-alias-border-l2)",
|
|
289
|
+
borderRadius: "999px",
|
|
290
|
+
background: "var(--dsw-specific-input-major, var(--dsw-alias-bg-base))",
|
|
291
|
+
cursor: "pointer",
|
|
292
|
+
transition: "background-color 120ms ease, border-color 120ms ease"
|
|
293
|
+
},
|
|
294
|
+
toggleTrackOn: {
|
|
295
|
+
background: "var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary))",
|
|
296
|
+
borderColor: "transparent"
|
|
297
|
+
},
|
|
298
|
+
toggleKnob: {
|
|
299
|
+
position: "absolute",
|
|
300
|
+
top: "50%",
|
|
301
|
+
left: "2px",
|
|
302
|
+
width: "12px",
|
|
303
|
+
height: "12px",
|
|
304
|
+
marginTop: "-6px",
|
|
305
|
+
borderRadius: "999px",
|
|
306
|
+
background: "var(--dsw-alias-label-secondary)",
|
|
307
|
+
transition: "left 120ms ease, background-color 120ms ease"
|
|
308
|
+
},
|
|
309
|
+
toggleKnobOn: { left: "18px", background: "var(--dsw-alias-label-primary-inverted)" },
|
|
310
|
+
/**
|
|
311
|
+
* Three or more values: an option bar.
|
|
312
|
+
*
|
|
313
|
+
* Every value is visible at once and the chosen one is marked, so the control answers "what
|
|
314
|
+
* are my choices" as well as "what is it now". The cycling chip it replaces did neither
|
|
315
|
+
* without clicking: to learn the options you had to click until they came round again, and to
|
|
316
|
+
* reach one you clicked past the others.
|
|
317
|
+
*/
|
|
318
|
+
optionBar: {
|
|
319
|
+
display: "inline-flex",
|
|
320
|
+
flex: "0 0 auto",
|
|
321
|
+
gap: "2px",
|
|
322
|
+
padding: "2px",
|
|
323
|
+
border: "0.5px solid var(--dsw-alias-border-l2)",
|
|
324
|
+
borderRadius: "8px"
|
|
325
|
+
},
|
|
326
|
+
optionItem: {
|
|
327
|
+
appearance: "none",
|
|
328
|
+
border: "none",
|
|
329
|
+
background: "transparent",
|
|
330
|
+
font: FONT,
|
|
331
|
+
fontSize: "11px",
|
|
332
|
+
lineHeight: 1.6,
|
|
333
|
+
padding: "2px 8px",
|
|
334
|
+
borderRadius: "6px",
|
|
335
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
336
|
+
cursor: "pointer",
|
|
337
|
+
whiteSpace: "nowrap"
|
|
338
|
+
},
|
|
339
|
+
optionItemActive: {
|
|
340
|
+
background: "var(--dsw-alias-interactive-bg-hover)",
|
|
341
|
+
color: "var(--dsw-alias-label-primary)"
|
|
342
|
+
},
|
|
343
|
+
/**
|
|
344
|
+
* A delay: a slider plus a box for the exact number.
|
|
345
|
+
*
|
|
346
|
+
* One control in two shapes because the job is two jobs — sweeping to find a feel, and typing a
|
|
347
|
+
* value already in mind. The box is deliberately narrow: it holds three digits.
|
|
348
|
+
*/
|
|
349
|
+
delayField: { display: "inline-flex", flex: "0 0 auto", alignItems: "center", gap: "6px" },
|
|
350
|
+
delaySlider: { width: "110px", accentColor: "var(--dsw-alias-brand-primary, #4d6bfe)" },
|
|
351
|
+
delayBox: {
|
|
352
|
+
width: "52px",
|
|
353
|
+
font: FONT,
|
|
354
|
+
fontSize: "11px",
|
|
355
|
+
padding: "2px 4px",
|
|
356
|
+
textAlign: "right",
|
|
357
|
+
color: "var(--dsw-alias-label-primary)",
|
|
358
|
+
background: "var(--dsw-specific-input-major, var(--dsw-alias-bg-base))",
|
|
359
|
+
border: "0.5px solid var(--dsw-alias-border-l2)",
|
|
360
|
+
borderRadius: "6px"
|
|
361
|
+
},
|
|
362
|
+
delayUnit: { font: FONT, fontSize: "11px", color: "var(--dsw-alias-label-tertiary)" },
|
|
363
|
+
/** The bar that appears while rows can be picked. */
|
|
364
|
+
selectBar: {
|
|
365
|
+
display: "flex",
|
|
366
|
+
alignItems: "center",
|
|
367
|
+
gap: "8px",
|
|
368
|
+
flexWrap: "wrap",
|
|
369
|
+
margin: "0 20px 8px",
|
|
370
|
+
padding: "6px 10px",
|
|
371
|
+
fontSize: "12px",
|
|
372
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
373
|
+
background: "var(--dsw-alias-bg-layer-2, transparent)",
|
|
374
|
+
border: "0.5px solid var(--dsw-alias-border-l3)",
|
|
375
|
+
borderRadius: "var(--dsw-radius-sm, 6px)"
|
|
376
|
+
},
|
|
377
|
+
/** A row that is being picked rather than read. */
|
|
378
|
+
rowPicking: {
|
|
379
|
+
...surface,
|
|
380
|
+
display: "flex",
|
|
381
|
+
alignItems: "center",
|
|
382
|
+
gap: "8px",
|
|
383
|
+
padding: "8px 10px",
|
|
384
|
+
cursor: "pointer"
|
|
385
|
+
},
|
|
386
|
+
checkbox: {
|
|
387
|
+
width: "15px",
|
|
388
|
+
height: "15px",
|
|
389
|
+
flex: "none",
|
|
390
|
+
margin: 0,
|
|
391
|
+
// The host styles its own radios and checkboxes with exactly this property.
|
|
392
|
+
accentColor: "var(--dsw-alias-brand-primary)"
|
|
393
|
+
},
|
|
394
|
+
//#endregion
|
|
395
|
+
|
|
396
|
+
//#region the import/export page, and the deleted view
|
|
397
|
+
/** A row of controls on the transfer page: an option bar, or the download/copy pair. */
|
|
398
|
+
transferRow: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap", padding: "2px 0 8px" },
|
|
399
|
+
/** The category checklist. */
|
|
400
|
+
transferList: { display: "flex", flexDirection: "column", gap: "2px", padding: "0 0 8px" },
|
|
401
|
+
/**
|
|
402
|
+
* One selectable category.
|
|
403
|
+
*
|
|
404
|
+
* A `<label>`, so the whole line is the hit target rather than the 15px box: the checkbox is the
|
|
405
|
+
* control, and next to it the count is what decides which categories are worth exporting.
|
|
406
|
+
*/
|
|
407
|
+
transferItem: { display: "flex", alignItems: "center", gap: "8px", padding: "3px 0", fontSize: "12px", color: "var(--dsw-alias-label-primary)", cursor: "pointer" },
|
|
408
|
+
transferName: { flex: "1 1 auto", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
409
|
+
transferCount: { flex: "none", fontSize: "11px", color: "var(--dsw-alias-label-caption, var(--dsw-alias-label-tertiary))" },
|
|
410
|
+
/** The file picker. Chrome renders it; it only needs to sit with the rest of the page. */
|
|
411
|
+
transferFile: { font: FONT, fontSize: "12px", color: "var(--dsw-alias-label-secondary)", padding: "2px 0 6px" },
|
|
412
|
+
/**
|
|
413
|
+
* The name of an entry the user deleted.
|
|
414
|
+
*
|
|
415
|
+
* Struck through, because the row is the record of a decision rather than a term to read — and
|
|
416
|
+
* the row is the only place the two states are ever side by side.
|
|
417
|
+
*/
|
|
418
|
+
deletedTerm: { fontSize: "13px", color: "var(--dsw-alias-label-secondary)", textDecoration: "line-through" },
|
|
419
|
+
/** A term that is only being named, not offered as a control (the flagged rows). */
|
|
420
|
+
termPlain: { fontSize: "13px", fontWeight: 500, color: "var(--dsw-alias-label-primary)" },
|
|
421
|
+
/**
|
|
422
|
+
* The flagged view's own column.
|
|
423
|
+
*
|
|
424
|
+
* Same reason as the deleted view: the panel's list `gap` reaches only its direct children, and
|
|
425
|
+
* this view is a single child holding the hint and every row.
|
|
426
|
+
*/
|
|
427
|
+
flaggedList: { display: "flex", flexDirection: "column", gap: "8px" },
|
|
428
|
+
/**
|
|
429
|
+
* The deleted view's own column.
|
|
430
|
+
*
|
|
431
|
+
* The panel's list is a column too, but its `gap` only reaches its DIRECT children — and this view
|
|
432
|
+
* is one child holding the hint and every row, so without a column of its own the cards would sit
|
|
433
|
+
* flush against each other.
|
|
434
|
+
*/
|
|
435
|
+
deletedList: { display: "flex", flexDirection: "column", gap: "8px" },
|
|
436
|
+
//#endregion
|
|
437
|
+
|
|
438
|
+
editorBody: { flex: "1 1 auto", minHeight: 0, overflowY: "auto", padding: "0 20px 16px", display: "flex", flexDirection: "column", gap: "12px" },
|
|
439
|
+
field: { display: "flex", flexDirection: "column", gap: "4px" },
|
|
440
|
+
fieldLabel: { fontSize: "12px", color: "var(--dsw-alias-label-secondary)" },
|
|
441
|
+
input,
|
|
442
|
+
searchInput: SEARCH_INPUT_STYLE,
|
|
443
|
+
actions: { display: "flex", gap: "8px", flexWrap: "wrap", paddingTop: "4px" },
|
|
444
|
+
button: {
|
|
445
|
+
appearance: "none",
|
|
446
|
+
font: FONT,
|
|
447
|
+
fontSize: "13px",
|
|
448
|
+
color: "var(--dsw-alias-label-primary)",
|
|
449
|
+
background: "var(--dsw-alias-bg-base)",
|
|
450
|
+
border: "0.5px solid var(--dsw-alias-border-l3)",
|
|
451
|
+
borderRadius: "var(--dsw-radius-sm, 6px)",
|
|
452
|
+
padding: "5px 12px",
|
|
453
|
+
cursor: "pointer"
|
|
454
|
+
},
|
|
455
|
+
primaryButton: {
|
|
456
|
+
appearance: "none",
|
|
457
|
+
font: FONT,
|
|
458
|
+
fontSize: "13px",
|
|
459
|
+
fontWeight: 600,
|
|
460
|
+
// The host's own primary-button pair.
|
|
461
|
+
//
|
|
462
|
+
// This used to be `var(--dsw-alias-brand-primary)` with
|
|
463
|
+
// `var(--dsw-alias-label-inverse, #fff)`. Neither name is real: the design
|
|
464
|
+
// platform has no `--dsw-alias-label-inverse`, so the label fell back to white,
|
|
465
|
+
// and in the DARK theme `--dsw-alias-brand-primary` is
|
|
466
|
+
// `--dsw-static-neutral-bluish-50` — near-white, the same value the theme gives
|
|
467
|
+
// `--dsw-alias-label-primary`. The label and the fill were therefore both white
|
|
468
|
+
// and the Save button rendered as an empty rectangle.
|
|
469
|
+
//
|
|
470
|
+
// The values below are the ones the platform defines for exactly this job:
|
|
471
|
+
// `button-primary-fill` is the brand fill and `label-primary-inverted` is the
|
|
472
|
+
// text that sits on it — white-on-near-black in the light theme, dark-on-
|
|
473
|
+
// near-white in the dark one. Measured from the shipped theme CSS, not assumed.
|
|
474
|
+
color: "var(--dsw-alias-label-primary-inverted)",
|
|
475
|
+
background: "var(--dsw-alias-button-primary-fill)",
|
|
476
|
+
border: "0.5px solid transparent",
|
|
477
|
+
borderRadius: "var(--dsw-radius-sm, 6px)",
|
|
478
|
+
padding: "5px 12px",
|
|
479
|
+
cursor: "pointer"
|
|
480
|
+
},
|
|
481
|
+
|
|
482
|
+
//#region popup
|
|
483
|
+
popup: {
|
|
484
|
+
...surface,
|
|
485
|
+
position: "fixed",
|
|
486
|
+
zIndex: 2147483000,
|
|
487
|
+
width: "min(360px, calc(100vw - 24px))",
|
|
488
|
+
maxHeight: "min(60vh, 420px)",
|
|
489
|
+
overflowY: "auto",
|
|
490
|
+
boxShadow: "0 8px 28px rgba(0, 0, 0, 0.18)",
|
|
491
|
+
padding: "12px 14px",
|
|
492
|
+
display: "flex",
|
|
493
|
+
flexDirection: "column",
|
|
494
|
+
gap: "6px",
|
|
495
|
+
font: FONT,
|
|
496
|
+
color: "var(--dsw-alias-label-primary)"
|
|
497
|
+
},
|
|
498
|
+
popupHead: { display: "flex", alignItems: "baseline", gap: "8px", flexWrap: "wrap" },
|
|
499
|
+
popupTerm: { margin: 0, fontSize: "15px", fontWeight: 600, lineHeight: 1.3 },
|
|
500
|
+
popupZh: { fontSize: "13px", color: "var(--dsw-alias-label-secondary)" },
|
|
501
|
+
popupGloss: { margin: 0, fontSize: "13px", lineHeight: 1.7, color: "var(--dsw-alias-label-secondary)", whiteSpace: "pre-wrap" },
|
|
502
|
+
popupUsage: {
|
|
503
|
+
margin: 0,
|
|
504
|
+
fontSize: "12px",
|
|
505
|
+
lineHeight: 1.6,
|
|
506
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
507
|
+
paddingLeft: "8px",
|
|
508
|
+
borderLeft: "2px solid var(--dsw-alias-border-l2)",
|
|
509
|
+
whiteSpace: "pre-wrap"
|
|
510
|
+
},
|
|
511
|
+
popupMeta: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap", fontSize: "11px", color: "var(--dsw-alias-label-caption, var(--dsw-alias-label-tertiary))" },
|
|
512
|
+
popupActions: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap", paddingTop: "2px" },
|
|
513
|
+
popupContext: {
|
|
514
|
+
margin: 0,
|
|
515
|
+
fontSize: "12px",
|
|
516
|
+
lineHeight: 1.6,
|
|
517
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
518
|
+
overflowWrap: "anywhere"
|
|
519
|
+
},
|
|
520
|
+
//#endregion
|
|
521
|
+
|
|
522
|
+
//#region hover hint
|
|
523
|
+
//
|
|
524
|
+
// REMOVED with the hover gesture (2026-10-09). The pointer layer belongs to
|
|
525
|
+
// dsh-plugin-term-wikilink, which brings its own card styles; keeping this region would
|
|
526
|
+
// keep a second, unreachable definition of "what a hover card looks like" — and the two
|
|
527
|
+
// would drift the moment the inline styles change, which is precisely the confusion the
|
|
528
|
+
// merge is meant to end.
|
|
529
|
+
//#endregion
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
//#region selection affordance
|
|
533
|
+
selectionButton: {
|
|
534
|
+
...surface,
|
|
535
|
+
position: "fixed",
|
|
536
|
+
zIndex: 2147483001,
|
|
537
|
+
display: "inline-flex",
|
|
538
|
+
alignItems: "center",
|
|
539
|
+
gap: "6px",
|
|
540
|
+
padding: "5px 10px",
|
|
541
|
+
font: FONT,
|
|
542
|
+
fontSize: "12px",
|
|
543
|
+
color: "var(--dsw-alias-label-primary)",
|
|
544
|
+
boxShadow: "0 4px 14px rgba(0, 0, 0, 0.16)",
|
|
545
|
+
cursor: "pointer"
|
|
546
|
+
},
|
|
547
|
+
toast: {
|
|
548
|
+
...surface,
|
|
549
|
+
position: "fixed",
|
|
550
|
+
left: "50%",
|
|
551
|
+
bottom: "28px",
|
|
552
|
+
transform: "translateX(-50%)",
|
|
553
|
+
zIndex: 2147483002,
|
|
554
|
+
display: "flex",
|
|
555
|
+
alignItems: "center",
|
|
556
|
+
gap: "12px",
|
|
557
|
+
padding: "8px 14px",
|
|
558
|
+
font: FONT,
|
|
559
|
+
fontSize: "13px",
|
|
560
|
+
color: "var(--dsw-alias-label-primary)",
|
|
561
|
+
boxShadow: "0 6px 20px rgba(0, 0, 0, 0.18)",
|
|
562
|
+
// A NOTICE MUST NOT EAT INPUT.
|
|
563
|
+
//
|
|
564
|
+
// This sits at the bottom centre with the highest z-index in the plugin — which is exactly
|
|
565
|
+
// where the composer is. Without this line it swallowed every click in its rectangle for the
|
|
566
|
+
// six seconds it lived, so deleting an entry, importing, exporting or clearing the dictionary
|
|
567
|
+
// left the input box unusable until the notice timed out. The interactive parts opt back in
|
|
568
|
+
// (see the toast's own buttons), so undo and close still work.
|
|
569
|
+
pointerEvents: "none"
|
|
570
|
+
}
|
|
571
|
+
//#endregion
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
module.exports = { applyStyles, SEARCH_INPUT_STYLE, surface, input, FONT };
|