@smartdoca/doca-easy-pomodoro-plugin 0.0.0-stage → 0.1.1
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 +21 -0
- package/README.md +14 -2
- package/THIRD_PARTY_NOTICES.txt +101 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +566 -0
- package/manifest.json +7 -0
- package/package.json +34 -4
- package/web/index.d.ts +4 -0
- package/web/index.js +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Doca Everyday Plugins contributors
|
|
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
|
@@ -1,3 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 番茄钟 / Pomodoro
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
npm 包:`@smartdoca/doca-easy-pomodoro-plugin`。入口使用番茄红色时钟 SVG 图标。
|
|
4
|
+
|
|
5
|
+
插件 ID:`doca.easy.pomodoro`。主页只显示点击入口,全局更多菜单和文档工具栏直接打开同一个非模态悬浮卡片。默认专注/短休息/长休息为 25/5/15 分钟,可在空闲时调整到 1–120 分钟。
|
|
6
|
+
|
|
7
|
+
卡片有缩小、关闭按钮,顶部与缩小后的拖动手柄支持鼠标/触摸拖拽,也可聚焦手柄后用方向键移动。移动、展开和视口变化时限制在可见范围。缩小后只保留倒计时、状态图标和关闭按钮:专注为橙色,休息为绿色,暂停为灰色。开始、暂停与继续保持当前展示大小,只有手动点击缩小按钮才折叠;关闭只隐藏卡片,计时继续。单页内切换页面保留悬浮卡片;倒计时结束后,即使已缩小或关闭也自动显示完整卡片,提示完成并提供下一阶段入口,不自动启动下一阶段。
|
|
8
|
+
|
|
9
|
+
按实际结束时间计时,支持暂停、继续、重置、刷新恢复和每日专注次数。每完成四次专注进入长休息。计时状态写入宿主托管数据库并按 Doca 用户隔离,多视图及跨设备同步;并发完成或修改不会覆盖其他窗口的版本。同用户其他窗口保存的完成状态也会展开提醒。显示大小、位置与完成提示仅保留在当前页面会话中,刷新后由入口重新打开;刷新载入的已过期计时仍会自动完成并展开提醒。
|
|
10
|
+
|
|
11
|
+
没有外部 API 或系统通知。悬浮层使用浏览器原生手动 Popover,只挂载插件自身 DOM,不阻止操作页面;浏览器需支持 Popover API。服务端通过 SDK 托管关系库保存截止时间与设置,不创建独立数据库。关闭页面释放前端计时器、同步通道与 DOM;插件或登录权限被撤销时,下一次校准清理悬浮层。卸载数据清理由宿主负责。
|
|
12
|
+
|
|
13
|
+
版本 0.1.1,`sdkRange: "^0.1.9"`、`doca.storage: "host"`、`dataVersion: "2"`。随包提供服务端和 Web 入口的 `.d.ts`。
|
|
14
|
+
|
|
15
|
+
在仓库根目录安装依赖后执行 `npm run build --workspace pomodoro`;`npm run release` 生成独立 ZIP。开发、安装和验证流程见上一级 README。
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
@deepseek-ai/cordis@4.0.4 (MIT)
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2021-present Shigma
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
--------------------
|
|
26
|
+
|
|
27
|
+
@deepseek-ai/cosmokit@1.8.5 (MIT)
|
|
28
|
+
MIT License
|
|
29
|
+
|
|
30
|
+
Copyright (c) 2021-present Shigma
|
|
31
|
+
|
|
32
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
33
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
34
|
+
in the Software without restriction, including without limitation the rights
|
|
35
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
36
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
37
|
+
furnished to do so, subject to the following conditions:
|
|
38
|
+
|
|
39
|
+
The above copyright notice and this permission notice shall be included in all
|
|
40
|
+
copies or substantial portions of the Software.
|
|
41
|
+
|
|
42
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
43
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
44
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
45
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
46
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
47
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
48
|
+
SOFTWARE.
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
--------------------
|
|
52
|
+
|
|
53
|
+
@smartdoca/plugin-contracts@0.1.7 (MIT)
|
|
54
|
+
MIT License
|
|
55
|
+
|
|
56
|
+
Copyright (c) 2026 smartdoca
|
|
57
|
+
|
|
58
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
59
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
60
|
+
in the Software without restriction, including without limitation the rights
|
|
61
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
62
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
63
|
+
furnished to do so, subject to the following conditions:
|
|
64
|
+
|
|
65
|
+
The above copyright notice and this permission notice shall be included in all
|
|
66
|
+
copies or substantial portions of the Software.
|
|
67
|
+
|
|
68
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
69
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
70
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
71
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
72
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
73
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
74
|
+
SOFTWARE.
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
--------------------
|
|
78
|
+
|
|
79
|
+
@smartdoca/plugin-sdk@0.1.9 (MIT)
|
|
80
|
+
MIT License
|
|
81
|
+
|
|
82
|
+
Copyright (c) 2026 smartdoca
|
|
83
|
+
|
|
84
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
85
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
86
|
+
in the Software without restriction, including without limitation the rights
|
|
87
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
88
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
89
|
+
furnished to do so, subject to the following conditions:
|
|
90
|
+
|
|
91
|
+
The above copyright notice and this permission notice shall be included in all
|
|
92
|
+
copies or substantial portions of the Software.
|
|
93
|
+
|
|
94
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
95
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
96
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
97
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
98
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
99
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
100
|
+
SOFTWARE.
|
|
101
|
+
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
// node_modules/@smartdoca/plugin-sdk/node_modules/@smartdoca/plugin-contracts/dist/editor-elements.js
|
|
2
|
+
var PLUGIN_ELEMENT_BYTES = 32 * 1024;
|
|
3
|
+
|
|
4
|
+
// node_modules/@smartdoca/plugin-sdk/node_modules/@smartdoca/plugin-contracts/dist/index.js
|
|
5
|
+
var PluginContractError = class extends Error {
|
|
6
|
+
code;
|
|
7
|
+
path;
|
|
8
|
+
name = "PluginContractError";
|
|
9
|
+
constructor(code, message, path = "$") {
|
|
10
|
+
super(`${path}: ${message}`);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.path = path;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var identifierPattern = /^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/;
|
|
16
|
+
var configKeyPattern = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
17
|
+
var semverPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
18
|
+
var dependencyRangePattern = /^(?:\*|[\^~]?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)$/;
|
|
19
|
+
function fail(code, path, message) {
|
|
20
|
+
throw new PluginContractError(code, message, path);
|
|
21
|
+
}
|
|
22
|
+
function isRecord(value) {
|
|
23
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
24
|
+
return false;
|
|
25
|
+
const prototype = Object.getPrototypeOf(value);
|
|
26
|
+
return prototype === Object.prototype || prototype === null;
|
|
27
|
+
}
|
|
28
|
+
function objectAt(value, path, code) {
|
|
29
|
+
if (!isRecord(value))
|
|
30
|
+
fail(code, path, "expected an object");
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
function allowedKeys(value, allowed, path, code) {
|
|
34
|
+
const set = new Set(allowed);
|
|
35
|
+
const unexpected = Object.keys(value).find((key) => !set.has(key));
|
|
36
|
+
if (unexpected)
|
|
37
|
+
fail(code, `${path}.${unexpected}`, "unknown field");
|
|
38
|
+
}
|
|
39
|
+
function stringAt(value, path, code, options = {}) {
|
|
40
|
+
if (typeof value !== "string" || options.nonempty && value.trim().length === 0)
|
|
41
|
+
fail(code, path, "expected a non-empty string");
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
function finiteAt(value, path, code) {
|
|
45
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
46
|
+
fail(code, path, "expected a finite number");
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
function nonNegativeIntegerAt(value, path, code) {
|
|
50
|
+
const number = finiteAt(value, path, code);
|
|
51
|
+
if (!Number.isInteger(number) || number < 0)
|
|
52
|
+
fail(code, path, "expected a non-negative integer");
|
|
53
|
+
return number;
|
|
54
|
+
}
|
|
55
|
+
function optionalDescription(value, path, code) {
|
|
56
|
+
if (value.description !== void 0)
|
|
57
|
+
stringAt(value.description, `${path}.description`, code, {
|
|
58
|
+
nonempty: true
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function assertConfigSchema(value, path) {
|
|
62
|
+
const schema = objectAt(value, path, "INVALID_MANIFEST");
|
|
63
|
+
const type = stringAt(schema.type, `${path}.type`, "INVALID_MANIFEST");
|
|
64
|
+
const common = ["type", "description", "default"];
|
|
65
|
+
optionalDescription(schema, path, "INVALID_MANIFEST");
|
|
66
|
+
if (type === "string") {
|
|
67
|
+
allowedKeys(schema, [...common, "enum", "minLength", "maxLength", "pattern"], path, "INVALID_MANIFEST");
|
|
68
|
+
if (schema.enum !== void 0) {
|
|
69
|
+
if (!Array.isArray(schema.enum) || schema.enum.some((item) => typeof item !== "string"))
|
|
70
|
+
fail("INVALID_MANIFEST", `${path}.enum`, "expected string array");
|
|
71
|
+
if (new Set(schema.enum).size !== schema.enum.length)
|
|
72
|
+
fail("INVALID_MANIFEST", `${path}.enum`, "contains duplicate values");
|
|
73
|
+
}
|
|
74
|
+
const min = schema.minLength === void 0 ? void 0 : nonNegativeIntegerAt(schema.minLength, `${path}.minLength`, "INVALID_MANIFEST");
|
|
75
|
+
const max = schema.maxLength === void 0 ? void 0 : nonNegativeIntegerAt(schema.maxLength, `${path}.maxLength`, "INVALID_MANIFEST");
|
|
76
|
+
if (min !== void 0 && max !== void 0 && min > max)
|
|
77
|
+
fail("INVALID_MANIFEST", path, "minLength cannot exceed maxLength");
|
|
78
|
+
if (schema.pattern !== void 0) {
|
|
79
|
+
const pattern = stringAt(schema.pattern, `${path}.pattern`, "INVALID_MANIFEST");
|
|
80
|
+
try {
|
|
81
|
+
new RegExp(pattern);
|
|
82
|
+
} catch {
|
|
83
|
+
fail("INVALID_MANIFEST", `${path}.pattern`, "invalid regular expression");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} else if (type === "number" || type === "integer") {
|
|
87
|
+
allowedKeys(schema, [...common, "minimum", "maximum"], path, "INVALID_MANIFEST");
|
|
88
|
+
const minimum = schema.minimum === void 0 ? void 0 : finiteAt(schema.minimum, `${path}.minimum`, "INVALID_MANIFEST");
|
|
89
|
+
const maximum = schema.maximum === void 0 ? void 0 : finiteAt(schema.maximum, `${path}.maximum`, "INVALID_MANIFEST");
|
|
90
|
+
if (minimum !== void 0 && maximum !== void 0 && minimum > maximum)
|
|
91
|
+
fail("INVALID_MANIFEST", path, "minimum cannot exceed maximum");
|
|
92
|
+
} else if (type === "boolean" || type === "null") {
|
|
93
|
+
allowedKeys(schema, common, path, "INVALID_MANIFEST");
|
|
94
|
+
} else if (type === "array") {
|
|
95
|
+
allowedKeys(schema, [...common, "items", "minItems", "maxItems"], path, "INVALID_MANIFEST");
|
|
96
|
+
assertConfigSchema(schema.items, `${path}.items`);
|
|
97
|
+
const min = schema.minItems === void 0 ? void 0 : nonNegativeIntegerAt(schema.minItems, `${path}.minItems`, "INVALID_MANIFEST");
|
|
98
|
+
const max = schema.maxItems === void 0 ? void 0 : nonNegativeIntegerAt(schema.maxItems, `${path}.maxItems`, "INVALID_MANIFEST");
|
|
99
|
+
if (min !== void 0 && max !== void 0 && min > max)
|
|
100
|
+
fail("INVALID_MANIFEST", path, "minItems cannot exceed maxItems");
|
|
101
|
+
} else if (type === "object") {
|
|
102
|
+
allowedKeys(schema, [...common, "properties", "required", "additionalProperties"], path, "INVALID_MANIFEST");
|
|
103
|
+
const properties = schema.properties === void 0 ? {} : objectAt(schema.properties, `${path}.properties`, "INVALID_MANIFEST");
|
|
104
|
+
for (const [key, child] of Object.entries(properties)) {
|
|
105
|
+
if (!configKeyPattern.test(key))
|
|
106
|
+
fail("INVALID_MANIFEST", `${path}.properties.${key}`, "property names must be stable alphanumeric identifiers");
|
|
107
|
+
assertConfigSchema(child, `${path}.properties.${key}`);
|
|
108
|
+
}
|
|
109
|
+
if (schema.required !== void 0) {
|
|
110
|
+
if (!Array.isArray(schema.required) || schema.required.some((item) => typeof item !== "string"))
|
|
111
|
+
fail("INVALID_MANIFEST", `${path}.required`, "expected string array");
|
|
112
|
+
if (new Set(schema.required).size !== schema.required.length)
|
|
113
|
+
fail("INVALID_MANIFEST", `${path}.required`, "contains duplicate values");
|
|
114
|
+
for (const key of schema.required)
|
|
115
|
+
if (!Object.hasOwn(properties, key))
|
|
116
|
+
fail("INVALID_MANIFEST", `${path}.required`, `unknown required property ${key}`);
|
|
117
|
+
}
|
|
118
|
+
if (schema.additionalProperties !== void 0 && typeof schema.additionalProperties !== "boolean")
|
|
119
|
+
fail("INVALID_MANIFEST", `${path}.additionalProperties`, "expected boolean");
|
|
120
|
+
} else {
|
|
121
|
+
fail("INVALID_MANIFEST", `${path}.type`, `unsupported configuration type ${String(type)}`);
|
|
122
|
+
}
|
|
123
|
+
if (schema.default !== void 0)
|
|
124
|
+
readConfigValue(schema, schema.default, `${path}.default`, "INVALID_MANIFEST");
|
|
125
|
+
return schema;
|
|
126
|
+
}
|
|
127
|
+
function readJson(value, path, code) {
|
|
128
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
129
|
+
return value;
|
|
130
|
+
if (typeof value === "number") {
|
|
131
|
+
if (!Number.isFinite(value))
|
|
132
|
+
fail(code, path, "numbers must be finite");
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
if (Array.isArray(value))
|
|
136
|
+
return value.map((item, index) => readJson(item, `${path}[${index}]`, code));
|
|
137
|
+
const object = objectAt(value, path, code);
|
|
138
|
+
return Object.fromEntries(Object.entries(object).map(([key, item]) => [
|
|
139
|
+
key,
|
|
140
|
+
readJson(item, `${path}.${key}`, code)
|
|
141
|
+
]));
|
|
142
|
+
}
|
|
143
|
+
function readConfigValue(schema, input, path, code = "INVALID_CONFIG") {
|
|
144
|
+
if (input === void 0) {
|
|
145
|
+
if (schema.default !== void 0)
|
|
146
|
+
return readConfigValue(schema, schema.default, path, code);
|
|
147
|
+
fail(code, path, "value is required");
|
|
148
|
+
}
|
|
149
|
+
if (schema.type === "string") {
|
|
150
|
+
if (typeof input !== "string")
|
|
151
|
+
fail(code, path, "expected string");
|
|
152
|
+
if (schema.minLength !== void 0 && input.length < schema.minLength)
|
|
153
|
+
fail(code, path, `must contain at least ${schema.minLength} characters`);
|
|
154
|
+
if (schema.maxLength !== void 0 && input.length > schema.maxLength)
|
|
155
|
+
fail(code, path, `must contain at most ${schema.maxLength} characters`);
|
|
156
|
+
if (schema.enum && !schema.enum.includes(input))
|
|
157
|
+
fail(code, path, "is not an allowed value");
|
|
158
|
+
if (schema.pattern && !new RegExp(schema.pattern).test(input))
|
|
159
|
+
fail(code, path, "does not match the required pattern");
|
|
160
|
+
return input;
|
|
161
|
+
}
|
|
162
|
+
if (schema.type === "number" || schema.type === "integer") {
|
|
163
|
+
if (typeof input !== "number" || !Number.isFinite(input))
|
|
164
|
+
fail(code, path, "expected finite number");
|
|
165
|
+
if (schema.type === "integer" && !Number.isInteger(input))
|
|
166
|
+
fail(code, path, "expected integer");
|
|
167
|
+
if (schema.minimum !== void 0 && input < schema.minimum)
|
|
168
|
+
fail(code, path, `must be at least ${schema.minimum}`);
|
|
169
|
+
if (schema.maximum !== void 0 && input > schema.maximum)
|
|
170
|
+
fail(code, path, `must be at most ${schema.maximum}`);
|
|
171
|
+
return input;
|
|
172
|
+
}
|
|
173
|
+
if (schema.type === "boolean") {
|
|
174
|
+
if (typeof input !== "boolean")
|
|
175
|
+
fail(code, path, "expected boolean");
|
|
176
|
+
return input;
|
|
177
|
+
}
|
|
178
|
+
if (schema.type === "null") {
|
|
179
|
+
if (input !== null)
|
|
180
|
+
fail(code, path, "expected null");
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
if (schema.type === "array") {
|
|
184
|
+
if (!Array.isArray(input))
|
|
185
|
+
fail(code, path, "expected array");
|
|
186
|
+
if (schema.minItems !== void 0 && input.length < schema.minItems)
|
|
187
|
+
fail(code, path, `must contain at least ${schema.minItems} items`);
|
|
188
|
+
if (schema.maxItems !== void 0 && input.length > schema.maxItems)
|
|
189
|
+
fail(code, path, `must contain at most ${schema.maxItems} items`);
|
|
190
|
+
return input.map((item, index) => readConfigValue(schema.items, item, `${path}[${index}]`, code));
|
|
191
|
+
}
|
|
192
|
+
const objectSchema = schema;
|
|
193
|
+
const inputObject = objectAt(input, path, code);
|
|
194
|
+
const properties = objectSchema.properties ?? {};
|
|
195
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
196
|
+
for (const [key, child] of Object.entries(properties)) {
|
|
197
|
+
if (Object.hasOwn(inputObject, key))
|
|
198
|
+
result[key] = readConfigValue(child, inputObject[key], `${path}.${key}`, code);
|
|
199
|
+
else if (child.default !== void 0)
|
|
200
|
+
result[key] = readConfigValue(child, child.default, `${path}.${key}`, code);
|
|
201
|
+
else if (objectSchema.required?.includes(key))
|
|
202
|
+
fail(code, `${path}.${key}`, "value is required");
|
|
203
|
+
}
|
|
204
|
+
for (const [key, value] of Object.entries(inputObject)) {
|
|
205
|
+
if (Object.hasOwn(properties, key))
|
|
206
|
+
continue;
|
|
207
|
+
if (!objectSchema.additionalProperties)
|
|
208
|
+
fail(code, `${path}.${key}`, "unknown configuration field");
|
|
209
|
+
result[key] = readJson(value, `${path}.${key}`, code);
|
|
210
|
+
}
|
|
211
|
+
return { ...result };
|
|
212
|
+
}
|
|
213
|
+
function validatePluginManifest(input) {
|
|
214
|
+
const manifest = objectAt(input, "$", "INVALID_MANIFEST");
|
|
215
|
+
allowedKeys(manifest, [
|
|
216
|
+
"schemaVersion",
|
|
217
|
+
"id",
|
|
218
|
+
"version",
|
|
219
|
+
"sdkRange",
|
|
220
|
+
"displayName",
|
|
221
|
+
"description",
|
|
222
|
+
"dependencies",
|
|
223
|
+
"config",
|
|
224
|
+
"contributions"
|
|
225
|
+
], "$", "INVALID_MANIFEST");
|
|
226
|
+
if (manifest.schemaVersion !== 1)
|
|
227
|
+
fail("INVALID_MANIFEST", "$.schemaVersion", "only schema version 1 is supported");
|
|
228
|
+
const id = stringAt(manifest.id, "$.id", "INVALID_MANIFEST", {
|
|
229
|
+
nonempty: true
|
|
230
|
+
});
|
|
231
|
+
if (!identifierPattern.test(id))
|
|
232
|
+
fail("INVALID_MANIFEST", "$.id", "expected a stable lowercase dotted or dashed identifier");
|
|
233
|
+
const version = stringAt(manifest.version, "$.version", "INVALID_MANIFEST", {
|
|
234
|
+
nonempty: true
|
|
235
|
+
});
|
|
236
|
+
if (!semverPattern.test(version))
|
|
237
|
+
fail("INVALID_MANIFEST", "$.version", "expected a semantic version");
|
|
238
|
+
if (manifest.sdkRange !== void 0) {
|
|
239
|
+
const sdkRange = stringAt(manifest.sdkRange, "$.sdkRange", "INVALID_MANIFEST", { nonempty: true });
|
|
240
|
+
if (!dependencyRangePattern.test(sdkRange))
|
|
241
|
+
fail("INVALID_MANIFEST", "$.sdkRange", "unsupported semantic version range");
|
|
242
|
+
}
|
|
243
|
+
stringAt(manifest.displayName, "$.displayName", "INVALID_MANIFEST", {
|
|
244
|
+
nonempty: true
|
|
245
|
+
});
|
|
246
|
+
if (manifest.description !== void 0)
|
|
247
|
+
stringAt(manifest.description, "$.description", "INVALID_MANIFEST", {
|
|
248
|
+
nonempty: true
|
|
249
|
+
});
|
|
250
|
+
if (manifest.dependencies !== void 0 && !Array.isArray(manifest.dependencies))
|
|
251
|
+
fail("INVALID_MANIFEST", "$.dependencies", "expected an array");
|
|
252
|
+
const seenDependencies = /* @__PURE__ */ new Set();
|
|
253
|
+
const dependencies = (manifest.dependencies ?? []).map((inputDependency, index) => {
|
|
254
|
+
const path = `$.dependencies[${index}]`;
|
|
255
|
+
const dependency = objectAt(inputDependency, path, "INVALID_MANIFEST");
|
|
256
|
+
allowedKeys(dependency, ["id", "range", "optional"], path, "INVALID_MANIFEST");
|
|
257
|
+
const dependencyId = stringAt(dependency.id, `${path}.id`, "INVALID_MANIFEST", { nonempty: true });
|
|
258
|
+
if (!identifierPattern.test(dependencyId))
|
|
259
|
+
fail("INVALID_MANIFEST", `${path}.id`, "invalid plugin identifier");
|
|
260
|
+
if (dependencyId === id)
|
|
261
|
+
fail("INVALID_MANIFEST", `${path}.id`, "plugin cannot depend on itself");
|
|
262
|
+
if (seenDependencies.has(dependencyId))
|
|
263
|
+
fail("INVALID_MANIFEST", `${path}.id`, "duplicate dependency");
|
|
264
|
+
seenDependencies.add(dependencyId);
|
|
265
|
+
const range = stringAt(dependency.range, `${path}.range`, "INVALID_MANIFEST", { nonempty: true });
|
|
266
|
+
if (!dependencyRangePattern.test(range))
|
|
267
|
+
fail("INVALID_MANIFEST", `${path}.range`, "unsupported semantic version range");
|
|
268
|
+
if (dependency.optional !== void 0 && typeof dependency.optional !== "boolean")
|
|
269
|
+
fail("INVALID_MANIFEST", `${path}.optional`, "expected boolean");
|
|
270
|
+
return {
|
|
271
|
+
id: dependencyId,
|
|
272
|
+
range,
|
|
273
|
+
...dependency.optional === void 0 ? {} : { optional: dependency.optional }
|
|
274
|
+
};
|
|
275
|
+
});
|
|
276
|
+
const config = manifest.config === void 0 ? void 0 : structuredClone(assertConfigSchema(manifest.config, "$.config"));
|
|
277
|
+
if (config && config.type !== "object")
|
|
278
|
+
fail("INVALID_MANIFEST", "$.config", "plugin config root must be an object");
|
|
279
|
+
if (manifest.contributions !== void 0 && !Array.isArray(manifest.contributions))
|
|
280
|
+
fail("INVALID_MANIFEST", "$.contributions", "expected an array");
|
|
281
|
+
const contributionIds = /* @__PURE__ */ new Set();
|
|
282
|
+
const contributions = (manifest.contributions ?? []).map((inputContribution, index) => {
|
|
283
|
+
const path = `$.contributions[${index}]`;
|
|
284
|
+
const contribution = objectAt(inputContribution, path, "INVALID_MANIFEST");
|
|
285
|
+
allowedKeys(contribution, ["id", "kind", "target"], path, "INVALID_MANIFEST");
|
|
286
|
+
const contributionId = stringAt(contribution.id, `${path}.id`, "INVALID_MANIFEST", { nonempty: true });
|
|
287
|
+
if (!identifierPattern.test(contributionId))
|
|
288
|
+
fail("INVALID_MANIFEST", `${path}.id`, "invalid contribution identifier");
|
|
289
|
+
if (!contributionId.startsWith(`${id}.`))
|
|
290
|
+
fail("INVALID_MANIFEST", `${path}.id`, `contribution must use the "${id}." namespace`);
|
|
291
|
+
if (contributionIds.has(contributionId))
|
|
292
|
+
fail("INVALID_MANIFEST", `${path}.id`, "duplicate contribution identifier");
|
|
293
|
+
contributionIds.add(contributionId);
|
|
294
|
+
const kind = stringAt(contribution.kind, `${path}.kind`, "INVALID_MANIFEST", { nonempty: true });
|
|
295
|
+
if (contribution.target !== void 0 && !["server", "web", "mobile"].includes(contribution.target))
|
|
296
|
+
fail("INVALID_MANIFEST", `${path}.target`, "expected server, web, or mobile");
|
|
297
|
+
return {
|
|
298
|
+
id: contributionId,
|
|
299
|
+
kind,
|
|
300
|
+
...contribution.target === void 0 ? {} : {
|
|
301
|
+
target: contribution.target
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
});
|
|
305
|
+
return {
|
|
306
|
+
schemaVersion: 1,
|
|
307
|
+
id,
|
|
308
|
+
version,
|
|
309
|
+
...manifest.sdkRange === void 0 ? {} : { sdkRange: manifest.sdkRange },
|
|
310
|
+
displayName: manifest.displayName,
|
|
311
|
+
...manifest.description === void 0 ? {} : { description: manifest.description },
|
|
312
|
+
...dependencies.length ? { dependencies } : {},
|
|
313
|
+
...config ? { config } : {},
|
|
314
|
+
...contributions.length ? { contributions } : {}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// node_modules/@smartdoca/plugin-sdk/package.json
|
|
319
|
+
var package_default = {
|
|
320
|
+
name: "@smartdoca/plugin-sdk",
|
|
321
|
+
version: "0.1.9",
|
|
322
|
+
license: "MIT",
|
|
323
|
+
type: "module",
|
|
324
|
+
exports: {
|
|
325
|
+
".": {
|
|
326
|
+
types: "./dist/index.d.ts",
|
|
327
|
+
import: "./dist/index.js"
|
|
328
|
+
},
|
|
329
|
+
"./version": {
|
|
330
|
+
types: "./dist/version.d.ts",
|
|
331
|
+
import: "./dist/version.js"
|
|
332
|
+
},
|
|
333
|
+
"./testing": {
|
|
334
|
+
types: "./dist/testing.d.ts",
|
|
335
|
+
import: "./dist/testing.js"
|
|
336
|
+
},
|
|
337
|
+
"./platform": {
|
|
338
|
+
types: "./dist/platform.d.ts",
|
|
339
|
+
import: "./dist/platform.js"
|
|
340
|
+
},
|
|
341
|
+
"./files": {
|
|
342
|
+
types: "./dist/files.d.ts",
|
|
343
|
+
import: "./dist/files.js"
|
|
344
|
+
},
|
|
345
|
+
"./ai": {
|
|
346
|
+
types: "./dist/ai.d.ts",
|
|
347
|
+
import: "./dist/ai.js"
|
|
348
|
+
},
|
|
349
|
+
"./search": {
|
|
350
|
+
types: "./dist/search.d.ts",
|
|
351
|
+
import: "./dist/search.js"
|
|
352
|
+
},
|
|
353
|
+
"./knowledge": {
|
|
354
|
+
types: "./dist/knowledge.d.ts",
|
|
355
|
+
import: "./dist/knowledge.js"
|
|
356
|
+
},
|
|
357
|
+
"./web": {
|
|
358
|
+
types: "./dist/web.d.ts",
|
|
359
|
+
import: "./dist/web.js"
|
|
360
|
+
},
|
|
361
|
+
"./content": {
|
|
362
|
+
types: "./dist/content.d.ts",
|
|
363
|
+
import: "./dist/content.js"
|
|
364
|
+
},
|
|
365
|
+
"./documents": {
|
|
366
|
+
types: "./dist/documents.d.ts",
|
|
367
|
+
import: "./dist/documents.js"
|
|
368
|
+
},
|
|
369
|
+
"./native": {
|
|
370
|
+
types: "./dist/native.d.ts",
|
|
371
|
+
import: "./dist/native.js"
|
|
372
|
+
},
|
|
373
|
+
"./creation-resources": {
|
|
374
|
+
types: "./dist/creation-resources.d.ts",
|
|
375
|
+
import: "./dist/creation-resources.js"
|
|
376
|
+
},
|
|
377
|
+
"./editor-elements": {
|
|
378
|
+
types: "./dist/editor-elements.d.ts",
|
|
379
|
+
import: "./dist/editor-elements.js"
|
|
380
|
+
},
|
|
381
|
+
"./storage": {
|
|
382
|
+
types: "./dist/storage.d.ts",
|
|
383
|
+
import: "./dist/storage.js"
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
dependencies: {
|
|
387
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
388
|
+
"@smartdoca/plugin-contracts": "0.1.7",
|
|
389
|
+
"@smartdoca/files-capability": "0.1.1",
|
|
390
|
+
"@smartdoca/search-host": "0.1.1",
|
|
391
|
+
"@smartdoca/knowledge-capability": "0.1.1",
|
|
392
|
+
"@smartdoca/web-plugin-registry": "0.1.3"
|
|
393
|
+
},
|
|
394
|
+
files: [
|
|
395
|
+
"dist"
|
|
396
|
+
],
|
|
397
|
+
publishConfig: {
|
|
398
|
+
access: "public"
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// node_modules/@smartdoca/plugin-sdk/dist/version.js
|
|
403
|
+
var PLUGIN_SDK_VERSION = package_default.version;
|
|
404
|
+
|
|
405
|
+
// node_modules/@smartdoca/plugin-sdk/dist/index.js
|
|
406
|
+
var contractIdentifier = /^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/;
|
|
407
|
+
function assertIdentifier(value, kind) {
|
|
408
|
+
if (!contractIdentifier.test(value))
|
|
409
|
+
throw new PluginContractError("INVALID_MANIFEST", `${kind} must be a stable lowercase dotted or dashed identifier`, value || "$");
|
|
410
|
+
}
|
|
411
|
+
function defineService(id) {
|
|
412
|
+
assertIdentifier(id, "service identifier");
|
|
413
|
+
return Object.freeze({ id });
|
|
414
|
+
}
|
|
415
|
+
function definePlugin(plugin) {
|
|
416
|
+
const manifest = validatePluginManifest(plugin.manifest);
|
|
417
|
+
const required = plugin.injections?.required ?? [];
|
|
418
|
+
const optional = plugin.injections?.optional ?? [];
|
|
419
|
+
const seen = /* @__PURE__ */ new Set();
|
|
420
|
+
for (const token of [...required, ...optional]) {
|
|
421
|
+
assertIdentifier(token.id, "service identifier");
|
|
422
|
+
if (seen.has(token.id))
|
|
423
|
+
throw new PluginContractError("INVALID_MANIFEST", `service ${token.id} is declared more than once`, `${manifest.id}.injections`);
|
|
424
|
+
seen.add(token.id);
|
|
425
|
+
}
|
|
426
|
+
return Object.freeze({ ...plugin, manifest });
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// node_modules/@smartdoca/plugin-sdk/dist/platform.js
|
|
430
|
+
var usersServiceToken = defineService("users.v1");
|
|
431
|
+
var permissionsServiceToken = defineService("permissions.v1");
|
|
432
|
+
var httpServiceToken = defineService("http.v1");
|
|
433
|
+
var policiesServiceToken = defineService("policies.v1");
|
|
434
|
+
var eventsServiceToken = defineService("events.v1");
|
|
435
|
+
var notificationsServiceToken = defineService("notifications.v1");
|
|
436
|
+
var activityServiceToken = defineService("activity.v1");
|
|
437
|
+
|
|
438
|
+
// node_modules/@smartdoca/plugin-sdk/dist/storage.js
|
|
439
|
+
var pluginDatabaseToken = defineService("storage.sql.v1");
|
|
440
|
+
var pluginObjectStorageToken = defineService("storage.objects.v1");
|
|
441
|
+
var pluginCredentialToken = defineService("storage.credentials.v1");
|
|
442
|
+
|
|
443
|
+
// shared/http.ts
|
|
444
|
+
var ApiError = class extends Error {
|
|
445
|
+
constructor(status, message) {
|
|
446
|
+
super(message);
|
|
447
|
+
this.status = status;
|
|
448
|
+
}
|
|
449
|
+
status;
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
// shared/preferences.ts
|
|
453
|
+
var preferenceTable = {
|
|
454
|
+
columns: { user_id: { type: "text", nullable: false }, name: { type: "text", nullable: false }, value: { type: "text", nullable: false }, revision: { type: "integer", nullable: false } },
|
|
455
|
+
primaryKey: ["user_id", "name"],
|
|
456
|
+
unique: []
|
|
457
|
+
};
|
|
458
|
+
var MAX_REVISION = 2147483647;
|
|
459
|
+
function revisionValid(value) {
|
|
460
|
+
return Number.isInteger(value) && Number(value) >= 0 && Number(value) < MAX_REVISION;
|
|
461
|
+
}
|
|
462
|
+
function storedPreference(row, validate) {
|
|
463
|
+
try {
|
|
464
|
+
const value = JSON.parse(String(row.value));
|
|
465
|
+
if (!validate(value) || !Number.isInteger(row.revision) || Number(row.revision) < 1 || Number(row.revision) > MAX_REVISION) throw Error();
|
|
466
|
+
return { value, revision: Number(row.revision) };
|
|
467
|
+
} catch {
|
|
468
|
+
throw new ApiError(503, "\u4FDD\u5B58\u7684\u8BBE\u7F6E\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002");
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function preferenceRoutes(database, validators) {
|
|
472
|
+
const validator = (name) => {
|
|
473
|
+
if (typeof name !== "string" || !Object.hasOwn(validators, name)) throw new ApiError(400, "\u8BBE\u7F6E\u540D\u79F0\u65E0\u6548\u3002");
|
|
474
|
+
return validators[name];
|
|
475
|
+
};
|
|
476
|
+
const where = (userId, name) => [{ column: "user_id", operator: "=", value: userId }, { column: "name", operator: "=", value: name }];
|
|
477
|
+
return [
|
|
478
|
+
{ method: "GET", path: "/preferences", async handle(req) {
|
|
479
|
+
const name = req.query.name, validate = validator(name);
|
|
480
|
+
const [row] = await database().select("preferences", { where: where(req.principal.id, String(name)), limit: 1 });
|
|
481
|
+
return row ? storedPreference(row, validate) : { value: null, revision: 0 };
|
|
482
|
+
} },
|
|
483
|
+
{ method: "POST", path: "/preferences", bodyLimit: 512 * 1024, async handle(req) {
|
|
484
|
+
const body = req.body;
|
|
485
|
+
if (!body || typeof body !== "object" || Array.isArray(body) || Object.keys(body).some((k) => !["name", "value", "revision"].includes(k))) throw new ApiError(400, "\u8BBE\u7F6E\u53C2\u6570\u65E0\u6548\u3002");
|
|
486
|
+
const validate = validator(body.name);
|
|
487
|
+
if (!validate(body.value) || !revisionValid(body.revision)) throw new ApiError(400, "\u8BBE\u7F6E\u5185\u5BB9\u6216\u7248\u672C\u53F7\u65E0\u6548\u3002");
|
|
488
|
+
const serialized = JSON.stringify(body.value);
|
|
489
|
+
if (Buffer.byteLength(serialized) > 256 * 1024) throw new ApiError(400, "\u8BBE\u7F6E\u5185\u5BB9\u8FC7\u957F\u3002");
|
|
490
|
+
const filters = where(req.principal.id, String(body.name)), expected = body.revision;
|
|
491
|
+
const result = await database().transaction(async (tx) => {
|
|
492
|
+
const [row] = await tx.select("preferences", { where: filters, limit: 1 });
|
|
493
|
+
if (row) {
|
|
494
|
+
try {
|
|
495
|
+
storedPreference(row, validate);
|
|
496
|
+
} catch {
|
|
497
|
+
return "invalid";
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if ((row?.revision ?? 0) !== expected) return "conflict";
|
|
501
|
+
if (!row) await tx.insert("preferences", [{ user_id: req.principal.id, name: String(body.name), value: serialized, revision: expected + 1 }]);
|
|
502
|
+
else if (await tx.update("preferences", { value: serialized, revision: expected + 1 }, [...filters, { column: "revision", operator: "=", value: expected }]) !== 1) return "conflict";
|
|
503
|
+
return "saved";
|
|
504
|
+
});
|
|
505
|
+
if (result === "invalid") throw new ApiError(503, "\u4FDD\u5B58\u7684\u8BBE\u7F6E\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002");
|
|
506
|
+
if (result === "conflict") throw new ApiError(409, "\u8BBE\u7F6E\u5DF2\u5728\u5176\u4ED6\u7A97\u53E3\u4FEE\u6539\uFF0C\u5DF2\u91CD\u65B0\u8F7D\u5165\uFF0C\u8BF7\u518D\u8BD5\u4E00\u6B21\u3002");
|
|
507
|
+
return { value: body.value, revision: expected + 1 };
|
|
508
|
+
} }
|
|
509
|
+
];
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// shared/server.ts
|
|
513
|
+
function serverPlugin(manifest, routes, options) {
|
|
514
|
+
const metadata = validatePluginManifest(manifest);
|
|
515
|
+
let context;
|
|
516
|
+
const allRoutes = [...routes, ...preferenceRoutes(() => context.inject(pluginDatabaseToken), options.preferences)];
|
|
517
|
+
return definePlugin({
|
|
518
|
+
manifest: metadata,
|
|
519
|
+
injections: { required: [httpServiceToken, pluginDatabaseToken], optional: options.optional },
|
|
520
|
+
uninstall() {
|
|
521
|
+
},
|
|
522
|
+
async initialize(lifecycle) {
|
|
523
|
+
await lifecycle.inject(pluginDatabaseToken).defineSchema({ version: 1, tables: { preferences: preferenceTable, ...options.tables } });
|
|
524
|
+
},
|
|
525
|
+
async mount(lifecycle) {
|
|
526
|
+
context = lifecycle;
|
|
527
|
+
await options.mount?.(lifecycle);
|
|
528
|
+
await lifecycle.effectAsync(() => lifecycle.inject(httpServiceToken).register(metadata.id, allRoutes.map((route) => {
|
|
529
|
+
if (route.auth === "external") throw Error("Everyday plugins require a host session");
|
|
530
|
+
return { ...route, async handle(request, response) {
|
|
531
|
+
response.header("Cache-Control", "no-store");
|
|
532
|
+
try {
|
|
533
|
+
return await route.handle(request, response);
|
|
534
|
+
} catch (error) {
|
|
535
|
+
if (request.signal.aborted) throw error;
|
|
536
|
+
const code = error?.code;
|
|
537
|
+
response.status(error instanceof ApiError ? error.status : code === "conflict" ? 409 : 503);
|
|
538
|
+
return { error: error instanceof ApiError ? error.message : code === "conflict" ? "\u8BBE\u7F6E\u5DF2\u5728\u5176\u4ED6\u7A97\u53E3\u4FEE\u6539\uFF0C\u8BF7\u91CD\u65B0\u8F7D\u5165\u540E\u91CD\u8BD5\u3002" : "\u63D2\u4EF6\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" };
|
|
539
|
+
}
|
|
540
|
+
} };
|
|
541
|
+
})));
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// pomodoro/src/model.ts
|
|
547
|
+
function isTimer(v) {
|
|
548
|
+
if (!v || typeof v !== "object") return false;
|
|
549
|
+
const s = v;
|
|
550
|
+
return ["focus", "short", "long"].includes(s.phase) && ["idle", "running", "paused"].includes(s.status) && (s.deadline === null || Number.isFinite(s.deadline) && s.deadline > 0) && s.status === "running" === (s.deadline !== null) && Number.isFinite(s.remaining) && s.remaining >= 0 && s.remaining <= 120 * 6e4 && !!s.durations && [s.durations.focus, s.durations.short, s.durations.long].every((n) => Number.isInteger(n) && n >= 1 && n <= 120) && Number.isInteger(s.completed) && s.completed >= 0 && /^\d{4}-\d{2}-\d{2}$/.test(s.day);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// pomodoro/manifest.json
|
|
554
|
+
var manifest_default = {
|
|
555
|
+
schemaVersion: 1,
|
|
556
|
+
id: "doca.easy.pomodoro",
|
|
557
|
+
version: "0.1.1",
|
|
558
|
+
displayName: "Pomodoro / \u756A\u8304\u949F",
|
|
559
|
+
sdkRange: "^0.1.9"
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// pomodoro/src/server.ts
|
|
563
|
+
var server_default = () => serverPlugin(manifest_default, [], { preferences: { timer: isTimer } });
|
|
564
|
+
export {
|
|
565
|
+
server_default as default
|
|
566
|
+
};
|
package/manifest.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartdoca/doca-easy-pomodoro-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "番茄钟 / Pomodoro",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.13.0"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"manifest.json",
|
|
12
|
+
"dist/server.js",
|
|
13
|
+
"dist/server.d.ts",
|
|
14
|
+
"web/index.js",
|
|
15
|
+
"web/index.d.ts",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"THIRD_PARTY_NOTICES.txt"
|
|
19
|
+
],
|
|
20
|
+
"doca": {
|
|
21
|
+
"dataVersion": "2",
|
|
22
|
+
"manifest": "./manifest.json",
|
|
23
|
+
"server": "./dist/server.js",
|
|
24
|
+
"web": {
|
|
25
|
+
"directory": "./web",
|
|
26
|
+
"entry": "./index.js"
|
|
27
|
+
},
|
|
28
|
+
"storage": "host"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public",
|
|
32
|
+
"registry": "https://registry.npmjs.org/"
|
|
33
|
+
},
|
|
34
|
+
"main": "./dist/server.js",
|
|
35
|
+
"types": "./dist/server.d.ts"
|
|
36
|
+
}
|
package/web/index.d.ts
ADDED
package/web/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
var O=`.ep-root{--ep-bg:#fff;--ep-ink:#172c39;--ep-muted:#6c7e89;--ep-line:#e4e9eb;--ep-soft:#f4f7f7;--ep-accent:#157a70;--ep-tint:#e7f4ef;box-sizing:border-box;background:var(--ep-bg);color:var(--ep-ink);font:14px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC",sans-serif;border:1px solid var(--ep-line);border-radius:20px;padding:24px;min-width:0;width:100%;text-align:left;container-type:inline-size}
|
|
2
|
+
.ep-root[data-theme=soft]{--ep-bg:#f8faf8;--ep-soft:#edf3ee}.ep-root *{box-sizing:border-box}.ep-root h1,.ep-root h2,.ep-root h3,.ep-root p{margin:0}.ep-root h1{font-size:30px;line-height:1.3;letter-spacing:-1px}.ep-root h2{font-size:22px;letter-spacing:-.6px}.ep-root h3{font-size:16px}.ep-root button,.ep-root input,.ep-root textarea,.ep-root select{font:inherit}.ep-root button{cursor:pointer}.ep-root button:disabled{cursor:default;opacity:.5}.ep-root button:focus-visible,.ep-root input:focus-visible,.ep-root textarea:focus-visible,.ep-root select:focus-visible{outline:3px solid #5bbaad;outline-offset:3px}.ep-root input,.ep-root textarea,.ep-root select{border:1px solid var(--ep-line);border-radius:11px;padding:10px 12px;background:var(--ep-bg);color:var(--ep-ink);min-width:0}.ep-root textarea{resize:vertical;width:100%;min-height:140px}.ep-root a{color:var(--ep-accent)}
|
|
3
|
+
.ep-root a.ep-button{color:white;text-decoration:none}
|
|
4
|
+
.ep-head{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:22px}.ep-kicker{color:var(--ep-muted);font-size:10px;letter-spacing:2px;font-weight:700;text-transform:uppercase;margin-bottom:6px}.ep-muted{color:var(--ep-muted);font-size:12px}.ep-actions,.ep-row{display:flex;align-items:center;gap:9px;flex-wrap:wrap}.ep-between{justify-content:space-between}.ep-button{border:0;border-radius:11px;padding:10px 16px;background:var(--ep-accent);color:white;font-weight:600;display:inline-flex;align-items:center;justify-content:center;gap:6px}.ep-button.secondary{background:var(--ep-soft);color:var(--ep-ink)}.ep-button.ghost{background:transparent;color:var(--ep-muted);border:1px solid var(--ep-line)}.ep-button.small{padding:6px 10px;font-size:12px}.ep-search{display:flex;gap:9px;margin:14px 0}.ep-search input{flex:1}.ep-box{padding:16px;background:var(--ep-soft);border-radius:13px}.ep-error{color:#a23d36;background:#fff0ee;border:1px solid #f1d1cb;padding:12px;border-radius:12px;margin:12px 0;white-space:pre-wrap}.ep-empty{padding:25px 10px;text-align:center;color:var(--ep-muted)}.ep-loading{min-height:100px;display:grid;place-items:center;color:var(--ep-muted)}.ep-foot{border-top:1px solid var(--ep-line);margin-top:18px;padding-top:13px;display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;color:var(--ep-muted);font-size:11px}.ep-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.ep-tag{display:inline-block;border-radius:6px;padding:2px 7px;background:var(--ep-tint);color:var(--ep-accent);font-size:11px}.ep-tabs{display:flex;gap:6px;margin:15px 0;flex-wrap:wrap}.ep-tabs button{border:0;border-radius:9px;padding:8px 13px;background:var(--ep-soft);color:var(--ep-muted)}.ep-tabs button.active{background:var(--ep-ink);color:white}.ep-divider{border:0;border-top:1px solid var(--ep-line);margin:20px 0}.ep-page{max-width:1180px;margin:24px auto;padding:32px}.ep-list{list-style:none;margin:12px 0;padding:0}.ep-list li{border-bottom:1px solid var(--ep-line);padding:10px 0}.ep-result-button{display:block;width:100%;border:0;border-bottom:1px solid var(--ep-line);background:transparent;text-align:left;padding:12px;color:var(--ep-ink)}.ep-result-button:hover{background:var(--ep-soft)}.ep-field{display:grid;gap:5px;font-size:12px;color:var(--ep-muted)}.ep-alert{font-size:12px;color:var(--ep-muted);padding:10px 0}.ep-visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}
|
|
5
|
+
@container (max-width:420px){.ep-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.ep-head{align-items:flex-start}.ep-root h1{font-size:24px}}
|
|
6
|
+
@media(max-width:600px){.ep-root{padding:18px;border-radius:15px}.ep-page{margin:12px;padding:20px}.ep-search{flex-wrap:wrap}.ep-search input{min-width:160px}}
|
|
7
|
+
@media(prefers-reduced-motion:reduce){.ep-root *{animation:none!important;transition:none!important}}
|
|
8
|
+
`;var q=(n,o,l)=>n==="zh"?o:l;function G(n,o){let l=n.React;function t({children:y,className:T=""}){let b=n.useEnvironment();return l.createElement("section",{className:`ep-root ${T}`,"data-theme":b.theme,lang:b.locale==="zh"?"zh-CN":"en"},l.createElement("style",null,O),y)}function h(y,T,b){let g=n.useEnvironment().locale,[u,E]=l.useState(T),[m,z]=l.useState(!1),[B,x]=l.useState(""),r=l.useRef({value:T,revision:0}),s=l.useRef(!1),e=l.useRef(!1),w=l.useRef(async()=>{}),$=l.useRef(null),f=l.useRef(""),M=l.useRef(null),d=v=>{if(!Number.isInteger(v.revision)||v.revision<0||(v.revision===0?v.value!==null:!b(v.value)))throw Error(q(g,"\u4FDD\u5B58\u7684\u8BBE\u7F6E\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002","Saved settings could not be read. Contact your administrator."));let k=v.revision===0?T:v.value;JSON.stringify(r.current.value)!==JSON.stringify(k)&&E(k),r.current={value:k,revision:v.revision}};return l.useEffect(()=>{e.current=!0;let v,k=null,C=async(a=!1)=>{if(s.current||!e.current)return;M.current?.abort();let p=new AbortController;M.current=p;try{let c=await n.request(`/preferences?name=${encodeURIComponent(y)}`,{signal:p.signal});!p.signal.aborted&&e.current&&(d(c),z(!0),a||x(""))}catch(c){!p.signal.aborted&&e.current&&(z(!1),x(c.message))}};w.current=C;let H=a=>{a.detail===f.current&&C()},N=()=>{document.visibilityState==="visible"&&C()};return window.addEventListener("doca.everyday.preference",H),document.addEventListener("visibilitychange",N),n.platform.users.me().then(async a=>{e.current&&(f.current=`${o}:${a.id}:${y}`,typeof BroadcastChannel<"u"&&(k=new BroadcastChannel(`doca.everyday.${f.current}`),$.current=k,k.onmessage=()=>{C()}),await C(),e.current&&(v=window.setInterval(N,15e3)))}).catch(()=>{e.current&&x(q(g,"\u65E0\u6CD5\u8F7D\u5165\u5F53\u524D\u7528\u6237\u8BBE\u7F6E\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u540E\u91CD\u8BD5\u3002","Could not load your settings. Sign in again."))}),()=>{e.current=!1,M.current?.abort(),clearInterval(v),k?.close(),$.current=null,window.removeEventListener("doca.everyday.preference",H),document.removeEventListener("visibilitychange",N)}},[y]),[u,async v=>{if(!m||s.current||!e.current)return!1;let k=typeof v=="function"?v(r.current.value):v;if(!b(k))throw Error("Invalid preference");if(JSON.stringify(k)===JSON.stringify(r.current.value))return!0;s.current=!0,z(!1),M.current?.abort();let C=new AbortController;M.current=C;let H=!1;try{let N=await n.request("/preferences",{method:"POST",signal:C.signal,headers:{"Content-Type":"application/json"},body:JSON.stringify({name:y,value:k,revision:r.current.revision})});e.current&&!C.signal.aborted&&(d(N),x(""),H=!0)}catch(N){e.current&&!C.signal.aborted&&x(N.message)}finally{s.current=!1,e.current&&(H?(z(!0),window.dispatchEvent(new CustomEvent("doca.everyday.preference",{detail:f.current})),$.current?.postMessage("reload")):await w.current(!0))}return H},m,B]}return{Frame:t,usePreference:h}}function D(n){let o=new Date(n);return`${o.getFullYear()}-${String(o.getMonth()+1).padStart(2,"0")}-${String(o.getDate()).padStart(2,"0")}`}function j(n=Date.now()){return{phase:"focus",status:"idle",deadline:null,remaining:25*6e4,durations:{focus:25,short:5,long:15},completed:0,day:D(n)}}function W(n){if(!n||typeof n!="object")return!1;let o=n;return["focus","short","long"].includes(o.phase)&&["idle","running","paused"].includes(o.status)&&(o.deadline===null||Number.isFinite(o.deadline)&&o.deadline>0)&&o.status==="running"==(o.deadline!==null)&&Number.isFinite(o.remaining)&&o.remaining>=0&&o.remaining<=120*6e4&&!!o.durations&&[o.durations.focus,o.durations.short,o.durations.long].every(l=>Number.isInteger(l)&&l>=1&&l<=120)&&Number.isInteger(o.completed)&&o.completed>=0&&/^\d{4}-\d{2}-\d{2}$/.test(o.day)}function F(n,o){return n.status==="running"?Math.max(0,n.deadline-o):n.remaining}function X(n,o){return n.status==="running"?n:{...n,status:"running",deadline:o+n.remaining}}function Z(n,o){return n.status!=="running"?n:{...n,status:"paused",deadline:null,remaining:F(n,o)}}function R(n,o=n.phase){return{...n,phase:o,status:"idle",deadline:null,remaining:n.durations[o]*6e4}}function V(n,o){if(n.status!=="running"||n.deadline>o)return n;let l=n.phase==="focus",t=D(o),h=(n.day===t?n.completed:0)+(l?1:0),y=l?h%4===0?"long":"short":"focus";return{...n,phase:y,status:"idle",deadline:null,remaining:n.durations[y]*6e4,completed:h,day:t}}function J(n){let o=Math.ceil(n/1e3);return`${String(Math.floor(o/60)).padStart(2,"0")}:${String(o%60).padStart(2,"0")}`}var L={schemaVersion:1,id:"doca.easy.pomodoro",version:"0.1.1",displayName:"Pomodoro / \u756A\u8304\u949F",sdkRange:"^0.1.9"};var te={focus:'<circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="3"/><path d="M12 2v2m0 16v2M2 12h2m16 0h2"/>',break:'<path d="M5 7h12v7a5 5 0 0 1-5 5h-2a5 5 0 0 1-5-5V7Zm12 1h1a3 3 0 0 1 0 6h-1M8 2v2m4-2v2M4 22h15"/>',paused:'<path d="M8 5v14M16 5v14"/>',complete:'<circle cx="12" cy="12" r="9"/><path d="m8 12 3 3 5-6"/>',clock:'<circle cx="12" cy="13" r="8"/><path d="M12 9v4l3 2M9 2h6M12 2v3"/>',settings:'<path d="M10 3h4l.5 2.2 2 1.2 2.2-.7 2 3.6-1.6 1.5v2.4l1.6 1.5-2 3.6-2.2-.7-2 1.2L14 21h-4l-.5-2.2-2-1.2-2.2.7-2-3.6 1.6-1.5v-2.4L3.3 9.3l2-3.6 2.2.7 2-1.2L10 3Z"/><circle cx="12" cy="12" r="3"/>'};function U(n){return`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${te[n]}</svg>`}function K(n,o){let l=document.activeElement instanceof HTMLElement?document.activeElement:null,t=document.createElement("div");t.className="pomodoro-float",t.popover="manual",t.setAttribute("data-plugin",L.id),t.innerHTML=`<style></style>
|
|
9
|
+
<section class="ep-root pomodoro-card" role="dialog" aria-modal="false" aria-labelledby="pomodoro-title" hidden>
|
|
10
|
+
<header class="pomodoro-drag" tabindex="0" data-drag>
|
|
11
|
+
<span class="pomodoro-symbol" data-icon></span>
|
|
12
|
+
<div class="pomodoro-heading"><span class="ep-kicker">ONE THING AT A TIME</span><h2 id="pomodoro-title" data-text="title"></h2></div>
|
|
13
|
+
<div class="pomodoro-window-actions"><button type="button" data-action="minimize">\u2212</button><button type="button" data-action="close">\xD7</button></div>
|
|
14
|
+
</header>
|
|
15
|
+
<div class="pomodoro-notice" role="alert" hidden><span>${U("complete")}</span><div><strong data-text="noticeTitle"></strong><p data-text="noticeBody"></p></div></div>
|
|
16
|
+
<div class="pomodoro-phase-row"><div class="ep-tabs">${["focus","short","long"].map(r=>`<button type="button" data-phase="${r}"></button>`).join("")}</div><button type="button" class="pomodoro-settings" data-action="settings">${U("settings")}</button></div>
|
|
17
|
+
<div class="ep-box ep-grid pomodoro-settings-panel" hidden>${["focus","short","long"].map(r=>`<label class="ep-field"><span data-duration-label="${r}"></span><input type="number" min="1" max="120" step="1" data-duration="${r}"></label>`).join("")}</div>
|
|
18
|
+
<div class="pomodoro-ring"><div><span class="pomodoro-status" data-text="status"></span><strong data-countdown></strong><span class="ep-muted" data-text="hint"></span></div></div>
|
|
19
|
+
<div class="ep-actions pomodoro-controls"><button type="button" class="ep-button" data-action="toggle"></button><button type="button" class="ep-button secondary" data-action="reset" data-text="reset"></button></div>
|
|
20
|
+
<p class="ep-error" role="alert" hidden></p>
|
|
21
|
+
<footer class="ep-foot"><span><span data-text="today"></span> <strong data-completed></strong> <span data-text="sessions"></span></span><span data-text="cycle"></span></footer>
|
|
22
|
+
<p class="pomodoro-tip" data-text="tip"></p>
|
|
23
|
+
</section>
|
|
24
|
+
<section class="ep-root pomodoro-mini" hidden>
|
|
25
|
+
<span class="pomodoro-mini-grip" tabindex="0" data-drag>\u283F</span>
|
|
26
|
+
<button type="button" class="pomodoro-mini-expand" data-action="expand"><span class="pomodoro-symbol" data-icon></span><strong data-countdown></strong><span class="ep-visually-hidden" data-text="status"></span></button>
|
|
27
|
+
<button type="button" class="pomodoro-mini-close" data-action="close">\xD7</button>
|
|
28
|
+
</section>`,t.querySelector("style").textContent=n,document.body.append(t);let h=t.querySelector(".pomodoro-card"),y=t.querySelector(".pomodoro-mini"),T=null,b=null,g=null,u=null,E=12;function m(){if(!t.matches(":popover-open"))return;let r=t.getBoundingClientRect(),s=window.visualViewport,e=s?.width??window.innerWidth,w=s?.height??window.innerHeight,$=s?.offsetLeft??0,f=s?.offsetTop??0;b=Math.max($+E,Math.min(b??$+e-r.width-24,$+e-r.width-E)),g=Math.max(f+E,Math.min(g??f+w-r.height-24,f+w-r.height-E)),t.style.left=`${b}px`,t.style.top=`${g}px`,t.style.setProperty("--pomodoro-max-height",`${Math.max(100,w-E*2)}px`)}function z(){let r=u;u=null,t.classList.remove("is-dragging"),r?.target.hasPointerCapture(r.id)&&r.target.releasePointerCapture(r.id)}t.addEventListener("pointerdown",r=>{let s=r.target,e=s.closest("[data-drag]");if(!e||s.closest("button, input")||r.button!==0)return;let w=t.getBoundingClientRect();u={id:r.pointerId,x:r.clientX,y:r.clientY,left:w.left,top:w.top,target:e},e.setPointerCapture(r.pointerId),t.classList.add("is-dragging"),r.preventDefault()}),t.addEventListener("pointermove",r=>{!u||u.id!==r.pointerId||(b=u.left+r.clientX-u.x,g=u.top+r.clientY-u.y,m())}),t.addEventListener("pointerup",z),t.addEventListener("pointercancel",z),t.addEventListener("lostpointercapture",z),t.addEventListener("keydown",r=>{let s=r.target;if(r.key==="Escape"){r.preventDefault(),o.close();return}if(!s.matches("[data-drag]"))return;let e={ArrowLeft:[-16,0],ArrowRight:[16,0],ArrowUp:[0,-16],ArrowDown:[0,16]};e[r.key]&&(r.preventDefault(),b=(b??0)+e[r.key][0],g=(g??0)+e[r.key][1],m())}),t.addEventListener("click",r=>{let s=r.target.closest("button");if(!(!s||s.disabled))if(s.dataset.phase)o.phase(s.dataset.phase);else{let e=s.dataset.action;o[e]?.()}}),t.addEventListener("change",r=>{let s=r.target;if(!s.dataset.duration||!T)return;let e=s.valueAsNumber,w=s.dataset.duration;Number.isInteger(e)&&e>=1&&e<=120?o.duration(w,e):s.value=String(T.state.durations[w])});let B=new ResizeObserver(m);B.observe(t),window.addEventListener("resize",m),window.visualViewport?.addEventListener("resize",m),window.visualViewport?.addEventListener("scroll",m);function x(r,s){t.querySelectorAll(`[data-text="${r}"]`).forEach(e=>{e.textContent!==s&&(e.textContent=s)})}return{update(r){let s=T;T=r;let{state:e,locale:w,display:$,notice:f,ready:M}=r,d=(i,A)=>q(w,i,A),P={focus:d("\u4E13\u6CE8","Focus"),short:d("\u77ED\u4F11\u606F","Short break"),long:d("\u957F\u4F11\u606F","Long break")},v=e.status==="paused"?d("\u5DF2\u6682\u505C","Paused"):e.status==="running"?e.phase==="focus"?d("\u4E13\u6CE8\u4E2D","Focusing"):d("\u4F11\u606F\u4E2D","On a break"):d("\u51C6\u5907\u5F00\u59CB","Ready to start"),k=f?"complete":e.status==="paused"?"paused":e.phase==="focus"?"focus":"break";t.dataset.tone=k,t.dataset.status=e.status,t.dataset.phase=e.phase,[h,y].forEach(i=>{i.dataset.theme=r.theme,i.lang=w==="zh"?"zh-CN":"en"}),h.hidden=$!=="full",y.hidden=$!=="mini",x("title",d("\u756A\u8304\u949F","Pomodoro")),x("status",`${P[e.phase]} \xB7 ${v}`),x("hint",e.status==="running"?e.phase==="focus"?d("\u53EA\u505A\u4E00\u4EF6\u4E8B\uFF0C\u6162\u6162\u6765","One thing at a time."):d("\u653E\u677E\u4E00\u4E0B\uFF0C\u6062\u590D\u7CBE\u529B","Take a breath. Recharge."):e.status==="paused"?d("\u51C6\u5907\u597D\u540E\u7EE7\u7EED","Resume when you\u2019re ready."):d("\u51C6\u5907\u597D\u4E86\u5C31\u5F00\u59CB","Start when you\u2019re ready.")),x("reset",d("\u91CD\u7F6E","Reset")),x("today",d("\u4ECA\u65E5\u5B8C\u6210","Completed today")),x("sessions",d("\u6B21\u4E13\u6CE8","sessions")),x("cycle",d("\u6BCF 4 \u6B21\u540E\u957F\u4F11\u606F","Long break every 4 sessions")),x("tip",d("\u62D6\u52A8\u9876\u90E8\u79FB\u52A8\u5361\u7247 \xB7 \u5173\u95ED\u540E\u8BA1\u65F6\u7EE7\u7EED","Drag the header to move \xB7 Closing keeps the timer running"));let C=F(e,r.now),H=J(C);t.querySelectorAll("[data-countdown]").forEach(i=>{i.textContent!==H&&(i.textContent=H),i.setAttribute("aria-label",d("\u5269\u4F59\u65F6\u95F4","Time remaining"))}),h.querySelector(".pomodoro-ring").style.setProperty("--progress",`${Math.max(0,Math.min(1,1-C/(e.durations[e.phase]*6e4)))*360}deg`);let N=U(k==="break"?"break":k==="paused"?"paused":k==="complete"?"complete":e.status==="idle"?"clock":"focus");(!s||s.state.status!==e.status||s.state.phase!==e.phase||s.notice!==f)&&t.querySelectorAll("[data-icon]").forEach(i=>{i.innerHTML=N}),t.querySelector("[data-completed]").textContent=String(e.day===D(r.now)?e.completed:0);let a=h.querySelector(".pomodoro-notice");a.hidden=!f,x("noticeTitle",f==="focus"?d("\u4E13\u6CE8\u5B8C\u6210\uFF01","Focus complete!"):d("\u4F11\u606F\u7ED3\u675F\uFF01","Break complete!")),x("noticeBody",f==="focus"?d(`\u505A\u5F97\u4E0D\u9519\uFF0C\u63A5\u4E0B\u6765${P[e.phase]} ${e.durations[e.phase]} \u5206\u949F\u3002`,`Nice work. Take a ${e.durations[e.phase]} minute ${P[e.phase].toLowerCase()}.`):d("\u6062\u590D\u597D\u7CBE\u529B\uFF0C\u51C6\u5907\u5F00\u59CB\u4E0B\u4E00\u6B21\u4E13\u6CE8\u3002","Feeling refreshed? Start your next focus session."));let p=h.querySelector("[data-action=toggle]");p.disabled=!M,p.textContent=e.status==="running"?d("\u6682\u505C","Pause"):e.status==="paused"?d("\u7EE7\u7EED","Resume"):f?d(`\u5F00\u59CB${P[e.phase]}`,`Start ${P[e.phase].toLowerCase()}`):d("\u5F00\u59CB","Start"),h.querySelector("[data-action=reset]").disabled=!M,h.querySelectorAll("[data-phase]").forEach(i=>{let A=i.dataset.phase;i.textContent=P[A],i.classList.toggle("active",e.phase===A),i.disabled=!M||e.status==="running",i.setAttribute("aria-pressed",String(e.phase===A))});let c=h.querySelector(".pomodoro-settings-panel");c.hidden=!r.settings,h.querySelectorAll("[data-duration]").forEach(i=>{let A=i.dataset.duration;i.disabled=!M||e.status!=="idle",i.setAttribute("aria-label",`${P[A]}${d("\u5206\u949F"," minutes")}`),document.activeElement!==i&&(i.value=String(e.durations[A]))}),h.querySelectorAll("[data-duration-label]").forEach(i=>{i.textContent=`${P[i.dataset.durationLabel]} (${d("\u5206\u949F","min")})`});let S=h.querySelector(".ep-error");S.hidden=!r.error,S.textContent=r.error;let I={minimize:d("\u7F29\u5C0F\u756A\u8304\u949F","Minimize Pomodoro"),close:d("\u5173\u95ED\u756A\u8304\u949F","Close Pomodoro"),expand:d(`\u5C55\u5F00\u756A\u8304\u949F\uFF0C${P[e.phase]}\uFF0C${v}\uFF0C${H}`,`Expand Pomodoro, ${P[e.phase]}, ${v}, ${H}`),settings:d("\u8BBE\u7F6E","Settings")};if(t.querySelectorAll("[data-action]").forEach(i=>{let A=I[i.dataset.action];A&&(i.setAttribute("aria-label",A),i.title=A)}),h.querySelector("[data-action=settings]").setAttribute("aria-expanded",String(r.settings)),t.querySelectorAll("[data-drag]").forEach(i=>{i.setAttribute("aria-label",d("\u62D6\u52A8\u756A\u8304\u949F\uFF0C\u6216\u7528\u65B9\u5411\u952E\u79FB\u52A8","Drag Pomodoro, or move with arrow keys")),i.title=d("\u62D6\u52A8\u79FB\u52A8","Drag to move")}),$==="closed"){let i=t.contains(document.activeElement);t.matches(":popover-open")&&t.hidePopover(),i&&l?.isConnected&&l.focus()}else s?.display==="closed"&&!f&&document.activeElement instanceof HTMLElement&&!t.contains(document.activeElement)&&(l=document.activeElement),t.matches(":popover-open")||t.showPopover(),m(),s?.display!==$&&!f&&t.querySelector($==="full"?"[data-drag]":"[data-action=expand]")?.focus({preventScroll:!0})},destroy(){z(),B.disconnect(),window.removeEventListener("resize",m),window.visualViewport?.removeEventListener("resize",m),window.visualViewport?.removeEventListener("scroll",m),t.remove()}}}function Q(n,o,l){let t={state:j(),now:Date.now(),ready:!1,error:"",display:"closed",notice:null,settings:!1,locale:"zh",theme:"light"},h=0,y="",T=!1,b=!1,g=!1,u=null,E=null,m=null,z,B,x=null,r=new Set,s=(a,p)=>q(t.locale,a,p);function e(a={}){t={...t,...a},m?.update(t),r.forEach(p=>p())}function w(a){g||(!m&&a!=="closed"&&(m=K(l,{expand:()=>w("full"),minimize:()=>{e({settings:!1}),w("mini")},close:()=>w("closed"),settings:()=>e({settings:!t.settings}),toggle:()=>{d()},reset:()=>{M(p=>R(p),{notice:null})},phase:p=>{M(c=>R(c,p),{notice:null})},duration:(p,c)=>{M(S=>R({...S,durations:{...S.durations,[p]:c}}))}})),e({display:a}))}function $(a){if(!Number.isInteger(a.revision)||a.revision<0||(a.revision===0?a.value!==null:!W(a.value)))throw Error(s("\u4FDD\u5B58\u7684\u8BBE\u7F6E\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002","Saved settings could not be read. Contact your administrator."));let p=a.revision===0?j():a.value,c=t.state,S=Date.now(),I=V(c,Math.max(S,c.deadline??S)),i=I!==c&&JSON.stringify(p)===JSON.stringify(I);h=a.revision,e({state:p,now:S}),i&&(e({notice:c.phase,settings:!1}),t.display!=="full"&&w("full"),n.toast(c.phase==="focus"?s("\u4E13\u6CE8\u5B8C\u6210\uFF0C\u4F11\u606F\u4E00\u4E0B\u5427\u3002","Focus complete. Time for a break."):s("\u4F11\u606F\u7ED3\u675F\uFF0C\u51C6\u5907\u5F00\u59CB\u4E0B\u4E00\u6B21\u4E13\u6CE8\u3002","Break complete. Ready to focus?"),"success"))}async function f(a=!1){if(T||g)return;u?.abort();let p=new AbortController;u=p;try{let c=await n.platform.users.me();if(p.signal.aborted||g)return;let S=`${o}:${c.id}:timer`;y!==S&&(E?.close(),y=S,h=0,m?.destroy(),m=null,e({state:j(),notice:null,ready:!1,display:"closed",settings:!1}),E=new BroadcastChannel(`doca.everyday.${y}`),E.onmessage=()=>{f()});let I=await n.request("/preferences?name=timer",{signal:p.signal});!p.signal.aborted&&!g&&($(I),e({ready:!0,...!a&&{error:""}}))}catch(c){!p.signal.aborted&&!g&&(e({ready:!1,error:c.message}),[401,403,404].includes(c.status??0)&&H())}}async function M(a,p={}){if(!t.ready||T||g)return!1;let c=a(t.state);if(!W(c))throw Error("Invalid timer preference");if(JSON.stringify(c)===JSON.stringify(t.state))return e(p),!0;T=!0,e({ready:!1}),u?.abort();let S=new AbortController;u=S;let I=!1;try{let i=await n.request("/preferences",{method:"POST",signal:S.signal,headers:{"Content-Type":"application/json"},body:JSON.stringify({name:"timer",value:c,revision:h})});!S.signal.aborted&&!g&&($(i),e({error:"",...p}),I=!0)}catch(i){!S.signal.aborted&&!g&&e({error:i.message})}finally{T=!1,g||(I?(e({ready:!0}),window.dispatchEvent(new CustomEvent("doca.everyday.preference",{detail:y})),E?.postMessage("reload")):await f(!0))}return I}async function d(){let a=t.state.status==="running",p=Date.now();await M(c=>a?Z(c,p):X(c,p),{notice:null,settings:!1})}function P(){let a=Date.now();e({now:a}),t.ready&&!t.error&&t.state.status==="running"&&t.state.deadline<=a&&M(p=>V(p,a))}function v(){document.visibilityState==="visible"&&(P(),f())}function k(a){a.detail===y&&f()}function C(a){a.persisted||H()}function H(){g=!0,u?.abort(),E?.close(),m?.destroy(),m=null,clearInterval(z),clearInterval(B),window.removeEventListener("doca.everyday.preference",k),window.removeEventListener("pagehide",C),document.removeEventListener("visibilitychange",v)}async function N(){if(b)return x;b=!0,window.addEventListener("doca.everyday.preference",k),window.addEventListener("pagehide",C),document.addEventListener("visibilitychange",v),z=window.setInterval(P,250),B=window.setInterval(()=>{document.visibilityState==="visible"&&f()},15e3),x=f(),await x,P()}return{snapshot:()=>t,subscribe(a){return r.add(a),()=>{r.delete(a)}},ensure:N,environment(a,p){(t.locale!==a||t.theme!==p)&&e({locale:a,theme:p})},async open(a=t.locale){e({locale:a}),await N(),w("full")}}}var Y=`.pomodoro-entry-symbol{width:54px;height:54px;flex-shrink:0;display:grid;place-items:center;border-radius:17px;background:#fff0eb;color:#cf5038}.pomodoro-entry-symbol svg{display:block;width:30px;height:30px}.pomodoro-entry .ep-head{margin-bottom:20px}
|
|
29
|
+
.pomodoro-float{position:fixed;inset:auto;margin:0;padding:0;border:0;background:transparent;overflow:visible;width:max-content;max-width:calc(100vw - 24px);z-index:1000;--pomodoro-color:#cf5038;--pomodoro-tint:#fff0eb;--pomodoro-border:#f2cabb;color:var(--pomodoro-color)}
|
|
30
|
+
.pomodoro-float[data-tone=break]{--pomodoro-color:#157a70;--pomodoro-tint:#e7f4ef;--pomodoro-border:#bfe1d6}.pomodoro-float[data-tone=paused]{--pomodoro-color:#77818a;--pomodoro-tint:#eef1f3;--pomodoro-border:#d7dfe4}.pomodoro-float[data-tone=complete]{--pomodoro-color:#36856a;--pomodoro-tint:#e7f5ed;--pomodoro-border:#bfdfcb}
|
|
31
|
+
.pomodoro-float [hidden]{display:none!important}.pomodoro-float .ep-root{--ep-accent:var(--pomodoro-color);--ep-tint:var(--pomodoro-tint);border-color:var(--pomodoro-border);box-shadow:0 20px 64px #172c3926,0 3px 12px #172c3910}
|
|
32
|
+
.pomodoro-card{width:min(360px,calc(100vw - 24px));padding:20px;max-height:var(--pomodoro-max-height,calc(100dvh - 24px));overflow:auto;animation:pomodoro-appear .24s ease-out}.pomodoro-drag{display:flex;gap:12px;align-items:center;cursor:grab;touch-action:none;user-select:none;outline-offset:5px}.pomodoro-drag:focus-visible,.pomodoro-mini-grip:focus-visible{outline:3px solid var(--pomodoro-color);border-radius:8px}.is-dragging [data-drag]{cursor:grabbing}.pomodoro-symbol{color:var(--pomodoro-color);background:var(--pomodoro-tint);display:grid;place-items:center;border-radius:12px;flex-shrink:0;width:40px;height:40px}.pomodoro-symbol svg{width:23px;height:23px}.pomodoro-heading{flex:1}.pomodoro-heading .ep-kicker{font-size:8px;letter-spacing:1.3px;margin:0}.pomodoro-heading h2{font-size:18px}.pomodoro-window-actions{display:flex;gap:4px}.pomodoro-window-actions button,.pomodoro-mini-close,.pomodoro-settings{border:0;background:transparent;color:var(--ep-muted);display:grid;place-items:center;border-radius:8px;width:28px;height:28px;padding:0;font-size:20px;line-height:1}.pomodoro-window-actions button:hover,.pomodoro-mini-close:hover,.pomodoro-settings:hover{background:var(--ep-soft);color:var(--ep-ink)}
|
|
33
|
+
.pomodoro-phase-row{display:flex;gap:8px;align-items:center;justify-content:space-between;margin-top:16px}.pomodoro-card .ep-tabs{gap:4px;margin:0}.pomodoro-card .ep-tabs button{padding:7px 12px;font-size:12px;font-weight:500;color:#445968;background:var(--ep-soft)}.pomodoro-card .ep-tabs button.active{background:var(--ep-ink);color:#fff;font-weight:600}.pomodoro-card .ep-tabs button:disabled{opacity:1}.pomodoro-card .pomodoro-settings{display:flex;align-items:center;justify-content:center;width:36px;height:36px;flex:0 0 36px;padding:0;color:var(--ep-ink);background:var(--ep-soft);border:1px solid var(--ep-line);border-radius:10px}.pomodoro-settings svg{display:block;width:22px;height:22px;flex:0 0 22px}.pomodoro-settings-panel{margin-top:14px;padding:12px;gap:8px}.pomodoro-card .ep-field input{width:100%;padding:6px 8px}
|
|
34
|
+
.pomodoro-ring{--progress:0deg;width:210px;height:210px;margin:22px auto;border-radius:50%;background:conic-gradient(var(--pomodoro-color) var(--progress),var(--pomodoro-tint) 0);padding:5px}.pomodoro-ring>div{border-radius:50%;height:100%;background:var(--ep-bg);display:flex;flex-direction:column;justify-content:center;align-items:center;gap:8px}.pomodoro-status{color:var(--pomodoro-color);font-size:11px}.pomodoro-ring strong{font-size:48px;line-height:1.1;letter-spacing:-2px;font-variant-numeric:tabular-nums;font-weight:400}.pomodoro-ring .ep-muted{font-size:11px}.pomodoro-controls{justify-content:center}.pomodoro-controls .ep-button:first-child{min-width:140px}.pomodoro-card .ep-foot{margin-top:20px;padding-top:12px;gap:4px;font-size:10px}.pomodoro-tip{color:var(--ep-muted);text-align:center;font-size:10px;margin-top:9px!important}
|
|
35
|
+
.pomodoro-notice{display:flex;align-items:flex-start;gap:10px;border:1px solid var(--pomodoro-border);background:var(--pomodoro-tint);border-radius:12px;padding:12px;margin-top:18px;animation:pomodoro-appear .3s ease-out}.pomodoro-notice svg{width:22px;height:22px;color:var(--pomodoro-color)}.pomodoro-notice strong{color:var(--pomodoro-color);font-size:14px}.pomodoro-notice p{color:var(--ep-muted);font-size:11px;margin-top:3px}.pomodoro-card:has(.pomodoro-notice:not([hidden])) .pomodoro-ring{width:180px;height:180px;margin:18px auto}.pomodoro-card:has(.pomodoro-notice:not([hidden])) .pomodoro-ring strong{font-size:42px}
|
|
36
|
+
.pomodoro-float .pomodoro-mini{width:190px;padding:9px 8px;display:flex;align-items:center;gap:4px;border-radius:17px;animation:pomodoro-appear .18s ease-out}.pomodoro-mini-grip{color:var(--ep-muted);font-size:18px;cursor:grab;touch-action:none;user-select:none;width:16px;text-align:center}.pomodoro-mini-expand{display:flex;align-items:center;gap:9px;border:0;padding:0;background:transparent;color:var(--ep-ink);flex:1}.pomodoro-mini-expand .pomodoro-symbol{width:30px;height:30px;border-radius:9px}.pomodoro-mini-expand .pomodoro-symbol svg{width:19px;height:19px}.pomodoro-mini-expand strong{font-size:23px;line-height:1.2;font-weight:500;font-variant-numeric:tabular-nums;letter-spacing:-.6px}.pomodoro-mini-close{width:24px;height:28px;font-size:18px}
|
|
37
|
+
@keyframes pomodoro-appear{from{opacity:0;transform:translateY(6px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}
|
|
38
|
+
@media(max-width:600px){.pomodoro-float .pomodoro-card{padding:18px;border-radius:18px}.pomodoro-heading .ep-kicker{font-size:7px}.pomodoro-card .ep-grid{grid-template-columns:repeat(3,minmax(0,1fr))}.pomodoro-ring{width:190px;height:190px;margin:18px auto}}
|
|
39
|
+
@media(prefers-reduced-motion:reduce){.pomodoro-float,.pomodoro-float *{animation:none!important;transition:none!important}}
|
|
40
|
+
`;var Te=async n=>{let o=n.React,{Frame:l}=G(n,L.id),t=Q(n,L.id,`${O}
|
|
41
|
+
${Y}`);function h(){let b=n.useEnvironment(),g=b.locale,u=(x,r)=>q(g,x,r),E=o.useSyncExternalStore(t.subscribe,t.snapshot),{state:m}=E;o.useEffect(()=>{t.environment(g,b.theme),t.ensure()},[g,b.theme]);let z=m.status!=="idle",B={focus:u("\u4E13\u6CE8","Focus"),short:u("\u77ED\u4F11\u606F","Short break"),long:u("\u957F\u4F11\u606F","Long break")};return o.createElement(l,{className:"pomodoro-entry"},o.createElement("style",null,Y),o.createElement("header",{className:"ep-head"},o.createElement("div",null,o.createElement("div",{className:"ep-kicker"},"FOCUS / ONE THING AT A TIME"),o.createElement("h2",null,u("\u756A\u8304\u949F","Pomodoro")),o.createElement("p",{className:"ep-muted"},u("\u7ED9\u4E00\u4EF6\u4E8B\uFF0C\u4E00\u6BB5\u4E13\u6CE8\u7684\u65F6\u95F4","Make a little room for one thing."))),o.createElement("span",{className:"pomodoro-entry-symbol","aria-hidden":"true"},o.createElement("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round"},o.createElement("circle",{cx:"12",cy:"12",r:"9"}),o.createElement("path",{d:"M12 7v5l3 2"})))),o.createElement("div",{className:"ep-row ep-between"},o.createElement("button",{className:"ep-button",onClick:()=>{t.open(g)}},u("\u6253\u5F00\u756A\u8304\u949F","Open Pomodoro")),o.createElement("span",{className:"ep-muted"},z?`${B[m.phase]} \xB7 ${J(F(m,E.now))}${m.status==="paused"?u(" \xB7 \u5DF2\u6682\u505C"," \xB7 Paused"):""}`:u("\u60AC\u6D6E\u5361\u7247 \xB7 \u53EF\u62D6\u62FD","Floating card \xB7 Draggable"))),E.error&&o.createElement("p",{className:"ep-error",role:"alert"},E.error))}let y=`${L.id}.entry`,T=`${L.id}.open`;return{manifest:{pluginId:L.id,version:L.version,targets:["web"]},views:[{id:y,pluginId:L.id,title:{zh:"\u756A\u8304\u949F",en:"Pomodoro"},supportedContexts:["home"],render:()=>o.createElement(h,null)}],commands:[{id:T,pluginId:L.id,title:{zh:"\u756A\u8304\u949F",en:"Pomodoro"},supportedContexts:["home","global","document"],execute:b=>(b.signal.throwIfAborted(),t.open(b.locale))}],placements:[{id:`${L.id}.home-entry`,pluginId:L.id,slot:"home.cards",viewId:y},{id:`${L.id}.global-tool`,pluginId:L.id,slot:"global.more",commandId:T},{id:`${L.id}.document-tool`,pluginId:L.id,slot:"document.toolbar",commandId:T}]}};export{Te as default};
|