dsh-taiwind 0.1.0 → 0.3.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/LICENSE.md +21 -21
- package/README.md +21 -18
- package/cordis.patch.yml +3 -3
- package/dist/client.cjs +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/index.js +48 -49
- package/icon.svg +1 -1
- package/locale/en.json +6 -6
- package/locale/zh.json +6 -6
- package/package.json +1 -1
package/LICENSE.md
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025-PRESENT dsh-tauri-desk
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-PRESENT dsh-tauri-desk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -32,12 +32,10 @@ flowchart LR
|
|
|
32
32
|
## ✨ 特性
|
|
33
33
|
|
|
34
34
|
- **CSS-first**:保留未编译的 `main.css`,支持 `@theme`、`@utility`、`@custom-variant`、`@apply`、`@source inline(...)`。
|
|
35
|
-
- **按需扫描**:从 `clientModules.graph()` 取当前客户端包,`clientPath(id)` 定位构建产物,不依赖插件源码。
|
|
36
35
|
- **官方编译器**:Tailwind 扫描器提取候选类,统一编译;不加载 Preflight,不重置宿主界面。
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
- **条件请求**:带 ETag 的 CSS 接口,命中返回 304;插件图变化立即刷新,另每两秒检查产物与配置。
|
|
36
|
+
- **单一入口**:首屏注入 `<head>`,客户端接管同一个 `<style data-plugin="dsh-taiwind" data-plugin-css="utilities">`。
|
|
37
|
+
- **热更新**:插件安装/卸载/启停,或客户端产物、`main.css` 改动后都会自动重新编译;已打开的页面无需刷新即可换上新样式,也不必重启宿主。
|
|
38
|
+
- **条件请求**:带 ETag 的 CSS 接口,命中返回 304;内容没变就不读产物、不跑 Tailwind。
|
|
41
39
|
- **完整清理**:停用移除路由、监听、定时器与客户端样式,并取消进行中的请求。
|
|
42
40
|
|
|
43
41
|
## 🚀 快速开始
|
|
@@ -58,15 +56,24 @@ pnpm add dsh-taiwind
|
|
|
58
56
|
name: dsh-taiwind
|
|
59
57
|
```
|
|
60
58
|
|
|
61
|
-
用户启用业务插件时,`dsh-taiwind`
|
|
59
|
+
用户启用业务插件时,`dsh-taiwind` 将随行一起启用。
|
|
62
60
|
|
|
63
|
-
-
|
|
64
|
-
- 宿主半要求 `clientModules`、`webServer`、`connection` 三件服务齐备。
|
|
61
|
+
- 它没有界面和工具,唯一产物是共享样式接口 `plugins/taiwind.css` 和页面上的 `<style data-plugin="dsh-taiwind">`。
|
|
65
62
|
- 扫描范围是当前客户端图里所有插件的构建产物,插件图变化会自动触发重新编译。
|
|
66
63
|
|
|
67
|
-
### 2.
|
|
64
|
+
### 2. 声明样式入口
|
|
68
65
|
|
|
69
|
-
|
|
66
|
+
在插件包的 `package.json` 里显式声明未编译的 CSS 入口(相对包根,必须位于包内):
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"dsh": {
|
|
71
|
+
"taiwind": "src/main.css"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`main.css` 保留未编译的 CSS-first 写法:
|
|
70
77
|
|
|
71
78
|
```css
|
|
72
79
|
@import "tailwindcss";
|
|
@@ -81,17 +88,13 @@ pnpm add dsh-taiwind
|
|
|
81
88
|
}
|
|
82
89
|
```
|
|
83
90
|
|
|
84
|
-
客户端直接使用 `bg-notes-accent w-notes-panel notes-compact` 即可。
|
|
85
|
-
|
|
86
|
-
入口发现顺序:
|
|
91
|
+
🚀 客户端直接使用 taiwind 类名,例如 `bg-notes-accent w-notes-panel notes-compact` 即可。
|
|
87
92
|
|
|
88
|
-
|
|
89
|
-
2. 客户端入口同目录的 `client/main.css`
|
|
90
|
-
3. 找到多个会明确报错,不任意选择
|
|
93
|
+
没有声明 `dsh.taiwind` 的包不参与样式编译;声明了但路径不是相对 `.css`、文件不存在或指向包外都会明确报错,不会去猜别的 `main.css`。
|
|
91
94
|
|
|
92
95
|
### 3. 发布时携带样式文件
|
|
93
96
|
|
|
94
|
-
在 `package.json` 中添加 `files`
|
|
97
|
+
在 `package.json` 中添加 `files` 字段,发布时需要携带入口 CSS 及它引用的相对 CSS:
|
|
95
98
|
|
|
96
99
|
```json
|
|
97
100
|
{
|
|
@@ -104,7 +107,7 @@ pnpm add dsh-taiwind
|
|
|
104
107
|
|
|
105
108
|
## ⚙️ 配置文件(可选)
|
|
106
109
|
|
|
107
|
-
可在包根目录放一份 `tailwind.config.js
|
|
110
|
+
可在包根目录放一份 `tailwind.config.js`,或用 CSS 的 `@config` 显式引用:
|
|
108
111
|
|
|
109
112
|
```js
|
|
110
113
|
/** @type {import("tailwindcss").Config} */
|
package/cordis.patch.yml
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
- insert:
|
|
2
|
-
- id: dsh-taiwind
|
|
3
|
-
name: dsh-taiwind
|
|
1
|
+
- insert:
|
|
2
|
+
- id: dsh-taiwind
|
|
3
|
+
name: dsh-taiwind
|
package/dist/client.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
window.__ModuleLoader__.load({id:"dsh-taiwind",factory:(require)=>{var module={exports:{}};var exports=module.exports;
|
|
2
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=`dsh-taiwind`;function t(){let t=document.head.querySelector(`[data-plugin="dsh-taiwind"][data-plugin-css="utilities"]`),n=t??document.createElement(`style`);n.dataset.plugin=e,n.dataset.pluginCss=`utilities`,t||document.head.append(n);let r=new AbortController,i=``,a=!1,o,s=``,c=async()=>{let t=new AbortController,l=setTimeout(()=>t.abort(),15e3),u=()=>t.abort();r.signal.addEventListener(`abort`,u,{once:!0});try{let e=await fetch(new URL(`plugins/
|
|
2
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=`dsh-taiwind`;function t(){let t=document.head.querySelector(`[data-plugin="dsh-taiwind"][data-plugin-css="utilities"]`),n=t??document.createElement(`style`);n.dataset.plugin=e,n.dataset.pluginCss=`utilities`,t||document.head.append(n);let r=new AbortController,i=``,a=!1,o,s=``,c=async()=>{let t=new AbortController,l=setTimeout(()=>t.abort(),15e3),u=()=>t.abort();r.signal.addEventListener(`abort`,u,{once:!0});try{let e=await fetch(new URL(`plugins/taiwind.css`,document.baseURI),{credentials:`same-origin`,cache:`no-cache`,headers:i?{"If-None-Match":i}:{},signal:t.signal});if(e.status!==304){if(!e.ok)throw Error(`Stylesheet request failed (${e.status})`);let t=await e.text();a||(n.textContent!==t&&(n.textContent=t),i=e.headers.get(`ETag`)??``)}s=``}catch(t){if(!a){let n=t instanceof Error?t.message:String(t);s!==n&&(s=n,console.warn(`${e}: ${n}; keeping previous stylesheet`))}}finally{clearTimeout(l),r.signal.removeEventListener(`abort`,u),a||(o=setTimeout(()=>void c(),2e3))}};return c(),()=>{a=!0,r.abort(),clearTimeout(o),n.remove()}}function n(e){e.effect(t)}exports.apply=n;
|
|
3
3
|
return module.exports;}});
|
|
4
4
|
//# sourceMappingURL=client.cjs.map
|
package/dist/client.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.cjs","names":[],"sources":["../src/constants.ts","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["export const PLUGIN_ID = 'dsh-taiwind'\
|
|
1
|
+
{"version":3,"file":"client.cjs","names":[],"sources":["../src/constants.ts","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["export const PLUGIN_ID = 'dsh-taiwind'\nexport const STYLESHEET_PATH = 'plugins/taiwind.css'\nexport const STYLE_SELECTOR = '[data-plugin=\"dsh-taiwind\"][data-plugin-css=\"utilities\"]'\nexport const POLL_INTERVAL_MS = 2000\n","import { PLUGIN_ID, POLL_INTERVAL_MS, STYLE_SELECTOR, STYLESHEET_PATH } from '../constants'\n\nexport function mountStyles(): () => void {\n const existing = document.head.querySelector<HTMLStyleElement>(STYLE_SELECTOR)\n const style = existing ?? document.createElement('style')\n style.dataset.plugin = PLUGIN_ID\n style.dataset.pluginCss = 'utilities'\n if (!existing)\n document.head.append(style)\n\n const controller = new AbortController()\n let etag = ''\n let disposed = false\n let timer: ReturnType<typeof setTimeout> | undefined\n let lastError = ''\n\n const update = async (): Promise<void> => {\n const request = new AbortController()\n const timeout = setTimeout(() => request.abort(), 15000)\n const abort = (): void => request.abort()\n controller.signal.addEventListener('abort', abort, { once: true })\n try {\n const response = await fetch(new URL(STYLESHEET_PATH, document.baseURI), {\n credentials: 'same-origin',\n cache: 'no-cache',\n headers: etag ? { 'If-None-Match': etag } : {},\n signal: request.signal,\n })\n if (response.status !== 304) {\n if (!response.ok)\n throw new Error(`Stylesheet request failed (${response.status})`)\n const css = await response.text()\n if (!disposed) {\n if (style.textContent !== css)\n style.textContent = css\n etag = response.headers.get('ETag') ?? ''\n }\n }\n lastError = ''\n }\n catch (error) {\n if (!disposed) {\n const message = error instanceof Error ? error.message : String(error)\n if (lastError !== message) {\n lastError = message\n console.warn(`${PLUGIN_ID}: ${message}; keeping previous stylesheet`)\n }\n }\n }\n finally {\n clearTimeout(timeout)\n controller.signal.removeEventListener('abort', abort)\n if (!disposed)\n timer = setTimeout(() => void update(), POLL_INTERVAL_MS)\n }\n }\n void update()\n\n return () => {\n disposed = true\n controller.abort()\n clearTimeout(timer)\n style.remove()\n }\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { mountStyles } from './styles'\n\nexport function apply(ctx: Context): void {\n ctx.effect(mountStyles)\n}\n"],"mappings":";mEAAA,MAAa,EAAY,cCEzB,SAAgB,GAA0B,CACxC,IAAM,EAAW,SAAS,KAAK,cAAgC,0DAAc,EACvE,EAAQ,GAAY,SAAS,cAAc,OAAO,EACxD,EAAM,QAAQ,OAAS,EACvB,EAAM,QAAQ,UAAY,YACrB,GACH,SAAS,KAAK,OAAO,CAAK,EAE5B,IAAM,EAAa,IAAI,gBACnB,EAAO,GACP,EAAW,GACX,EACA,EAAY,GAEV,EAAS,SAA2B,CACxC,IAAM,EAAU,IAAI,gBACd,EAAU,eAAiB,EAAQ,MAAM,EAAG,IAAK,EACjD,MAAoB,EAAQ,MAAM,EACxC,EAAW,OAAO,iBAAiB,QAAS,EAAO,CAAE,KAAM,EAAK,CAAC,EACjE,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,IAAI,IAAI,sBAAiB,SAAS,OAAO,EAAG,CACvE,YAAa,cACb,MAAO,WACP,QAAS,EAAO,CAAE,gBAAiB,CAAK,EAAI,CAAC,EAC7C,OAAQ,EAAQ,MAClB,CAAC,EACD,GAAI,EAAS,SAAW,IAAK,CAC3B,GAAI,CAAC,EAAS,GACZ,MAAU,MAAM,8BAA8B,EAAS,OAAO,EAAE,EAClE,IAAM,EAAM,MAAM,EAAS,KAAK,EAC3B,IACC,EAAM,cAAgB,IACxB,EAAM,YAAc,GACtB,EAAO,EAAS,QAAQ,IAAI,MAAM,GAAK,GAE3C,CACA,EAAY,EACd,OACO,EAAO,CACZ,GAAI,CAAC,EAAU,CACb,IAAM,EAAU,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EACjE,IAAc,IAChB,EAAY,EACZ,QAAQ,KAAK,GAAG,EAAU,IAAI,EAAQ,8BAA8B,EAExE,CACF,QACQ,CACN,aAAa,CAAO,EACpB,EAAW,OAAO,oBAAoB,QAAS,CAAK,EAC/C,IACH,EAAQ,eAAiB,KAAK,EAAO,EAAG,GAAgB,EAC5D,CACF,EAGA,OAFA,EAAY,MAEC,CACX,EAAW,GACX,EAAW,MAAM,EACjB,aAAa,CAAK,EAClB,EAAM,OAAO,CACf,CACF,CC7DA,SAAgB,EAAM,EAAoB,CACxC,EAAI,OAAO,CAAW,CACxB"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { readFile, readdir, realpath } from "node:fs/promises";
|
|
3
|
+
import { readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
4
4
|
import { dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import { Scanner } from "@tailwindcss/oxide";
|
|
@@ -138,34 +138,20 @@ async function bundles(directory) {
|
|
|
138
138
|
}
|
|
139
139
|
return files;
|
|
140
140
|
}
|
|
141
|
-
async function cssEntry(root,
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (
|
|
145
|
-
|
|
146
|
-
return join(directory, entry.name);
|
|
147
|
-
}
|
|
141
|
+
async function cssEntry(root, owner) {
|
|
142
|
+
const entry = JSON.parse(await readFile(join(root, "package.json"), "utf8")).dsh?.taiwind;
|
|
143
|
+
if (entry === void 0) {
|
|
144
|
+
if (await stat(join(root, "main.css")).then((info) => info.isFile(), () => false)) throw new Error(`${owner}: main.css is not discovered automatically anymore; declare "dsh": { "taiwind": "main.css" } in package.json`);
|
|
145
|
+
return;
|
|
148
146
|
}
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
"node_modules",
|
|
154
|
-
"archive",
|
|
155
|
-
".git"
|
|
156
|
-
].includes(item.name) || item.isSymbolicLink()) continue;
|
|
157
|
-
const path = join(directory, item.name);
|
|
158
|
-
if (item.isDirectory()) await visit(path);
|
|
159
|
-
else if (item.isFile() && item.name === "main.css") found.push(path);
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
await visit(root);
|
|
163
|
-
if (found.length > 1) throw new Error(`${root}: multiple main.css entries found: ${found.sort().join(", ")}`);
|
|
164
|
-
return found[0];
|
|
147
|
+
if (typeof entry !== "string" || !entry.trim() || extname(entry) !== ".css") throw new Error(`${owner}: dsh.taiwind must be a relative .css path`);
|
|
148
|
+
const path = resolve(root, entry);
|
|
149
|
+
if (!await stat(path).then((info) => info.isFile(), () => false)) throw new Error(`${owner}: dsh.taiwind entry not found: ${entry}`);
|
|
150
|
+
return path;
|
|
165
151
|
}
|
|
166
152
|
async function compilePlugins(entries) {
|
|
167
153
|
const hash = createHash("sha256");
|
|
168
|
-
const
|
|
154
|
+
const bundleFiles = [];
|
|
169
155
|
const extend = Object.create(null);
|
|
170
156
|
const merged = { theme: { extend } };
|
|
171
157
|
const owners = /* @__PURE__ */ new Map();
|
|
@@ -281,14 +267,16 @@ async function compilePlugins(entries) {
|
|
|
281
267
|
for (const file of await bundles(dirname(path))) {
|
|
282
268
|
if (seen.has(file)) continue;
|
|
283
269
|
seen.add(file);
|
|
284
|
-
const
|
|
285
|
-
hash.update(JSON.stringify([
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
270
|
+
const info = await stat(file);
|
|
271
|
+
hash.update(JSON.stringify([
|
|
272
|
+
file,
|
|
273
|
+
info.mtimeMs,
|
|
274
|
+
info.ctimeMs,
|
|
275
|
+
info.size
|
|
276
|
+
]));
|
|
277
|
+
bundleFiles.push(file);
|
|
290
278
|
}
|
|
291
|
-
const stylesheet = await cssEntry(root,
|
|
279
|
+
const stylesheet = await cssEntry(root, entry.id);
|
|
292
280
|
if (stylesheet) {
|
|
293
281
|
cssImports.push(await loadCss(stylesheet, root, entry.id));
|
|
294
282
|
continue;
|
|
@@ -305,7 +293,12 @@ async function compilePlugins(entries) {
|
|
|
305
293
|
const utilities = await readFile(new URL(import.meta.resolve("tailwindcss/utilities.css")), "utf8");
|
|
306
294
|
hash.update(JSON.stringify([theme, utilities]));
|
|
307
295
|
const digest = hash.digest("hex");
|
|
308
|
-
if (cached?.
|
|
296
|
+
if (cached?.signature === digest) return cached.css;
|
|
297
|
+
const sources = [];
|
|
298
|
+
for (const file of bundleFiles) sources.push({
|
|
299
|
+
content: await readFile(file, "utf8"),
|
|
300
|
+
extension: extname(file).slice(1)
|
|
301
|
+
});
|
|
309
302
|
const candidates = new Scanner({}).scanFiles(sources);
|
|
310
303
|
const compiler = await compile(`@layer theme, base, components, utilities;\n@layer theme {\n${theme}\n}\n${cssImports.map((path) => `@import ${JSON.stringify(path.replaceAll("\\", "/"))};`).join("\n")}\n@layer utilities {\n${utilities}\n}\n@config "dsh-taiwind:merged";`, {
|
|
311
304
|
loadModule: async (id) => {
|
|
@@ -330,7 +323,7 @@ async function compilePlugins(entries) {
|
|
|
330
323
|
if (compiler.sources.length || compiler.root !== null && compiler.root !== "none") throw new Error("Tailwind source(...) directory scanning is not supported; file candidates come from client bundles");
|
|
331
324
|
const css = compiler.build(candidates);
|
|
332
325
|
cached = {
|
|
333
|
-
|
|
326
|
+
signature: digest,
|
|
334
327
|
css
|
|
335
328
|
};
|
|
336
329
|
return css;
|
|
@@ -338,7 +331,7 @@ async function compilePlugins(entries) {
|
|
|
338
331
|
//#endregion
|
|
339
332
|
//#region src/constants.ts
|
|
340
333
|
const PLUGIN_ID = "dsh-taiwind";
|
|
341
|
-
const STYLESHEET_PATH = "plugins/
|
|
334
|
+
const STYLESHEET_PATH = "plugins/taiwind.css";
|
|
342
335
|
const POLL_INTERVAL_MS = 2e3;
|
|
343
336
|
//#endregion
|
|
344
337
|
//#region src/index.ts
|
|
@@ -351,7 +344,7 @@ const inject = [
|
|
|
351
344
|
async function apply(ctx) {
|
|
352
345
|
let css = "";
|
|
353
346
|
let etag = "";
|
|
354
|
-
let failed =
|
|
347
|
+
let failed = true;
|
|
355
348
|
let disposed = false;
|
|
356
349
|
let generation = 0;
|
|
357
350
|
let pending;
|
|
@@ -385,23 +378,24 @@ async function apply(ctx) {
|
|
|
385
378
|
ctx.effect(() => () => {
|
|
386
379
|
disposed = true;
|
|
387
380
|
});
|
|
381
|
+
const fail = (error) => {
|
|
382
|
+
if (disposed) return;
|
|
383
|
+
failed = true;
|
|
384
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
385
|
+
if (message !== lastError) {
|
|
386
|
+
lastError = message;
|
|
387
|
+
ctx.logger.warn(`${PLUGIN_ID}: ${message}; keeping previous stylesheet`);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
388
390
|
const update = () => {
|
|
389
|
-
refresh().catch(
|
|
390
|
-
if (disposed) return;
|
|
391
|
-
failed = true;
|
|
392
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
393
|
-
if (message !== lastError) {
|
|
394
|
-
lastError = message;
|
|
395
|
-
ctx.logger.warn(`${PLUGIN_ID}: ${message}; keeping previous stylesheet`);
|
|
396
|
-
}
|
|
397
|
-
});
|
|
391
|
+
refresh().catch(fail);
|
|
398
392
|
};
|
|
399
|
-
|
|
393
|
+
const invalidate = () => {
|
|
400
394
|
generation++;
|
|
401
395
|
update();
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
|
|
396
|
+
};
|
|
397
|
+
ctx.effect(() => ctx.clientModules.onGraphChanged(invalidate));
|
|
398
|
+
ctx.effect(() => ctx.clientModules.onRebuilt(invalidate));
|
|
405
399
|
ctx.effect(() => ctx.webServer.register({
|
|
406
400
|
kind: "exact",
|
|
407
401
|
path: `/${STYLESHEET_PATH}`,
|
|
@@ -447,6 +441,11 @@ async function apply(ctx) {
|
|
|
447
441
|
timer.unref();
|
|
448
442
|
return () => clearInterval(timer);
|
|
449
443
|
});
|
|
444
|
+
try {
|
|
445
|
+
await refresh();
|
|
446
|
+
} catch (error) {
|
|
447
|
+
fail(error);
|
|
448
|
+
}
|
|
450
449
|
}
|
|
451
450
|
//#endregion
|
|
452
451
|
export { apply, inject, name };
|
package/icon.svg
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="#102a43"/><g fill="none" stroke="#67e8f9" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 18h12l9 14h15M14 46h12l9-14M14 32h36"/><path d="m44 26 6 6-6 6"/></g></svg>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="#102a43"/><g fill="none" stroke="#67e8f9" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 18h12l9 14h15M14 46h12l9-14M14 32h36"/><path d="m44 26 6 6-6 6"/></g></svg>
|
package/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{
|
|
2
|
-
"meta": {
|
|
3
|
-
"title": "Shared Tailwind CSS",
|
|
4
|
-
"description": "Combines client bundle utility classes into one stylesheet, with per-plugin static Tailwind configuration."
|
|
5
|
-
}
|
|
6
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "Shared Tailwind CSS",
|
|
4
|
+
"description": "Combines client bundle utility classes into one stylesheet, with per-plugin static Tailwind configuration."
|
|
5
|
+
}
|
|
6
|
+
}
|
package/locale/zh.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{
|
|
2
|
-
"meta": {
|
|
3
|
-
"title": "共享 Tailwind 样式",
|
|
4
|
-
"description": "聚合客户端插件的工具类,生成一份去重样式,支持各插件独立的静态 Tailwind 配置。"
|
|
5
|
-
}
|
|
6
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "共享 Tailwind 样式",
|
|
4
|
+
"description": "聚合客户端插件的工具类,生成一份去重样式,支持各插件独立的静态 Tailwind 配置。"
|
|
5
|
+
}
|
|
6
|
+
}
|
package/package.json
CHANGED