dsh-taiwind 0.1.0 → 0.2.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 +10 -11
- package/cordis.patch.yml +3 -3
- package/dist/client.cjs +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/index.js +51 -27
- 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
|
## 🚀 快速开始
|
|
@@ -60,13 +58,13 @@ pnpm add dsh-taiwind
|
|
|
60
58
|
|
|
61
59
|
用户启用业务插件时,`dsh-taiwind` 将随行一起启用,而客户端半也必须有一行启用中的 Loader 条目才会被 `dsh-client-modules` 加载。
|
|
62
60
|
|
|
63
|
-
- 它没有界面、没有工具,唯一产物是共享样式接口 `plugins/
|
|
61
|
+
- 它没有界面、没有工具,唯一产物是共享样式接口 `plugins/taiwind.css` 和页面上的一个 `<style data-plugin="dsh-taiwind">`。
|
|
64
62
|
- 宿主半要求 `clientModules`、`webServer`、`connection` 三件服务齐备。
|
|
65
63
|
- 扫描范围是当前客户端图里所有插件的构建产物,插件图变化会自动触发重新编译。
|
|
66
64
|
|
|
67
65
|
### 2. 在插件包里写样式
|
|
68
66
|
|
|
69
|
-
在插件包中保留未编译的 `main.css
|
|
67
|
+
在插件包中保留未编译的 `main.css`:
|
|
70
68
|
|
|
71
69
|
```css
|
|
72
70
|
@import "tailwindcss";
|
|
@@ -81,13 +79,14 @@ pnpm add dsh-taiwind
|
|
|
81
79
|
}
|
|
82
80
|
```
|
|
83
81
|
|
|
84
|
-
客户端直接使用 `bg-notes-accent w-notes-panel notes-compact` 即可。
|
|
82
|
+
🚀 客户端直接使用 taiwind 类名,例如 `bg-notes-accent w-notes-panel notes-compact` 即可。
|
|
85
83
|
|
|
86
84
|
入口发现顺序:
|
|
87
85
|
|
|
88
86
|
1. 包根目录的 `main.css`
|
|
89
|
-
2. 客户端入口同目录的 `client/main.css
|
|
90
|
-
3.
|
|
87
|
+
2. 客户端入口同目录的 `main.css`(如 `client/main.css`)
|
|
88
|
+
3. 以上都没有时,在包内查找 `main.css`;
|
|
89
|
+
4. 找到多个会明确报错,不任意选择
|
|
91
90
|
|
|
92
91
|
### 3. 发布时携带样式文件
|
|
93
92
|
|
|
@@ -104,7 +103,7 @@ pnpm add dsh-taiwind
|
|
|
104
103
|
|
|
105
104
|
## ⚙️ 配置文件(可选)
|
|
106
105
|
|
|
107
|
-
可在包根目录放一份 `tailwind.config.js
|
|
106
|
+
可在包根目录放一份 `tailwind.config.js`,或用 CSS 的 `@config` 显式引用:
|
|
108
107
|
|
|
109
108
|
```js
|
|
110
109
|
/** @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,6 +138,15 @@ async function bundles(directory) {
|
|
|
138
138
|
}
|
|
139
139
|
return files;
|
|
140
140
|
}
|
|
141
|
+
async function nestedPackage(directory) {
|
|
142
|
+
try {
|
|
143
|
+
await readFile(join(directory, "package.json"));
|
|
144
|
+
return true;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (error.code === "ENOENT") return false;
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
141
150
|
async function cssEntry(root, clientDirectory) {
|
|
142
151
|
for (const directory of /* @__PURE__ */ new Set([root, clientDirectory])) {
|
|
143
152
|
const entry = (await readdir(directory, { withFileTypes: true })).find((item) => item.name === "main.css");
|
|
@@ -155,8 +164,10 @@ async function cssEntry(root, clientDirectory) {
|
|
|
155
164
|
".git"
|
|
156
165
|
].includes(item.name) || item.isSymbolicLink()) continue;
|
|
157
166
|
const path = join(directory, item.name);
|
|
158
|
-
if (item.isDirectory())
|
|
159
|
-
|
|
167
|
+
if (item.isDirectory()) {
|
|
168
|
+
if (await nestedPackage(path)) continue;
|
|
169
|
+
await visit(path);
|
|
170
|
+
} else if (item.isFile() && item.name === "main.css") found.push(path);
|
|
160
171
|
}
|
|
161
172
|
};
|
|
162
173
|
await visit(root);
|
|
@@ -165,7 +176,7 @@ async function cssEntry(root, clientDirectory) {
|
|
|
165
176
|
}
|
|
166
177
|
async function compilePlugins(entries) {
|
|
167
178
|
const hash = createHash("sha256");
|
|
168
|
-
const
|
|
179
|
+
const bundleFiles = [];
|
|
169
180
|
const extend = Object.create(null);
|
|
170
181
|
const merged = { theme: { extend } };
|
|
171
182
|
const owners = /* @__PURE__ */ new Map();
|
|
@@ -281,12 +292,14 @@ async function compilePlugins(entries) {
|
|
|
281
292
|
for (const file of await bundles(dirname(path))) {
|
|
282
293
|
if (seen.has(file)) continue;
|
|
283
294
|
seen.add(file);
|
|
284
|
-
const
|
|
285
|
-
hash.update(JSON.stringify([
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
295
|
+
const info = await stat(file);
|
|
296
|
+
hash.update(JSON.stringify([
|
|
297
|
+
file,
|
|
298
|
+
info.mtimeMs,
|
|
299
|
+
info.ctimeMs,
|
|
300
|
+
info.size
|
|
301
|
+
]));
|
|
302
|
+
bundleFiles.push(file);
|
|
290
303
|
}
|
|
291
304
|
const stylesheet = await cssEntry(root, dirname(path));
|
|
292
305
|
if (stylesheet) {
|
|
@@ -305,7 +318,12 @@ async function compilePlugins(entries) {
|
|
|
305
318
|
const utilities = await readFile(new URL(import.meta.resolve("tailwindcss/utilities.css")), "utf8");
|
|
306
319
|
hash.update(JSON.stringify([theme, utilities]));
|
|
307
320
|
const digest = hash.digest("hex");
|
|
308
|
-
if (cached?.
|
|
321
|
+
if (cached?.signature === digest) return cached.css;
|
|
322
|
+
const sources = [];
|
|
323
|
+
for (const file of bundleFiles) sources.push({
|
|
324
|
+
content: await readFile(file, "utf8"),
|
|
325
|
+
extension: extname(file).slice(1)
|
|
326
|
+
});
|
|
309
327
|
const candidates = new Scanner({}).scanFiles(sources);
|
|
310
328
|
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
329
|
loadModule: async (id) => {
|
|
@@ -330,7 +348,7 @@ async function compilePlugins(entries) {
|
|
|
330
348
|
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
349
|
const css = compiler.build(candidates);
|
|
332
350
|
cached = {
|
|
333
|
-
|
|
351
|
+
signature: digest,
|
|
334
352
|
css
|
|
335
353
|
};
|
|
336
354
|
return css;
|
|
@@ -338,7 +356,7 @@ async function compilePlugins(entries) {
|
|
|
338
356
|
//#endregion
|
|
339
357
|
//#region src/constants.ts
|
|
340
358
|
const PLUGIN_ID = "dsh-taiwind";
|
|
341
|
-
const STYLESHEET_PATH = "plugins/
|
|
359
|
+
const STYLESHEET_PATH = "plugins/taiwind.css";
|
|
342
360
|
const POLL_INTERVAL_MS = 2e3;
|
|
343
361
|
//#endregion
|
|
344
362
|
//#region src/index.ts
|
|
@@ -351,7 +369,7 @@ const inject = [
|
|
|
351
369
|
async function apply(ctx) {
|
|
352
370
|
let css = "";
|
|
353
371
|
let etag = "";
|
|
354
|
-
let failed =
|
|
372
|
+
let failed = true;
|
|
355
373
|
let disposed = false;
|
|
356
374
|
let generation = 0;
|
|
357
375
|
let pending;
|
|
@@ -385,23 +403,24 @@ async function apply(ctx) {
|
|
|
385
403
|
ctx.effect(() => () => {
|
|
386
404
|
disposed = true;
|
|
387
405
|
});
|
|
406
|
+
const fail = (error) => {
|
|
407
|
+
if (disposed) return;
|
|
408
|
+
failed = true;
|
|
409
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
410
|
+
if (message !== lastError) {
|
|
411
|
+
lastError = message;
|
|
412
|
+
ctx.logger.warn(`${PLUGIN_ID}: ${message}; keeping previous stylesheet`);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
388
415
|
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
|
-
});
|
|
416
|
+
refresh().catch(fail);
|
|
398
417
|
};
|
|
399
|
-
|
|
418
|
+
const invalidate = () => {
|
|
400
419
|
generation++;
|
|
401
420
|
update();
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
|
|
421
|
+
};
|
|
422
|
+
ctx.effect(() => ctx.clientModules.onGraphChanged(invalidate));
|
|
423
|
+
ctx.effect(() => ctx.clientModules.onRebuilt(invalidate));
|
|
405
424
|
ctx.effect(() => ctx.webServer.register({
|
|
406
425
|
kind: "exact",
|
|
407
426
|
path: `/${STYLESHEET_PATH}`,
|
|
@@ -447,6 +466,11 @@ async function apply(ctx) {
|
|
|
447
466
|
timer.unref();
|
|
448
467
|
return () => clearInterval(timer);
|
|
449
468
|
});
|
|
469
|
+
try {
|
|
470
|
+
await refresh();
|
|
471
|
+
} catch (error) {
|
|
472
|
+
fail(error);
|
|
473
|
+
}
|
|
450
474
|
}
|
|
451
475
|
//#endregion
|
|
452
476
|
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