dsh-theme-gallery 0.1.4 → 0.1.5
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/README.md +26 -0
- package/lib/client.js +182 -31
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -114,6 +114,32 @@ dsh plugin --profile web add dsh-theme-gallery
|
|
|
114
114
|
只在你想跟源码时用它。本包**没有构建步骤**,所以不会出现"缺 `lib/` 目录"那类失败;
|
|
115
115
|
它的代价是 pnpm 可能要求你为构建脚本授权(`allowBuilds`)。
|
|
116
116
|
|
|
117
|
+
### 更新到新版本
|
|
118
|
+
|
|
119
|
+
**先说清楚:DSH 桌面版本身不会提示第三方插件有更新**,所以「发现新版」和「执行更新」这两件事得靠下面的渠道。
|
|
120
|
+
|
|
121
|
+
**怎么知道有新版本?**
|
|
122
|
+
|
|
123
|
+
| 渠道 | 说明 |
|
|
124
|
+
|---|---|
|
|
125
|
+
| **插件市场** | 装 [DSH-Plugins-Marketplace](https://github.com/bradeGithub/DSH-Plugins-Marketplace) 后,卡片会显示 **「已装 v0.1.4 → v0.1.6」** 并给**更新**按钮 —— 目前最省事的路径 |
|
|
126
|
+
| **GitHub Releases** | 在本仓库点 **Watch → Custom → Releases only**,有新版本会收到邮件 |
|
|
127
|
+
| **对照版本号** | 插件面板右上角显示**你装的版本**(如 `v0.1.4`),与 [npm 页面](https://www.npmjs.com/package/dsh-theme-gallery) 上的版本号直接对照 |
|
|
128
|
+
|
|
129
|
+
**怎么更新?**
|
|
130
|
+
|
|
131
|
+
1. **设置 → 插件 → 添加插件** → 再填一次 `dsh-theme-gallery` → **重启**(装上 `^x.y.z` 范围内的最新版)
|
|
132
|
+
2. 想要**强制拿最新**(含 0.1.x → 0.2.0 这类次版本号变更):**先移除、再添加**
|
|
133
|
+
3. 装了市场的:点卡片上的**更新**按钮
|
|
134
|
+
4. Web / CLI profile:`dsh plugin --profile web add dsh-theme-gallery`
|
|
135
|
+
|
|
136
|
+
**版本范围语义**:安装时会记录成 **`^0.1.4`**,即 `>=0.1.4 <0.2.0` ——
|
|
137
|
+
`0.1.5` / `0.1.9` 这类补丁会在重装时自动带上;**`0.2.0` 不会**(npm 对 `0.x` 的惯例是
|
|
138
|
+
把次版本号变更视为可能破坏性),那种情况按第 2 条显式重装即可。
|
|
139
|
+
|
|
140
|
+
> ⚠️ 从 **Release 的 `.tgz`** 安装的用户**没有更新机制**(tgz 是一次性快照,只能重新下载)——
|
|
141
|
+
> 想长期跟更新,建议用**包名安装**。
|
|
142
|
+
|
|
117
143
|
### 兼容性与权限
|
|
118
144
|
|
|
119
145
|
| 项 | 说明 |
|
package/lib/client.js
CHANGED
|
@@ -105,6 +105,21 @@ window.__ModuleLoader__.load({
|
|
|
105
105
|
*/
|
|
106
106
|
const READING = { bg: '#FFFFFF', alpha: 0.62, blur: 3, maxWidth: 640 }
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* The package version this bundle was built from.
|
|
110
|
+
*
|
|
111
|
+
* Written by `scripts/embed-themes.mjs` from package.json, because the browser
|
|
112
|
+
* half cannot read its own manifest: the client module system resolves `require`
|
|
113
|
+
* against the platform seed table and other plugins' boot-graph rows, not
|
|
114
|
+
* against this package's files. The panel shows it so a reader can compare what
|
|
115
|
+
* they have with what npm publishes.
|
|
116
|
+
*
|
|
117
|
+
* Kept honest by two checks: `scripts/publish-check.mjs` compares it with
|
|
118
|
+
* package.json, and the release workflow fails when re-running the embed step
|
|
119
|
+
* changes a tracked file.
|
|
120
|
+
*/
|
|
121
|
+
const BUNDLED_VERSION = '0.1.5'
|
|
122
|
+
|
|
108
123
|
/**
|
|
109
124
|
* Themes this package contributes, inlined from lib/themes/*.json.
|
|
110
125
|
*
|
|
@@ -2767,6 +2782,28 @@ window.__ModuleLoader__.load({
|
|
|
2767
2782
|
return BUNDLED_THEMES.find((theme) => theme.id === id)
|
|
2768
2783
|
}
|
|
2769
2784
|
|
|
2785
|
+
/**
|
|
2786
|
+
* Read one token out of a bundled skin, resolving the pair form to the skin's own scheme.
|
|
2787
|
+
*
|
|
2788
|
+
* The bundled skins store most tokens as `{ light, dark }` pairs even though a skin itself is
|
|
2789
|
+
* single-scheme. `flatten` collapses them for `register`; this is the same read, exposed
|
|
2790
|
+
* separately because the accent diagnostic needs ONE token's declared value without building
|
|
2791
|
+
* the whole registrable definition — and because that diagnostic lives at factory scope, where
|
|
2792
|
+
* `flatten` (declared inside `mountGallery`) is not reachable. Reaching for it from here is the
|
|
2793
|
+
* exact scope error `tests/check-scope-reach.mjs` exists to catch.
|
|
2794
|
+
* @param definition - a bundled skin.
|
|
2795
|
+
* @param name - the token name.
|
|
2796
|
+
* @returns the declared value, or undefined when the skin has no such token.
|
|
2797
|
+
*/
|
|
2798
|
+
function declaredToken(definition, name) {
|
|
2799
|
+
const raw = definition?.tokens?.[name]
|
|
2800
|
+
if (raw === undefined) return undefined
|
|
2801
|
+
// Deliberately not `raw?.[…]`: a skin whose token is neither a string nor a pair is a data
|
|
2802
|
+
// error, and `flatten` (the other caller) must keep failing loudly on it rather than
|
|
2803
|
+
// registering an unset variable that paints nothing.
|
|
2804
|
+
return typeof raw === 'string' ? raw : raw[definition.colorScheme]
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2770
2807
|
/**
|
|
2771
2808
|
* Disposer of the active accent layer, when one is stacked.
|
|
2772
2809
|
*
|
|
@@ -2779,6 +2816,20 @@ window.__ModuleLoader__.load({
|
|
|
2779
2816
|
*/
|
|
2780
2817
|
let accentLayerDispose
|
|
2781
2818
|
|
|
2819
|
+
/**
|
|
2820
|
+
* The accent colour {@link accentLayerDispose} was built from.
|
|
2821
|
+
*
|
|
2822
|
+
* Declared here for the same reason as the disposer above: `syncAccent` both reads and writes
|
|
2823
|
+
* it, and it is called during mount, so a `let` initialised later in the body would be in its
|
|
2824
|
+
* temporal dead zone at the first call.
|
|
2825
|
+
*
|
|
2826
|
+
* It exists so that re-stacking can be skipped when the colour has not moved. Without it every
|
|
2827
|
+
* `theme/change` — including ones this plugin caused and ones caused by something else —
|
|
2828
|
+
* disposed and re-created the layer, which emits again.
|
|
2829
|
+
* @type {string|undefined}
|
|
2830
|
+
*/
|
|
2831
|
+
let stackedAccent
|
|
2832
|
+
|
|
2782
2833
|
/**
|
|
2783
2834
|
* The plugin context, published for the factory-level helpers.
|
|
2784
2835
|
*
|
|
@@ -2827,15 +2878,46 @@ window.__ModuleLoader__.load({
|
|
|
2827
2878
|
// `try` swallowed it, and the accent marker silently never appeared. The module-level `ctx`
|
|
2828
2879
|
// below is assigned at mount and read here.
|
|
2829
2880
|
if (ctx === undefined) return
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2881
|
+
const wanted = typeof accent === 'string' && accent !== '' ? accent : undefined
|
|
2882
|
+
// ── WHY BOTH A SKIP AND THE SELF-EMIT GUARD ARE NEEDED ───────────────────────────────────
|
|
2883
|
+
//
|
|
2884
|
+
// `overrideTokens` and the disposer it returns BOTH emit `theme/change`, and this function
|
|
2885
|
+
// is reached from `publish()`, which is itself driven by that event:
|
|
2886
|
+
//
|
|
2887
|
+
// publish → syncSkin → syncAccent → overrideTokens → theme/change → publish → …
|
|
2888
|
+
//
|
|
2889
|
+
// Every level nests inside the previous CALL, so nothing ever yields to the event loop. The
|
|
2890
|
+
// stack therefore grows until the engine refuses to grow it further and throws
|
|
2891
|
+
// `RangeError: Maximum call stack size exceeded` — and because every level has its own
|
|
2892
|
+
// `try/catch` (see `syncSkin`), each one logs the SAME message on the way out. That is why
|
|
2893
|
+
// the console showed a flood of "could not stack the accent layer" lines rather than one
|
|
2894
|
+
// error: one flood, hundreds of identical lines, each from a different depth.
|
|
2895
|
+
//
|
|
2896
|
+
// Fixing it takes both halves, and they are not interchangeable:
|
|
2897
|
+
//
|
|
2898
|
+
// • the SKIP stops the churn — an accent that has not moved does not need a new layer, so
|
|
2899
|
+
// an unrelated `theme/change` (the shell's `adopt()`, a streamed render, another plugin)
|
|
2900
|
+
// no longer replaces the layer and causes a repaint;
|
|
2901
|
+
// • the GUARD stops the RE-ENTRY — it is what makes the `theme/change` emitted by our own
|
|
2902
|
+
// write invisible to the subscription, so the cycle cannot close even when the colour
|
|
2903
|
+
// genuinely did change.
|
|
2904
|
+
//
|
|
2905
|
+
// Only the guard is a correctness requirement; the skip is what keeps the layer stable. The
|
|
2906
|
+
// same shape is used by `stackSkinTokens` for the palette and by `syncReadingLayer`.
|
|
2907
|
+
if (wanted === stackedAccent) return
|
|
2908
|
+
emitting(() => {
|
|
2909
|
+
if (accentLayerDispose !== undefined) {
|
|
2910
|
+
accentLayerDispose()
|
|
2911
|
+
accentLayerDispose = undefined
|
|
2912
|
+
}
|
|
2913
|
+
stackedAccent = undefined
|
|
2914
|
+
if (wanted === undefined) return
|
|
2915
|
+
accentLayerDispose = ctx.theme.overrideTokens('theme-gallery: accent', {
|
|
2916
|
+
'--dsw-alias-button-ghost-active-fill': { light: `${wanted}29`, dark: `${wanted}29` },
|
|
2917
|
+
'--dsw-alias-button-ghost-active-border': { light: wanted, dark: wanted },
|
|
2918
|
+
'--dsw-alias-button-ghost-active-hover': { light: `${wanted}47`, dark: `${wanted}47` },
|
|
2919
|
+
})
|
|
2920
|
+
stackedAccent = wanted
|
|
2839
2921
|
})
|
|
2840
2922
|
}
|
|
2841
2923
|
|
|
@@ -2852,8 +2934,27 @@ window.__ModuleLoader__.load({
|
|
|
2852
2934
|
*
|
|
2853
2935
|
* The service makes this checkable without any guesswork: `composeActive` folds every
|
|
2854
2936
|
* override layer into `snapshot.active.tokens` before publishing, so the composed value is
|
|
2855
|
-
* readable straight off `getTheme()`.
|
|
2856
|
-
*
|
|
2937
|
+
* readable straight off `getTheme()`.
|
|
2938
|
+
*
|
|
2939
|
+
* ── WHAT CAN AND CANNOT PROVE IT ─────────────────────────────────────────
|
|
2940
|
+
*
|
|
2941
|
+
* The composed VALUE is the only channel that carries information, and only when it differs
|
|
2942
|
+
* from what the skin declares for that same token:
|
|
2943
|
+
*
|
|
2944
|
+
* • both bundled skins already ship `button-ghost-active-*` in their own palettes, AND the
|
|
2945
|
+
* palette layer supplies them too — so the token COUNT is 67 either way. An earlier
|
|
2946
|
+
* comment here claimed a count that "does not move" disproves the layer; for these skins
|
|
2947
|
+
* it cannot move, so the claim was wrong and the count is reported for context only;
|
|
2948
|
+
* • 梦海游鱼 declares `#2B74B5` for that token but its accent is `#FFD166`, so reading back
|
|
2949
|
+
* `#FFD166` proves the accent layer composed OVER the palette — the palette alone cannot
|
|
2950
|
+
* produce it;
|
|
2951
|
+
* • 山青婷彩's accent is `#E88BB0` and it declares `#E88BB0` for the same token, so its
|
|
2952
|
+
* reading is identical whether or not the layer is there. That is a property of the SKIN,
|
|
2953
|
+
* not of the layer, and the verdict below says so instead of implying the reading proved
|
|
2954
|
+
* something.
|
|
2955
|
+
*
|
|
2956
|
+
* The verdict is therefore computed, never asserted: it compares the composed value with the
|
|
2957
|
+
* skin's declared value and with the accent the layer was built from.
|
|
2857
2958
|
* @returns the reading, as one segment of the panel line.
|
|
2858
2959
|
*/
|
|
2859
2960
|
function describeAccentLayer() {
|
|
@@ -2864,16 +2965,31 @@ window.__ModuleLoader__.load({
|
|
|
2864
2965
|
const active = snapshot?.active
|
|
2865
2966
|
const tokens = active?.tokens ?? {}
|
|
2866
2967
|
const TOKEN = '--dsw-alias-button-ghost-active-border'
|
|
2867
|
-
const
|
|
2968
|
+
const composed = tokens[TOKEN]
|
|
2969
|
+
const definition = typeof active?.id === 'string' ? bundledTheme(active.id) : undefined
|
|
2970
|
+
const declared = declaredToken(definition, TOKEN)
|
|
2971
|
+
const accent = definition?.accent
|
|
2868
2972
|
const layers = snapshot?.overrides === undefined
|
|
2869
2973
|
? '(服务未暴露)'
|
|
2870
2974
|
: String(snapshot.overrides.size ?? snapshot.overrides.length ?? '?')
|
|
2871
|
-
//
|
|
2872
|
-
//
|
|
2975
|
+
// Computed, not assumed — see the note above. `不可分辨` is a real answer: it says this
|
|
2976
|
+
// skin's own value coincides with its accent, so this reading proves nothing either way.
|
|
2977
|
+
const verdict = accent === undefined
|
|
2978
|
+
? '非本包皮肤(无法判断)'
|
|
2979
|
+
: accent === declared
|
|
2980
|
+
? '不可分辨(强调色=皮肤自备值)'
|
|
2981
|
+
: composed === accent
|
|
2982
|
+
? '生效(强调色压过自备值)'
|
|
2983
|
+
: composed === declared
|
|
2984
|
+
? '未生效(仍是自备值)'
|
|
2985
|
+
: `未知(既非强调色也非自备值)`
|
|
2873
2986
|
return `激活层[id=${active?.id ?? '?'}`
|
|
2874
2987
|
+ ` 层数=${layers}`
|
|
2875
|
-
+ ` token数=${Object.keys(tokens).length}`
|
|
2876
|
-
+ ` ${TOKEN.replace('--dsw-alias-', '')}=${
|
|
2988
|
+
+ ` token数=${Object.keys(tokens).length}(自备同名令牌,不构成判据)`
|
|
2989
|
+
+ ` ${TOKEN.replace('--dsw-alias-', '')}=${composed === undefined ? '(缺失)' : String(composed)}`
|
|
2990
|
+
+ ` 自备=${declared === undefined ? '(无)' : String(declared)}`
|
|
2991
|
+
+ ` 强调色=${accent ?? '(无)'}`
|
|
2992
|
+
+ ` 判据=${verdict}]`
|
|
2877
2993
|
} catch (error) {
|
|
2878
2994
|
return `激活层[读取失败: ${String(error && error.message ? error.message : error)}]`
|
|
2879
2995
|
}
|
|
@@ -2922,6 +3038,9 @@ window.__ModuleLoader__.load({
|
|
|
2922
3038
|
jsx('span', { className: 'tg-title', children: t('title') }),
|
|
2923
3039
|
jsx('span', { className: 'tg-hint', children: t('hint') }),
|
|
2924
3040
|
jsx('span', { className: 'tg-hint', children: t('count', { count: ids.length }) }),
|
|
3041
|
+
// What this copy is, so a reader can compare it with the version npm
|
|
3042
|
+
// publishes without digging through the profile.
|
|
3043
|
+
jsx('span', { className: 'tg-hint', children: `v${BUNDLED_VERSION}` }),
|
|
2925
3044
|
],
|
|
2926
3045
|
}),
|
|
2927
3046
|
debugEnabled() ? jsx('div', { className: 'tg-debug', children: themeDiagnostics(selected) }) : null,
|
|
@@ -3725,8 +3844,10 @@ window.__ModuleLoader__.load({
|
|
|
3725
3844
|
*/
|
|
3726
3845
|
function flatten(definition) {
|
|
3727
3846
|
const tokens = {}
|
|
3728
|
-
for (const
|
|
3729
|
-
|
|
3847
|
+
for (const name of Object.keys(definition.tokens ?? {})) {
|
|
3848
|
+
// Delegated so the pair rule has exactly ONE implementation — the accent diagnostic reads
|
|
3849
|
+
// a single token through the same helper, and two copies of this rule would drift.
|
|
3850
|
+
tokens[name] = declaredToken(definition, name)
|
|
3730
3851
|
}
|
|
3731
3852
|
return { ...definition, tokens }
|
|
3732
3853
|
}
|
|
@@ -4101,6 +4222,17 @@ window.__ModuleLoader__.load({
|
|
|
4101
4222
|
let readingTag
|
|
4102
4223
|
/** Disposer of the active reading token layer, when one is stacked. */
|
|
4103
4224
|
let readingLayerDispose
|
|
4225
|
+
/**
|
|
4226
|
+
* The lightened ground {@link readingLayerDispose} was built from.
|
|
4227
|
+
*
|
|
4228
|
+
* Same role as `stackedAccent`: `syncReadingLayer` writes to the theme service, and it is
|
|
4229
|
+
* scheduled from a `MutationObserver` on `document.body` — so without this, every batch of
|
|
4230
|
+
* DOM mutations would replace the layer, which emits `theme/change`, which repaints the
|
|
4231
|
+
* shell, which mutates the DOM again. That is a loop through the microtask queue rather than
|
|
4232
|
+
* through the stack, so it would burn CPU steadily instead of throwing.
|
|
4233
|
+
* @type {string|undefined}
|
|
4234
|
+
*/
|
|
4235
|
+
let stackedReading
|
|
4104
4236
|
|
|
4105
4237
|
/**
|
|
4106
4238
|
* Install the reading rule, once per plugin lifetime.
|
|
@@ -4205,19 +4337,31 @@ window.__ModuleLoader__.load({
|
|
|
4205
4337
|
function syncReadingLayer() {
|
|
4206
4338
|
if (typeof document === 'undefined') return
|
|
4207
4339
|
const reading = document.body !== null && document.body.hasAttribute(READING_ATTRIBUTE)
|
|
4208
|
-
|
|
4340
|
+
// Computed before the skip test, because the skip compares VALUES. `lighten` is pure, so
|
|
4341
|
+
// the same document state always yields the same pair and the comparison is exact.
|
|
4342
|
+
const wanted = reading ? lighten(READING.bg, READING.alpha) : undefined
|
|
4343
|
+
const wantedDark = reading
|
|
4344
|
+
// Lightened ground for both palettes: a light theme wants a softer wash, a dark one a
|
|
4345
|
+
// slightly raised surface. Both keep the theme's hue.
|
|
4346
|
+
? lighten(READING.bg, Math.max(0, READING.alpha - 0.2))
|
|
4347
|
+
: undefined
|
|
4348
|
+
|
|
4349
|
+
// Both writes below emit `theme/change`, and this function is reached from a
|
|
4350
|
+
// `MutationObserver` on `document.body` — the same tree the shell repaints when the theme
|
|
4351
|
+
// changes. See the comment in `syncAccent` for the full shape; the short version is that
|
|
4352
|
+
// an unguarded write here lets the layer drive its own trigger.
|
|
4353
|
+
if (wanted === stackedReading) return
|
|
4354
|
+
emitting(() => {
|
|
4209
4355
|
if (readingLayerDispose !== undefined) {
|
|
4210
4356
|
readingLayerDispose()
|
|
4211
4357
|
readingLayerDispose = undefined
|
|
4212
4358
|
}
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
readingLayerDispose = ctx.theme.overrideTokens('theme-gallery: reading', {
|
|
4220
|
-
'--dsw-alias-bg-base': { light: lightened, dark: darkLightened },
|
|
4359
|
+
stackedReading = undefined
|
|
4360
|
+
if (wanted === undefined) return
|
|
4361
|
+
readingLayerDispose = ctx.theme.overrideTokens('theme-gallery: reading', {
|
|
4362
|
+
'--dsw-alias-bg-base': { light: wanted, dark: wantedDark },
|
|
4363
|
+
})
|
|
4364
|
+
stackedReading = wanted
|
|
4221
4365
|
})
|
|
4222
4366
|
}
|
|
4223
4367
|
|
|
@@ -4246,10 +4390,17 @@ window.__ModuleLoader__.load({
|
|
|
4246
4390
|
observer.disconnect()
|
|
4247
4391
|
if (readingTag !== undefined) readingTag.remove()
|
|
4248
4392
|
readingTag = undefined
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4393
|
+
// Guarded for the same reason as the writes in `syncReadingLayer`, and the remembered
|
|
4394
|
+
// value is cleared with the layer: leaving it set would make that function's skip test
|
|
4395
|
+
// believe a layer is still stacked after this one removed it, and it would then refuse
|
|
4396
|
+
// to re-stack.
|
|
4397
|
+
emitting(() => {
|
|
4398
|
+
if (readingLayerDispose !== undefined) {
|
|
4399
|
+
readingLayerDispose()
|
|
4400
|
+
readingLayerDispose = undefined
|
|
4401
|
+
}
|
|
4402
|
+
stackedReading = undefined
|
|
4403
|
+
})
|
|
4253
4404
|
document.body.removeAttribute(READING_ATTRIBUTE)
|
|
4254
4405
|
const centre = centreColumn()
|
|
4255
4406
|
if (centre !== null) centre.style.removeProperty('--dsh-reading-width')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-theme-gallery",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "A community theme gallery for DeepSeek Harness (DSH): JSON-defined skins with full-screen coverage and ported sidebar scenery, switched from the plugin's own sidebar panel — adding skins needs no code change",
|
|
5
5
|
"author": "renjie2026 (https://github.com/renjie2026)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -61,7 +61,8 @@
|
|
|
61
61
|
"test:scopelogic": "node tests/check-scope-reach-logic.mjs",
|
|
62
62
|
"test:preview": "node scripts/build-ambient-preview.mjs",
|
|
63
63
|
"test:calls": "node tests/check-undefined-calls.mjs",
|
|
64
|
-
"test": "
|
|
64
|
+
"test:emitguard": "node tests/check-self-emit-guard.mjs && node tests/check-self-emit-guard-logic.mjs",
|
|
65
|
+
"test": "npm run test:schema && npm run test:host && npm run test:store && npm run test:order && npm run test:tdz && npm run test:theme && npm run test:ambient && npm run test:boot && npm run test:safety && npm run test:module && npm run test:bounded && npm run test:bootpath && npm run test:scope && npm run test:scopelogic && npm run test:calls && npm run test:emitguard",
|
|
65
66
|
"prepack": "node scripts/embed-themes.mjs",
|
|
66
67
|
"publish:check": "node scripts/publish-check.mjs",
|
|
67
68
|
"embed-themes": "node scripts/embed-themes.mjs",
|