@rinova/playground 0.1.5 → 0.7.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/README.md +6 -7
- package/dist/PlaygroundRoot-Bcq7hG6m.js +4114 -0
- package/dist/bash-DXbFn1Mw.js +2 -0
- package/dist/diff-BbCHXb9k.js +2 -0
- package/dist/html-BheHDAwJ.js +10 -0
- package/dist/index.js +646 -5
- package/dist/less-DcFvX2iI.js +2 -0
- package/dist/light-plus-DuoQWI9Q.js +3 -0
- package/dist/markdown-qYHfSPvL.js +2 -0
- package/dist/marked.esm-CNYX-ybC.js +1796 -0
- package/dist/{App-DUVLlDOI.js → plugin-S72dg7Tb.js} +556 -1700
- package/dist/style.css +2857 -554
- package/dist/tokyo-night-A7e3DMsQ.js +3 -0
- package/dist/types/app/App.vue.d.ts +1 -0
- package/dist/types/app/PlaygroundApiTable.vue.d.ts +16 -0
- package/dist/types/app/PlaygroundEditorPicker.vue.d.ts +3 -0
- package/dist/types/app/PlaygroundModuleApi.vue.d.ts +17 -0
- package/dist/types/app/PlaygroundPage.vue.d.ts +30 -0
- package/dist/types/app/{PlaygroundSourcePanel.vue.d.ts → PlaygroundPageIndex.vue.d.ts} +4 -4
- package/dist/types/app/PlaygroundPluginPanel.vue.d.ts +3 -0
- package/dist/types/app/PlaygroundPluginSlot.vue.d.ts +12 -0
- package/dist/types/app/PlaygroundPluginWrappers.vue.d.ts +25 -0
- package/dist/types/app/PlaygroundRoot.vue.d.ts +4 -0
- package/dist/types/app/{PlaygroundParamsPanel.vue.d.ts → PlaygroundSceneArgs.vue.d.ts} +1 -1
- package/dist/types/app/PlaygroundSceneOutput.vue.d.ts +6 -0
- package/dist/types/app/PlaygroundSceneSection.vue.d.ts +16 -0
- package/dist/types/app/{PlaygroundStage.vue.d.ts → PlaygroundSceneSource.vue.d.ts} +0 -1
- package/dist/types/app/PlaygroundSidebar.vue.d.ts +19 -8
- package/dist/types/app/viewport.d.ts +39 -0
- package/dist/types/code/CodeBlock.vue.d.ts +19 -1
- package/dist/types/code/clipboard.d.ts +2 -0
- package/dist/types/code/find.d.ts +42 -0
- package/dist/types/code/highlighter.d.ts +9 -2
- package/dist/types/code/openInEditor.d.ts +35 -0
- package/dist/types/{app/PlaygroundGroupIndex.vue.d.ts → components/PwApiBrief.vue.d.ts} +9 -9
- package/dist/types/components/PwButton.vue.d.ts +2 -2
- package/dist/types/components/PwDemo.vue.d.ts +39 -0
- package/dist/types/components/PwDemoGrid.vue.d.ts +32 -0
- package/dist/types/components/PwInput.vue.d.ts +8 -8
- package/dist/types/components/PwSceneBoundary.vue.d.ts +20 -0
- package/dist/types/components/PwSceneNote.vue.d.ts +19 -0
- package/dist/types/components/PwTooltip.vue.d.ts +3 -0
- package/dist/types/components/prose/PwCallout.vue.d.ts +24 -0
- package/dist/types/components/prose/PwMarkdown.vue.d.ts +11 -0
- package/dist/types/components/prose/PwProse.vue.d.ts +27 -0
- package/dist/types/components/prose/PwStep.vue.d.ts +27 -0
- package/dist/types/components/prose/PwSteps.vue.d.ts +26 -0
- package/dist/types/components/prose/enhance.d.ts +4 -0
- package/dist/types/components/prose/index.d.ts +7 -0
- package/dist/types/components/prose/markdown-scene.d.ts +10 -0
- package/dist/types/components/prose/parse.d.ts +20 -0
- package/dist/types/components/prose/render.d.ts +22 -0
- package/dist/types/components/scene.d.ts +5 -0
- package/dist/types/config.d.ts +38 -10
- package/dist/types/index.d.ts +11 -4
- package/dist/types/module-api.d.ts +55 -0
- package/dist/types/payload/args.d.ts +5 -3
- package/dist/types/payload/context.d.ts +3 -0
- package/dist/types/payload/output.d.ts +4 -2
- package/dist/types/plugin.d.ts +119 -0
- package/dist/types/scene-meta.d.ts +38 -3
- package/dist/types/shims.d.ts +26 -8
- package/dist/types/types.d.ts +107 -20
- package/dist/vue-B_A2yz16.js +30 -0
- package/dist/yaml-CoEYOy4X.js +2 -0
- package/package.json +3 -5
- package/dist/assets/editor.worker-G4Kxf7Y6.js +0 -15827
- package/dist/assets/ts.worker-Dee0cZZb.js +0 -164076
- package/dist/dist-B0hittbo.js +0 -151
- package/dist/editor.api-YVDhnHI-.js +0 -126730
- package/dist/editor.css +0 -1
- package/dist/editor.worker-VDFPHiZj.js +0 -5
- package/dist/monaco.contribution-CaO2YABh.js +0 -220
- package/dist/output-CGawAeuu.js +0 -80
- package/dist/ts.worker-CpQm_d0w.js +0 -5
- package/dist/tsMode-BOlRJVVo.js +0 -52173
- package/dist/types/app/PlaygroundToolbar.vue.d.ts +0 -22
- package/dist/types/code/CodeEditor.vue.d.ts +0 -18
- package/dist/types/code/monaco.d.ts +0 -6
- package/dist/vitesse-dark-B9O9KI0c.js +0 -3
- package/dist/vitesse-light-Cs2DYTIe.js +0 -3
- package/dist/vue-tjAT-6a9.js +0 -36
|
@@ -0,0 +1,4114 @@
|
|
|
1
|
+
import { A as _plugin_vue_export_helper_default, C as toast, D as PW_THEME_LIGHT, E as PW_THEME_DARK, O as getHighlighter, S as provideSceneContext, T as createLucideIcon, _ as currentOutputSlots, a as pluginHeaderSlots, b as currentArgsSlots, c as pluginWrapperEntries, i as pluginFooterSlots, k as PwApiBrief_default, l as providePlaygroundTheme, o as pluginOpenPanelId, s as pluginPanelEntries, t as closePlaygroundPanel, u as runPlugins, w as toastState } from "./plugin-S72dg7Tb.js";
|
|
2
|
+
import { Fragment, Teleport, TransitionGroup, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, getCurrentInstance, guardReactiveProps, mergeProps, nextTick, normalizeClass, normalizeProps, normalizeStyle, onBeforeMount, onBeforeUnmount, onErrorCaptured, onMounted, openBlock, ref, renderList, renderSlot, resolveComponent, resolveDynamicComponent, shallowRef, toDisplayString, unref, vModelText, vShow, watch, watchEffect, withCtx, withDirectives, withKeys, withModifiers } from "vue";
|
|
3
|
+
import config from "virtual:playground/config";
|
|
4
|
+
import { pluginModules } from "virtual:playground/plugins";
|
|
5
|
+
import { pages, scenes } from "virtual:playground/scenes";
|
|
6
|
+
import { api, modules, pageComponents, pageModules } from "virtual:playground/api";
|
|
7
|
+
/**
|
|
8
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
9
|
+
*
|
|
10
|
+
* This source code is licensed under the ISC license.
|
|
11
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
12
|
+
*/
|
|
13
|
+
var CaseSensitive = createLucideIcon({
|
|
14
|
+
name: "case-sensitive",
|
|
15
|
+
size: 24,
|
|
16
|
+
node: [
|
|
17
|
+
["path", {
|
|
18
|
+
d: "m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16",
|
|
19
|
+
key: "d5nyq2"
|
|
20
|
+
}],
|
|
21
|
+
["path", {
|
|
22
|
+
d: "M22 9v7",
|
|
23
|
+
key: "pvm9v3"
|
|
24
|
+
}],
|
|
25
|
+
["path", {
|
|
26
|
+
d: "M3.304 13h6.392",
|
|
27
|
+
key: "1q3zxz"
|
|
28
|
+
}],
|
|
29
|
+
["circle", {
|
|
30
|
+
cx: "18.5",
|
|
31
|
+
cy: "12.5",
|
|
32
|
+
r: "3.5",
|
|
33
|
+
key: "z97x68"
|
|
34
|
+
}]
|
|
35
|
+
]
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
39
|
+
*
|
|
40
|
+
* This source code is licensed under the ISC license.
|
|
41
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
42
|
+
*/
|
|
43
|
+
var Check = createLucideIcon({
|
|
44
|
+
name: "check",
|
|
45
|
+
size: 24,
|
|
46
|
+
node: [["path", {
|
|
47
|
+
d: "M20 6 9 17l-5-5",
|
|
48
|
+
key: "1gmf2c"
|
|
49
|
+
}]]
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
53
|
+
*
|
|
54
|
+
* This source code is licensed under the ISC license.
|
|
55
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
56
|
+
*/
|
|
57
|
+
var ChevronDown = createLucideIcon({
|
|
58
|
+
name: "chevron-down",
|
|
59
|
+
size: 24,
|
|
60
|
+
node: [["path", {
|
|
61
|
+
d: "m6 9 6 6 6-6",
|
|
62
|
+
key: "qrunsl"
|
|
63
|
+
}]]
|
|
64
|
+
});
|
|
65
|
+
/**
|
|
66
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
67
|
+
*
|
|
68
|
+
* This source code is licensed under the ISC license.
|
|
69
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
70
|
+
*/
|
|
71
|
+
var ChevronRight = createLucideIcon({
|
|
72
|
+
name: "chevron-right",
|
|
73
|
+
size: 24,
|
|
74
|
+
node: [["path", {
|
|
75
|
+
d: "m9 18 6-6-6-6",
|
|
76
|
+
key: "mthhwq"
|
|
77
|
+
}]]
|
|
78
|
+
});
|
|
79
|
+
/**
|
|
80
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
81
|
+
*
|
|
82
|
+
* This source code is licensed under the ISC license.
|
|
83
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
84
|
+
*/
|
|
85
|
+
var ChevronUp = createLucideIcon({
|
|
86
|
+
name: "chevron-up",
|
|
87
|
+
size: 24,
|
|
88
|
+
node: [["path", {
|
|
89
|
+
d: "m18 15-6-6-6 6",
|
|
90
|
+
key: "153udz"
|
|
91
|
+
}]]
|
|
92
|
+
});
|
|
93
|
+
/**
|
|
94
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
95
|
+
*
|
|
96
|
+
* This source code is licensed under the ISC license.
|
|
97
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
98
|
+
*/
|
|
99
|
+
var Copy = createLucideIcon({
|
|
100
|
+
name: "copy",
|
|
101
|
+
size: 24,
|
|
102
|
+
node: [["rect", {
|
|
103
|
+
width: "14",
|
|
104
|
+
height: "14",
|
|
105
|
+
x: "8",
|
|
106
|
+
y: "8",
|
|
107
|
+
rx: "2",
|
|
108
|
+
ry: "2",
|
|
109
|
+
key: "17jyea"
|
|
110
|
+
}], ["path", {
|
|
111
|
+
d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",
|
|
112
|
+
key: "zix9uf"
|
|
113
|
+
}]]
|
|
114
|
+
});
|
|
115
|
+
/**
|
|
116
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
117
|
+
*
|
|
118
|
+
* This source code is licensed under the ISC license.
|
|
119
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
120
|
+
*/
|
|
121
|
+
var FileCodeCorner = createLucideIcon({
|
|
122
|
+
name: "file-code-corner",
|
|
123
|
+
size: 24,
|
|
124
|
+
node: [
|
|
125
|
+
["path", {
|
|
126
|
+
d: "M4 12.15V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-3.35",
|
|
127
|
+
key: "1wthlu"
|
|
128
|
+
}],
|
|
129
|
+
["path", {
|
|
130
|
+
d: "M14 2v5a1 1 0 0 0 1 1h5",
|
|
131
|
+
key: "wfsgrz"
|
|
132
|
+
}],
|
|
133
|
+
["path", {
|
|
134
|
+
d: "m5 16-3 3 3 3",
|
|
135
|
+
key: "331omg"
|
|
136
|
+
}],
|
|
137
|
+
["path", {
|
|
138
|
+
d: "m9 22 3-3-3-3",
|
|
139
|
+
key: "lsp7cz"
|
|
140
|
+
}]
|
|
141
|
+
],
|
|
142
|
+
aliases: ["file-code-2"]
|
|
143
|
+
});
|
|
144
|
+
/**
|
|
145
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
146
|
+
*
|
|
147
|
+
* This source code is licensed under the ISC license.
|
|
148
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
149
|
+
*/
|
|
150
|
+
var FileSymlink = createLucideIcon({
|
|
151
|
+
name: "file-symlink",
|
|
152
|
+
size: 24,
|
|
153
|
+
node: [
|
|
154
|
+
["path", {
|
|
155
|
+
d: "M4 11V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h7",
|
|
156
|
+
key: "huwfnr"
|
|
157
|
+
}],
|
|
158
|
+
["path", {
|
|
159
|
+
d: "M14 2v5a1 1 0 0 0 1 1h5",
|
|
160
|
+
key: "wfsgrz"
|
|
161
|
+
}],
|
|
162
|
+
["path", {
|
|
163
|
+
d: "m10 18 3-3-3-3",
|
|
164
|
+
key: "18f6ys"
|
|
165
|
+
}]
|
|
166
|
+
]
|
|
167
|
+
});
|
|
168
|
+
/**
|
|
169
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
170
|
+
*
|
|
171
|
+
* This source code is licensed under the ISC license.
|
|
172
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
173
|
+
*/
|
|
174
|
+
var Maximize = createLucideIcon({
|
|
175
|
+
name: "maximize",
|
|
176
|
+
size: 24,
|
|
177
|
+
node: [
|
|
178
|
+
["path", {
|
|
179
|
+
d: "M8 3H5a2 2 0 0 0-2 2v3",
|
|
180
|
+
key: "1dcmit"
|
|
181
|
+
}],
|
|
182
|
+
["path", {
|
|
183
|
+
d: "M21 8V5a2 2 0 0 0-2-2h-3",
|
|
184
|
+
key: "1e4gt3"
|
|
185
|
+
}],
|
|
186
|
+
["path", {
|
|
187
|
+
d: "M3 16v3a2 2 0 0 0 2 2h3",
|
|
188
|
+
key: "wsl5sc"
|
|
189
|
+
}],
|
|
190
|
+
["path", {
|
|
191
|
+
d: "M16 21h3a2 2 0 0 0 2-2v-3",
|
|
192
|
+
key: "18trek"
|
|
193
|
+
}]
|
|
194
|
+
]
|
|
195
|
+
});
|
|
196
|
+
/**
|
|
197
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
198
|
+
*
|
|
199
|
+
* This source code is licensed under the ISC license.
|
|
200
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
201
|
+
*/
|
|
202
|
+
var Monitor = createLucideIcon({
|
|
203
|
+
name: "monitor",
|
|
204
|
+
size: 24,
|
|
205
|
+
node: [
|
|
206
|
+
["rect", {
|
|
207
|
+
width: "20",
|
|
208
|
+
height: "14",
|
|
209
|
+
x: "2",
|
|
210
|
+
y: "3",
|
|
211
|
+
rx: "2",
|
|
212
|
+
key: "48i651"
|
|
213
|
+
}],
|
|
214
|
+
["line", {
|
|
215
|
+
x1: "8",
|
|
216
|
+
x2: "16",
|
|
217
|
+
y1: "21",
|
|
218
|
+
y2: "21",
|
|
219
|
+
key: "1svkeh"
|
|
220
|
+
}],
|
|
221
|
+
["line", {
|
|
222
|
+
x1: "12",
|
|
223
|
+
x2: "12",
|
|
224
|
+
y1: "17",
|
|
225
|
+
y2: "21",
|
|
226
|
+
key: "vw1qmm"
|
|
227
|
+
}]
|
|
228
|
+
]
|
|
229
|
+
});
|
|
230
|
+
/**
|
|
231
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
232
|
+
*
|
|
233
|
+
* This source code is licensed under the ISC license.
|
|
234
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
235
|
+
*/
|
|
236
|
+
var Moon = createLucideIcon({
|
|
237
|
+
name: "moon",
|
|
238
|
+
size: 24,
|
|
239
|
+
node: [["path", {
|
|
240
|
+
d: "M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401",
|
|
241
|
+
key: "kfwtm"
|
|
242
|
+
}]]
|
|
243
|
+
});
|
|
244
|
+
/**
|
|
245
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
246
|
+
*
|
|
247
|
+
* This source code is licensed under the ISC license.
|
|
248
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
249
|
+
*/
|
|
250
|
+
var PanelLeftClose = createLucideIcon({
|
|
251
|
+
name: "panel-left-close",
|
|
252
|
+
size: 24,
|
|
253
|
+
node: [
|
|
254
|
+
["rect", {
|
|
255
|
+
width: "18",
|
|
256
|
+
height: "18",
|
|
257
|
+
x: "3",
|
|
258
|
+
y: "3",
|
|
259
|
+
rx: "2",
|
|
260
|
+
key: "afitv7"
|
|
261
|
+
}],
|
|
262
|
+
["path", {
|
|
263
|
+
d: "M9 3v18",
|
|
264
|
+
key: "fh3hqa"
|
|
265
|
+
}],
|
|
266
|
+
["path", {
|
|
267
|
+
d: "m16 15-3-3 3-3",
|
|
268
|
+
key: "14y99z"
|
|
269
|
+
}]
|
|
270
|
+
],
|
|
271
|
+
aliases: ["sidebar-close"]
|
|
272
|
+
});
|
|
273
|
+
/**
|
|
274
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
275
|
+
*
|
|
276
|
+
* This source code is licensed under the ISC license.
|
|
277
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
278
|
+
*/
|
|
279
|
+
var PanelLeftOpen = createLucideIcon({
|
|
280
|
+
name: "panel-left-open",
|
|
281
|
+
size: 24,
|
|
282
|
+
node: [
|
|
283
|
+
["rect", {
|
|
284
|
+
width: "18",
|
|
285
|
+
height: "18",
|
|
286
|
+
x: "3",
|
|
287
|
+
y: "3",
|
|
288
|
+
rx: "2",
|
|
289
|
+
key: "afitv7"
|
|
290
|
+
}],
|
|
291
|
+
["path", {
|
|
292
|
+
d: "M9 3v18",
|
|
293
|
+
key: "fh3hqa"
|
|
294
|
+
}],
|
|
295
|
+
["path", {
|
|
296
|
+
d: "m14 9 3 3-3 3",
|
|
297
|
+
key: "8010ee"
|
|
298
|
+
}]
|
|
299
|
+
],
|
|
300
|
+
aliases: ["sidebar-open"]
|
|
301
|
+
});
|
|
302
|
+
/**
|
|
303
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
304
|
+
*
|
|
305
|
+
* This source code is licensed under the ISC license.
|
|
306
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
307
|
+
*/
|
|
308
|
+
var PanelRightClose = createLucideIcon({
|
|
309
|
+
name: "panel-right-close",
|
|
310
|
+
size: 24,
|
|
311
|
+
node: [
|
|
312
|
+
["rect", {
|
|
313
|
+
width: "18",
|
|
314
|
+
height: "18",
|
|
315
|
+
x: "3",
|
|
316
|
+
y: "3",
|
|
317
|
+
rx: "2",
|
|
318
|
+
key: "afitv7"
|
|
319
|
+
}],
|
|
320
|
+
["path", {
|
|
321
|
+
d: "M15 3v18",
|
|
322
|
+
key: "14nvp0"
|
|
323
|
+
}],
|
|
324
|
+
["path", {
|
|
325
|
+
d: "m8 9 3 3-3 3",
|
|
326
|
+
key: "12hl5m"
|
|
327
|
+
}]
|
|
328
|
+
]
|
|
329
|
+
});
|
|
330
|
+
/**
|
|
331
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
332
|
+
*
|
|
333
|
+
* This source code is licensed under the ISC license.
|
|
334
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
335
|
+
*/
|
|
336
|
+
var PanelRightOpen = createLucideIcon({
|
|
337
|
+
name: "panel-right-open",
|
|
338
|
+
size: 24,
|
|
339
|
+
node: [
|
|
340
|
+
["rect", {
|
|
341
|
+
width: "18",
|
|
342
|
+
height: "18",
|
|
343
|
+
x: "3",
|
|
344
|
+
y: "3",
|
|
345
|
+
rx: "2",
|
|
346
|
+
key: "afitv7"
|
|
347
|
+
}],
|
|
348
|
+
["path", {
|
|
349
|
+
d: "M15 3v18",
|
|
350
|
+
key: "14nvp0"
|
|
351
|
+
}],
|
|
352
|
+
["path", {
|
|
353
|
+
d: "m10 15-3-3 3-3",
|
|
354
|
+
key: "1pgupc"
|
|
355
|
+
}]
|
|
356
|
+
]
|
|
357
|
+
});
|
|
358
|
+
/**
|
|
359
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
360
|
+
*
|
|
361
|
+
* This source code is licensed under the ISC license.
|
|
362
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
363
|
+
*/
|
|
364
|
+
var PencilLine = createLucideIcon({
|
|
365
|
+
name: "pencil-line",
|
|
366
|
+
size: 24,
|
|
367
|
+
node: [
|
|
368
|
+
["path", {
|
|
369
|
+
d: "M13 21h8",
|
|
370
|
+
key: "1jsn5i"
|
|
371
|
+
}],
|
|
372
|
+
["path", {
|
|
373
|
+
d: "m15 5 4 4",
|
|
374
|
+
key: "1mk7zo"
|
|
375
|
+
}],
|
|
376
|
+
["path", {
|
|
377
|
+
d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
|
|
378
|
+
key: "1a8usu"
|
|
379
|
+
}]
|
|
380
|
+
]
|
|
381
|
+
});
|
|
382
|
+
/**
|
|
383
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
384
|
+
*
|
|
385
|
+
* This source code is licensed under the ISC license.
|
|
386
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
387
|
+
*/
|
|
388
|
+
var RotateCcw = createLucideIcon({
|
|
389
|
+
name: "rotate-ccw",
|
|
390
|
+
size: 24,
|
|
391
|
+
node: [["path", {
|
|
392
|
+
d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",
|
|
393
|
+
key: "1357e3"
|
|
394
|
+
}], ["path", {
|
|
395
|
+
d: "M3 3v5h5",
|
|
396
|
+
key: "1xhq8a"
|
|
397
|
+
}]]
|
|
398
|
+
});
|
|
399
|
+
/**
|
|
400
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
401
|
+
*
|
|
402
|
+
* This source code is licensed under the ISC license.
|
|
403
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
404
|
+
*/
|
|
405
|
+
var Search = createLucideIcon({
|
|
406
|
+
name: "search",
|
|
407
|
+
size: 24,
|
|
408
|
+
node: [["path", {
|
|
409
|
+
d: "m21 21-4.34-4.34",
|
|
410
|
+
key: "14j7rj"
|
|
411
|
+
}], ["circle", {
|
|
412
|
+
cx: "11",
|
|
413
|
+
cy: "11",
|
|
414
|
+
r: "8",
|
|
415
|
+
key: "4ej97u"
|
|
416
|
+
}]]
|
|
417
|
+
});
|
|
418
|
+
/**
|
|
419
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
420
|
+
*
|
|
421
|
+
* This source code is licensed under the ISC license.
|
|
422
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
423
|
+
*/
|
|
424
|
+
var Share2 = createLucideIcon({
|
|
425
|
+
name: "share-2",
|
|
426
|
+
size: 24,
|
|
427
|
+
node: [
|
|
428
|
+
["circle", {
|
|
429
|
+
cx: "18",
|
|
430
|
+
cy: "5",
|
|
431
|
+
r: "3",
|
|
432
|
+
key: "gq8acd"
|
|
433
|
+
}],
|
|
434
|
+
["circle", {
|
|
435
|
+
cx: "6",
|
|
436
|
+
cy: "12",
|
|
437
|
+
r: "3",
|
|
438
|
+
key: "w7nqdw"
|
|
439
|
+
}],
|
|
440
|
+
["circle", {
|
|
441
|
+
cx: "18",
|
|
442
|
+
cy: "19",
|
|
443
|
+
r: "3",
|
|
444
|
+
key: "1xt0gg"
|
|
445
|
+
}],
|
|
446
|
+
["line", {
|
|
447
|
+
x1: "8.59",
|
|
448
|
+
x2: "15.42",
|
|
449
|
+
y1: "13.51",
|
|
450
|
+
y2: "17.49",
|
|
451
|
+
key: "47mynk"
|
|
452
|
+
}],
|
|
453
|
+
["line", {
|
|
454
|
+
x1: "15.41",
|
|
455
|
+
x2: "8.59",
|
|
456
|
+
y1: "6.51",
|
|
457
|
+
y2: "10.49",
|
|
458
|
+
key: "1n3mei"
|
|
459
|
+
}]
|
|
460
|
+
]
|
|
461
|
+
});
|
|
462
|
+
/**
|
|
463
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
464
|
+
*
|
|
465
|
+
* This source code is licensed under the ISC license.
|
|
466
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
467
|
+
*/
|
|
468
|
+
var SlidersHorizontal = createLucideIcon({
|
|
469
|
+
name: "sliders-horizontal",
|
|
470
|
+
size: 24,
|
|
471
|
+
node: [
|
|
472
|
+
["path", {
|
|
473
|
+
d: "M10 5H3",
|
|
474
|
+
key: "1qgfaw"
|
|
475
|
+
}],
|
|
476
|
+
["path", {
|
|
477
|
+
d: "M12 19H3",
|
|
478
|
+
key: "yhmn1j"
|
|
479
|
+
}],
|
|
480
|
+
["path", {
|
|
481
|
+
d: "M14 3v4",
|
|
482
|
+
key: "1sua03"
|
|
483
|
+
}],
|
|
484
|
+
["path", {
|
|
485
|
+
d: "M16 17v4",
|
|
486
|
+
key: "1q0r14"
|
|
487
|
+
}],
|
|
488
|
+
["path", {
|
|
489
|
+
d: "M21 12h-9",
|
|
490
|
+
key: "1o4lsq"
|
|
491
|
+
}],
|
|
492
|
+
["path", {
|
|
493
|
+
d: "M21 19h-5",
|
|
494
|
+
key: "1rlt1p"
|
|
495
|
+
}],
|
|
496
|
+
["path", {
|
|
497
|
+
d: "M21 5h-7",
|
|
498
|
+
key: "1oszz2"
|
|
499
|
+
}],
|
|
500
|
+
["path", {
|
|
501
|
+
d: "M8 10v4",
|
|
502
|
+
key: "tgpxqk"
|
|
503
|
+
}],
|
|
504
|
+
["path", {
|
|
505
|
+
d: "M8 12H3",
|
|
506
|
+
key: "a7s4jb"
|
|
507
|
+
}]
|
|
508
|
+
]
|
|
509
|
+
});
|
|
510
|
+
/**
|
|
511
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
512
|
+
*
|
|
513
|
+
* This source code is licensed under the ISC license.
|
|
514
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
515
|
+
*/
|
|
516
|
+
var Smartphone = createLucideIcon({
|
|
517
|
+
name: "smartphone",
|
|
518
|
+
size: 24,
|
|
519
|
+
node: [["rect", {
|
|
520
|
+
width: "14",
|
|
521
|
+
height: "20",
|
|
522
|
+
x: "5",
|
|
523
|
+
y: "2",
|
|
524
|
+
rx: "2",
|
|
525
|
+
ry: "2",
|
|
526
|
+
key: "1yt0o3"
|
|
527
|
+
}], ["path", {
|
|
528
|
+
d: "M12 18h.01",
|
|
529
|
+
key: "mhygvu"
|
|
530
|
+
}]]
|
|
531
|
+
});
|
|
532
|
+
/**
|
|
533
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
534
|
+
*
|
|
535
|
+
* This source code is licensed under the ISC license.
|
|
536
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
537
|
+
*/
|
|
538
|
+
var SquareArrowOutUpRight = createLucideIcon({
|
|
539
|
+
name: "square-arrow-out-up-right",
|
|
540
|
+
size: 24,
|
|
541
|
+
node: [
|
|
542
|
+
["path", {
|
|
543
|
+
d: "M21 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h6",
|
|
544
|
+
key: "y09zxi"
|
|
545
|
+
}],
|
|
546
|
+
["path", {
|
|
547
|
+
d: "m21 3-9 9",
|
|
548
|
+
key: "mpx6sq"
|
|
549
|
+
}],
|
|
550
|
+
["path", {
|
|
551
|
+
d: "M15 3h6v6",
|
|
552
|
+
key: "1q9fwt"
|
|
553
|
+
}]
|
|
554
|
+
],
|
|
555
|
+
aliases: ["arrow-up-right-from-square"]
|
|
556
|
+
});
|
|
557
|
+
/**
|
|
558
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
559
|
+
*
|
|
560
|
+
* This source code is licensed under the ISC license.
|
|
561
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
562
|
+
*/
|
|
563
|
+
var Sun = createLucideIcon({
|
|
564
|
+
name: "sun",
|
|
565
|
+
size: 24,
|
|
566
|
+
node: [
|
|
567
|
+
["circle", {
|
|
568
|
+
cx: "12",
|
|
569
|
+
cy: "12",
|
|
570
|
+
r: "4",
|
|
571
|
+
key: "4exip2"
|
|
572
|
+
}],
|
|
573
|
+
["path", {
|
|
574
|
+
d: "M12 2v2",
|
|
575
|
+
key: "tus03m"
|
|
576
|
+
}],
|
|
577
|
+
["path", {
|
|
578
|
+
d: "M12 20v2",
|
|
579
|
+
key: "1lh1kg"
|
|
580
|
+
}],
|
|
581
|
+
["path", {
|
|
582
|
+
d: "m4.93 4.93 1.41 1.41",
|
|
583
|
+
key: "149t6j"
|
|
584
|
+
}],
|
|
585
|
+
["path", {
|
|
586
|
+
d: "m17.66 17.66 1.41 1.41",
|
|
587
|
+
key: "ptbguv"
|
|
588
|
+
}],
|
|
589
|
+
["path", {
|
|
590
|
+
d: "M2 12h2",
|
|
591
|
+
key: "1t8f8n"
|
|
592
|
+
}],
|
|
593
|
+
["path", {
|
|
594
|
+
d: "M20 12h2",
|
|
595
|
+
key: "1q8mjw"
|
|
596
|
+
}],
|
|
597
|
+
["path", {
|
|
598
|
+
d: "m6.34 17.66-1.41 1.41",
|
|
599
|
+
key: "1m8zz5"
|
|
600
|
+
}],
|
|
601
|
+
["path", {
|
|
602
|
+
d: "m19.07 4.93-1.41 1.41",
|
|
603
|
+
key: "1shlcs"
|
|
604
|
+
}]
|
|
605
|
+
]
|
|
606
|
+
});
|
|
607
|
+
/**
|
|
608
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
609
|
+
*
|
|
610
|
+
* This source code is licensed under the ISC license.
|
|
611
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
612
|
+
*/
|
|
613
|
+
var Tablet = createLucideIcon({
|
|
614
|
+
name: "tablet",
|
|
615
|
+
size: 24,
|
|
616
|
+
node: [["rect", {
|
|
617
|
+
width: "16",
|
|
618
|
+
height: "20",
|
|
619
|
+
x: "4",
|
|
620
|
+
y: "2",
|
|
621
|
+
rx: "2",
|
|
622
|
+
ry: "2",
|
|
623
|
+
key: "76otgf"
|
|
624
|
+
}], ["line", {
|
|
625
|
+
x1: "12",
|
|
626
|
+
x2: "12.01",
|
|
627
|
+
y1: "18",
|
|
628
|
+
y2: "18",
|
|
629
|
+
key: "1dp563"
|
|
630
|
+
}]]
|
|
631
|
+
});
|
|
632
|
+
/**
|
|
633
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
634
|
+
*
|
|
635
|
+
* This source code is licensed under the ISC license.
|
|
636
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
637
|
+
*/
|
|
638
|
+
var WholeWord = createLucideIcon({
|
|
639
|
+
name: "whole-word",
|
|
640
|
+
size: 24,
|
|
641
|
+
node: [
|
|
642
|
+
["circle", {
|
|
643
|
+
cx: "7",
|
|
644
|
+
cy: "12",
|
|
645
|
+
r: "3",
|
|
646
|
+
key: "12clwm"
|
|
647
|
+
}],
|
|
648
|
+
["path", {
|
|
649
|
+
d: "M10 9v6",
|
|
650
|
+
key: "17i7lo"
|
|
651
|
+
}],
|
|
652
|
+
["circle", {
|
|
653
|
+
cx: "17",
|
|
654
|
+
cy: "12",
|
|
655
|
+
r: "3",
|
|
656
|
+
key: "gl7c2s"
|
|
657
|
+
}],
|
|
658
|
+
["path", {
|
|
659
|
+
d: "M14 7v8",
|
|
660
|
+
key: "dl84cr"
|
|
661
|
+
}],
|
|
662
|
+
["path", {
|
|
663
|
+
d: "M22 17v1c0 .5-.5 1-1 1H3c-.5 0-1-.5-1-1v-1",
|
|
664
|
+
key: "lt2kga"
|
|
665
|
+
}]
|
|
666
|
+
]
|
|
667
|
+
});
|
|
668
|
+
/**
|
|
669
|
+
* @license @lucide/vue v1.66.0 - ISC
|
|
670
|
+
*
|
|
671
|
+
* This source code is licensed under the ISC license.
|
|
672
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
673
|
+
*/
|
|
674
|
+
var X = createLucideIcon({
|
|
675
|
+
name: "x",
|
|
676
|
+
size: 24,
|
|
677
|
+
node: [["path", {
|
|
678
|
+
d: "M18 6 6 18",
|
|
679
|
+
key: "1bl5f8"
|
|
680
|
+
}], ["path", {
|
|
681
|
+
d: "m6 6 12 12",
|
|
682
|
+
key: "d8bk6v"
|
|
683
|
+
}]]
|
|
684
|
+
});
|
|
685
|
+
// src/components/PwToastHost.vue?vue&type=script&setup=true&lang.ts
|
|
686
|
+
var _hoisted_1$18 = {
|
|
687
|
+
class: "pw-toast-host",
|
|
688
|
+
role: "status",
|
|
689
|
+
"aria-live": "polite"
|
|
690
|
+
};
|
|
691
|
+
// src/components/PwToastHost.vue
|
|
692
|
+
var PwToastHost_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
693
|
+
__name: "PwToastHost",
|
|
694
|
+
setup(__props) {
|
|
695
|
+
return (_ctx, _cache) => {
|
|
696
|
+
return openBlock(), createBlock(Teleport, { to: "body" }, [createElementVNode("div", _hoisted_1$18, [createVNode(TransitionGroup, { name: "pw-toast" }, {
|
|
697
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(toastState), (item) => {
|
|
698
|
+
return openBlock(), createElementBlock("div", {
|
|
699
|
+
key: item.id,
|
|
700
|
+
class: normalizeClass(["pw-toast", `pw-toast--${item.type}`])
|
|
701
|
+
}, toDisplayString(item.text), 3);
|
|
702
|
+
}), 128))]),
|
|
703
|
+
_: 1
|
|
704
|
+
})])]);
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
}), [["__scopeId", "data-v-4f7c0a94"]]);
|
|
708
|
+
// src/components/PwTooltip.vue
|
|
709
|
+
var PwTooltip_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
710
|
+
__name: "PwTooltip",
|
|
711
|
+
setup(__props) {
|
|
712
|
+
const text = ref("");
|
|
713
|
+
const visible = ref(false);
|
|
714
|
+
const style = ref({});
|
|
715
|
+
const bubbleEl = ref(null);
|
|
716
|
+
let timer = 0;
|
|
717
|
+
let current = null;
|
|
718
|
+
const hide = () => {
|
|
719
|
+
window.clearTimeout(timer);
|
|
720
|
+
visible.value = false;
|
|
721
|
+
current = null;
|
|
722
|
+
};
|
|
723
|
+
const place = (target) => {
|
|
724
|
+
const box = target.getBoundingClientRect();
|
|
725
|
+
const bubble = bubbleEl.value?.getBoundingClientRect();
|
|
726
|
+
const width = bubble?.width ?? 0;
|
|
727
|
+
const height = bubble?.height ?? 0;
|
|
728
|
+
const left = Math.max(8, Math.min(box.left + box.width / 2 - width / 2, window.innerWidth - width - 8));
|
|
729
|
+
const above = box.top - height - 8;
|
|
730
|
+
const top = above >= 8 ? above : box.bottom + 8;
|
|
731
|
+
style.value = {
|
|
732
|
+
left: `${left}px`,
|
|
733
|
+
top: `${top}px`
|
|
734
|
+
};
|
|
735
|
+
};
|
|
736
|
+
const show = (target) => {
|
|
737
|
+
const tip = target.dataset.tip;
|
|
738
|
+
if (!tip) return;
|
|
739
|
+
current = target;
|
|
740
|
+
text.value = tip;
|
|
741
|
+
window.clearTimeout(timer);
|
|
742
|
+
timer = window.setTimeout(async () => {
|
|
743
|
+
visible.value = true;
|
|
744
|
+
await nextTick();
|
|
745
|
+
place(target);
|
|
746
|
+
}, 120);
|
|
747
|
+
};
|
|
748
|
+
const targetOf = (node) => node instanceof Element ? node.closest("[data-tip]") : null;
|
|
749
|
+
const onOver = (event) => {
|
|
750
|
+
const target = targetOf(event.target);
|
|
751
|
+
if (target && target !== current) show(target);
|
|
752
|
+
};
|
|
753
|
+
const onOut = (event) => {
|
|
754
|
+
const target = targetOf(event.target);
|
|
755
|
+
if (!target) return;
|
|
756
|
+
if (target.contains(event.relatedTarget)) return;
|
|
757
|
+
if (target === current) hide();
|
|
758
|
+
};
|
|
759
|
+
const onFocusIn = (event) => {
|
|
760
|
+
const target = targetOf(event.target);
|
|
761
|
+
if (target) show(target);
|
|
762
|
+
};
|
|
763
|
+
onMounted(() => {
|
|
764
|
+
document.addEventListener("pointerover", onOver, true);
|
|
765
|
+
document.addEventListener("pointerout", onOut, true);
|
|
766
|
+
document.addEventListener("focusin", onFocusIn, true);
|
|
767
|
+
document.addEventListener("pointerdown", hide, true);
|
|
768
|
+
window.addEventListener("scroll", hide, true);
|
|
769
|
+
});
|
|
770
|
+
onBeforeUnmount(() => {
|
|
771
|
+
document.removeEventListener("pointerover", onOver, true);
|
|
772
|
+
document.removeEventListener("pointerout", onOut, true);
|
|
773
|
+
document.removeEventListener("focusin", onFocusIn, true);
|
|
774
|
+
document.removeEventListener("pointerdown", hide, true);
|
|
775
|
+
window.removeEventListener("scroll", hide, true);
|
|
776
|
+
window.clearTimeout(timer);
|
|
777
|
+
});
|
|
778
|
+
return (_ctx, _cache) => {
|
|
779
|
+
return visible.value ? (openBlock(), createElementBlock("div", {
|
|
780
|
+
key: 0,
|
|
781
|
+
ref_key: "bubbleEl",
|
|
782
|
+
ref: bubbleEl,
|
|
783
|
+
class: "pw-tip",
|
|
784
|
+
role: "tooltip",
|
|
785
|
+
style: normalizeStyle(style.value)
|
|
786
|
+
}, toDisplayString(text.value), 5)) : createCommentVNode("", true);
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
}), [["__scopeId", "data-v-2657675f"]]);
|
|
790
|
+
// src/components/PwInput.vue?vue&type=script&setup=true&lang.ts
|
|
791
|
+
var _hoisted_1$17 = ["data-empty"];
|
|
792
|
+
var _hoisted_2$15 = [
|
|
793
|
+
"type",
|
|
794
|
+
"value",
|
|
795
|
+
"placeholder",
|
|
796
|
+
"autofocus"
|
|
797
|
+
];
|
|
798
|
+
// src/components/PwInput.vue
|
|
799
|
+
var PwInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
800
|
+
__name: "PwInput",
|
|
801
|
+
props: {
|
|
802
|
+
modelValue: { default: "" },
|
|
803
|
+
placeholder: {},
|
|
804
|
+
clearable: {
|
|
805
|
+
type: Boolean,
|
|
806
|
+
default: true
|
|
807
|
+
},
|
|
808
|
+
size: { default: "md" },
|
|
809
|
+
autofocus: {
|
|
810
|
+
type: Boolean,
|
|
811
|
+
default: false
|
|
812
|
+
},
|
|
813
|
+
type: { default: "text" }
|
|
814
|
+
},
|
|
815
|
+
emits: [
|
|
816
|
+
"update:modelValue",
|
|
817
|
+
"keydown",
|
|
818
|
+
"focus",
|
|
819
|
+
"blur"
|
|
820
|
+
],
|
|
821
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
822
|
+
const props = __props;
|
|
823
|
+
const emit = __emit;
|
|
824
|
+
const inputRef = ref();
|
|
825
|
+
const onInput = (event) => {
|
|
826
|
+
emit("update:modelValue", event.target.value);
|
|
827
|
+
};
|
|
828
|
+
const clear = () => {
|
|
829
|
+
emit("update:modelValue", "");
|
|
830
|
+
inputRef.value?.focus();
|
|
831
|
+
};
|
|
832
|
+
__expose({ focus: () => inputRef.value?.focus() });
|
|
833
|
+
return (_ctx, _cache) => {
|
|
834
|
+
return openBlock(), createElementBlock("div", {
|
|
835
|
+
class: normalizeClass(["pw-input", `pw-input--${__props.size}`]),
|
|
836
|
+
"data-empty": !props.modelValue
|
|
837
|
+
}, [
|
|
838
|
+
renderSlot(_ctx.$slots, "prefix", {}, void 0, true),
|
|
839
|
+
createElementVNode("input", {
|
|
840
|
+
ref_key: "inputRef",
|
|
841
|
+
ref: inputRef,
|
|
842
|
+
class: "pw-input__field",
|
|
843
|
+
type: __props.type,
|
|
844
|
+
value: __props.modelValue,
|
|
845
|
+
placeholder: __props.placeholder,
|
|
846
|
+
autofocus: __props.autofocus,
|
|
847
|
+
onInput,
|
|
848
|
+
onKeydown: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("keydown", $event)),
|
|
849
|
+
onFocus: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("focus", $event)),
|
|
850
|
+
onBlur: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("blur", $event))
|
|
851
|
+
}, null, 40, _hoisted_2$15),
|
|
852
|
+
__props.clearable && __props.modelValue ? (openBlock(), createElementBlock("button", {
|
|
853
|
+
key: 0,
|
|
854
|
+
class: "pw-input__clear",
|
|
855
|
+
type: "button",
|
|
856
|
+
"data-tip": "清空",
|
|
857
|
+
"aria-label": "清空",
|
|
858
|
+
onClick: clear
|
|
859
|
+
}, " × ")) : createCommentVNode("", true),
|
|
860
|
+
renderSlot(_ctx.$slots, "suffix", {}, void 0, true)
|
|
861
|
+
], 10, _hoisted_1$17);
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
}), [["__scopeId", "data-v-d56e665e"]]);
|
|
865
|
+
// src/code/find.ts
|
|
866
|
+
var WORD_CHAR = /[\w$]/;
|
|
867
|
+
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
868
|
+
/** 收集 root 下所有文本节点(行号是 CSS counter,不参与匹配) */
|
|
869
|
+
var flattenTextNodes = (root) => {
|
|
870
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
871
|
+
const nodes = [];
|
|
872
|
+
const starts = [];
|
|
873
|
+
let text = "";
|
|
874
|
+
let current = walker.nextNode();
|
|
875
|
+
while (current) {
|
|
876
|
+
const node = current;
|
|
877
|
+
nodes.push(node);
|
|
878
|
+
starts.push(text.length);
|
|
879
|
+
text += node.data;
|
|
880
|
+
current = walker.nextNode();
|
|
881
|
+
}
|
|
882
|
+
return {
|
|
883
|
+
nodes,
|
|
884
|
+
text,
|
|
885
|
+
starts
|
|
886
|
+
};
|
|
887
|
+
};
|
|
888
|
+
/**
|
|
889
|
+
* 在扁平文本里找所有匹配。
|
|
890
|
+
*
|
|
891
|
+
* - `wholeWord` 用 `[\w$]` 判定前后边界;中文不属于词字符,按子串命中处理(符合直觉)
|
|
892
|
+
* - `limit` 防止超大文件把 DOM 撑爆,超出后不再收集
|
|
893
|
+
*/
|
|
894
|
+
var findMatches = (text, query, options = {}) => {
|
|
895
|
+
if (!query) return [];
|
|
896
|
+
const limit = options.limit ?? 2e3;
|
|
897
|
+
const pattern = new RegExp(escapeRegExp(query), options.caseSensitive ? "g" : "gi");
|
|
898
|
+
const matches = [];
|
|
899
|
+
let hit = pattern.exec(text);
|
|
900
|
+
while (hit && matches.length < limit) {
|
|
901
|
+
const start = hit.index;
|
|
902
|
+
const end = start + hit[0].length;
|
|
903
|
+
const before = start > 0 ? text[start - 1] : "";
|
|
904
|
+
const after = end < text.length ? text[end] : "";
|
|
905
|
+
if (!options.wholeWord || !WORD_CHAR.test(before) && !WORD_CHAR.test(after)) matches.push({
|
|
906
|
+
start,
|
|
907
|
+
end
|
|
908
|
+
});
|
|
909
|
+
pattern.lastIndex = end > start ? end : end + 1;
|
|
910
|
+
hit = pattern.exec(text);
|
|
911
|
+
}
|
|
912
|
+
return matches;
|
|
913
|
+
};
|
|
914
|
+
/** 把扁平文本上的匹配映射到各文本节点的局部片段 */
|
|
915
|
+
var segmentsOf = (flat, matches) => {
|
|
916
|
+
const segments = [];
|
|
917
|
+
matches.forEach((match, matchIndex) => {
|
|
918
|
+
flat.nodes.forEach((node, nodeIndex) => {
|
|
919
|
+
const nodeStart = flat.starts[nodeIndex] ?? 0;
|
|
920
|
+
const nodeEnd = nodeStart + node.data.length;
|
|
921
|
+
const start = Math.max(match.start, nodeStart);
|
|
922
|
+
const end = Math.min(match.end, nodeEnd);
|
|
923
|
+
if (start < end) segments.push({
|
|
924
|
+
nodeIndex,
|
|
925
|
+
start: start - nodeStart,
|
|
926
|
+
end: end - nodeStart,
|
|
927
|
+
matchIndex
|
|
928
|
+
});
|
|
929
|
+
});
|
|
930
|
+
});
|
|
931
|
+
return segments;
|
|
932
|
+
};
|
|
933
|
+
// src/code/CodeBlock.vue?vue&type=script&setup=true&lang.ts
|
|
934
|
+
var _hoisted_1$16 = { class: "pw-code" };
|
|
935
|
+
var _hoisted_2$14 = {
|
|
936
|
+
key: 0,
|
|
937
|
+
class: "pw-code__hint"
|
|
938
|
+
};
|
|
939
|
+
var _hoisted_3$14 = {
|
|
940
|
+
key: 1,
|
|
941
|
+
class: "pw-code__error"
|
|
942
|
+
};
|
|
943
|
+
var _hoisted_4$13 = ["innerHTML"];
|
|
944
|
+
// src/code/CodeBlock.vue
|
|
945
|
+
var CodeBlock_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
946
|
+
__name: "CodeBlock",
|
|
947
|
+
props: {
|
|
948
|
+
code: {},
|
|
949
|
+
lang: { default: "vue" },
|
|
950
|
+
query: { default: "" },
|
|
951
|
+
caseSensitive: {
|
|
952
|
+
type: Boolean,
|
|
953
|
+
default: false
|
|
954
|
+
},
|
|
955
|
+
wholeWord: {
|
|
956
|
+
type: Boolean,
|
|
957
|
+
default: false
|
|
958
|
+
},
|
|
959
|
+
active: { default: -1 }
|
|
960
|
+
},
|
|
961
|
+
emits: ["count"],
|
|
962
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
963
|
+
const props = __props;
|
|
964
|
+
const emit = __emit;
|
|
965
|
+
const html = ref("");
|
|
966
|
+
const state = ref("loading");
|
|
967
|
+
const error = ref("");
|
|
968
|
+
const bodyRef = ref(null);
|
|
969
|
+
/** 每个匹配的 mark 列表(一个匹配可能被 token 边界切成多段) */
|
|
970
|
+
let groups = [];
|
|
971
|
+
watch(() => [props.code, props.lang], async ([code, lang]) => {
|
|
972
|
+
state.value = "loading";
|
|
973
|
+
try {
|
|
974
|
+
const highlighter = await getHighlighter();
|
|
975
|
+
html.value = highlighter.codeToHtml(code, {
|
|
976
|
+
lang,
|
|
977
|
+
themes: {
|
|
978
|
+
light: PW_THEME_LIGHT,
|
|
979
|
+
dark: PW_THEME_DARK
|
|
980
|
+
},
|
|
981
|
+
defaultColor: false
|
|
982
|
+
});
|
|
983
|
+
state.value = "ready";
|
|
984
|
+
await nextTick();
|
|
985
|
+
applyQuery();
|
|
986
|
+
} catch (err) {
|
|
987
|
+
error.value = err instanceof Error ? err.message : String(err);
|
|
988
|
+
state.value = "error";
|
|
989
|
+
}
|
|
990
|
+
}, { immediate: true });
|
|
991
|
+
watch(() => [
|
|
992
|
+
props.query,
|
|
993
|
+
props.caseSensitive,
|
|
994
|
+
props.wholeWord
|
|
995
|
+
], async () => {
|
|
996
|
+
if (state.value !== "ready") return;
|
|
997
|
+
await nextTick();
|
|
998
|
+
applyQuery();
|
|
999
|
+
});
|
|
1000
|
+
watch(() => props.active, () => paintActive());
|
|
1001
|
+
const clearHits = () => {
|
|
1002
|
+
if (!groups.length) return;
|
|
1003
|
+
const root = bodyRef.value;
|
|
1004
|
+
for (const group of groups) for (const mark of group) mark.replaceWith(document.createTextNode(mark.textContent ?? ""));
|
|
1005
|
+
groups = [];
|
|
1006
|
+
root?.normalize();
|
|
1007
|
+
};
|
|
1008
|
+
const inDocumentOrder = (group) => [...group].sort((a, b) => a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
|
|
1009
|
+
/** 扁平文本匹配 + 节点内倒序切分 → <mark>;切在后面的偏移不会影响前面的 */
|
|
1010
|
+
const applyQuery = () => {
|
|
1011
|
+
clearHits();
|
|
1012
|
+
const root = bodyRef.value;
|
|
1013
|
+
const query = props.query;
|
|
1014
|
+
if (!root || !query) {
|
|
1015
|
+
emit("count", 0);
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
const flat = flattenTextNodes(root);
|
|
1019
|
+
const matches = findMatches(flat.text, query, {
|
|
1020
|
+
caseSensitive: props.caseSensitive,
|
|
1021
|
+
wholeWord: props.wholeWord
|
|
1022
|
+
});
|
|
1023
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1024
|
+
for (const segment of segmentsOf(flat, matches)) {
|
|
1025
|
+
const list = buckets.get(segment.nodeIndex) ?? [];
|
|
1026
|
+
list.push(segment);
|
|
1027
|
+
buckets.set(segment.nodeIndex, list);
|
|
1028
|
+
}
|
|
1029
|
+
const built = matches.map(() => []);
|
|
1030
|
+
for (const [nodeIndex, list] of buckets) {
|
|
1031
|
+
const node = flat.nodes[nodeIndex];
|
|
1032
|
+
if (!node) continue;
|
|
1033
|
+
list.sort((a, b) => b.start - a.start);
|
|
1034
|
+
for (const segment of list) {
|
|
1035
|
+
node.splitText(segment.end);
|
|
1036
|
+
const middle = node.splitText(segment.start);
|
|
1037
|
+
const mark = document.createElement("mark");
|
|
1038
|
+
mark.className = "pw-code__hit";
|
|
1039
|
+
mark.dataset.hit = String(segment.matchIndex);
|
|
1040
|
+
mark.textContent = middle.data;
|
|
1041
|
+
middle.replaceWith(mark);
|
|
1042
|
+
built[segment.matchIndex]?.push(mark);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
groups = built.map(inDocumentOrder).filter((group) => group.length > 0);
|
|
1046
|
+
emit("count", groups.length);
|
|
1047
|
+
paintActive();
|
|
1048
|
+
scrollToHit(props.active);
|
|
1049
|
+
};
|
|
1050
|
+
const paintActive = () => {
|
|
1051
|
+
const active = props.active;
|
|
1052
|
+
groups.forEach((group, index) => {
|
|
1053
|
+
const on = index === active;
|
|
1054
|
+
for (const mark of group) mark.classList.toggle("is-active", on);
|
|
1055
|
+
});
|
|
1056
|
+
};
|
|
1057
|
+
/** 滚到指定匹配:只滚最近的滚动容器,避免整页跟着动 */
|
|
1058
|
+
const scrollToHit = (index) => {
|
|
1059
|
+
const mark = groups[index]?.[0];
|
|
1060
|
+
if (!mark) return;
|
|
1061
|
+
let parent = mark.parentElement;
|
|
1062
|
+
while (parent && parent !== document.body) {
|
|
1063
|
+
if (parent.scrollHeight > parent.clientHeight + 1) {
|
|
1064
|
+
const offset = mark.getBoundingClientRect().top - parent.getBoundingClientRect().top;
|
|
1065
|
+
parent.scrollTo({
|
|
1066
|
+
top: parent.scrollTop + offset - parent.clientHeight / 2,
|
|
1067
|
+
behavior: "smooth"
|
|
1068
|
+
});
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
parent = parent.parentElement;
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
__expose({ scrollToHit });
|
|
1075
|
+
onBeforeUnmount(() => clearHits());
|
|
1076
|
+
return (_ctx, _cache) => {
|
|
1077
|
+
return openBlock(), createElementBlock("div", _hoisted_1$16, [state.value === "loading" ? (openBlock(), createElementBlock("div", _hoisted_2$14, "高亮中…")) : state.value === "error" ? (openBlock(), createElementBlock("pre", _hoisted_3$14, toDisplayString(error.value), 1)) : (openBlock(), createElementBlock("div", {
|
|
1078
|
+
key: 2,
|
|
1079
|
+
ref_key: "bodyRef",
|
|
1080
|
+
ref: bodyRef,
|
|
1081
|
+
class: "pw-code__body",
|
|
1082
|
+
innerHTML: html.value
|
|
1083
|
+
}, null, 8, _hoisted_4$13))]);
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
}), [["__scopeId", "data-v-14e60795"]]);
|
|
1087
|
+
// src/code/clipboard.ts
|
|
1088
|
+
/** 复制文本:优先 Clipboard API,失败时退回到 execCommand(无权限/非安全上下文) */
|
|
1089
|
+
var copyText = async (text) => {
|
|
1090
|
+
try {
|
|
1091
|
+
if (navigator.clipboard?.writeText) {
|
|
1092
|
+
await navigator.clipboard.writeText(text);
|
|
1093
|
+
return true;
|
|
1094
|
+
}
|
|
1095
|
+
} catch {}
|
|
1096
|
+
try {
|
|
1097
|
+
const area = document.createElement("textarea");
|
|
1098
|
+
area.value = text;
|
|
1099
|
+
area.setAttribute("readonly", "");
|
|
1100
|
+
area.style.position = "fixed";
|
|
1101
|
+
area.style.top = "-1000px";
|
|
1102
|
+
area.style.opacity = "0";
|
|
1103
|
+
document.body.appendChild(area);
|
|
1104
|
+
area.select();
|
|
1105
|
+
const ok = document.execCommand("copy");
|
|
1106
|
+
area.remove();
|
|
1107
|
+
return ok;
|
|
1108
|
+
} catch {
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
// src/app/PlaygroundApiTable.vue?vue&type=script&setup=true&lang.ts
|
|
1113
|
+
var _hoisted_1$15 = { class: "pw-api__head" };
|
|
1114
|
+
var _hoisted_2$13 = { class: "pw-api__title" };
|
|
1115
|
+
var _hoisted_3$13 = { class: "pw-api__name" };
|
|
1116
|
+
var _hoisted_4$12 = { class: "pw-api__meta" };
|
|
1117
|
+
var _hoisted_5$11 = { class: "pw-api__warn" };
|
|
1118
|
+
var _hoisted_6$9 = { class: "pw-api__tabs" };
|
|
1119
|
+
var _hoisted_7$7 = ["onClick"];
|
|
1120
|
+
var _hoisted_8$6 = { class: "pw-api__count" };
|
|
1121
|
+
var _hoisted_9$6 = { class: "pw-api__body" };
|
|
1122
|
+
var _hoisted_10$5 = {
|
|
1123
|
+
key: 0,
|
|
1124
|
+
class: "pw-api__col-default"
|
|
1125
|
+
};
|
|
1126
|
+
var _hoisted_11$5 = ["data-api"];
|
|
1127
|
+
var _hoisted_12$4 = { class: "pw-api__name-cell" };
|
|
1128
|
+
var _hoisted_13$4 = { class: "pw-api__name-line" };
|
|
1129
|
+
var _hoisted_14$4 = {
|
|
1130
|
+
key: 0,
|
|
1131
|
+
class: "pw-api__required"
|
|
1132
|
+
};
|
|
1133
|
+
var _hoisted_15$4 = ["title", "onClick"];
|
|
1134
|
+
var _hoisted_16$4 = ["title", "onClick"];
|
|
1135
|
+
var _hoisted_17$4 = {
|
|
1136
|
+
key: 2,
|
|
1137
|
+
class: "pw-api__empty"
|
|
1138
|
+
};
|
|
1139
|
+
var _hoisted_18$3 = {
|
|
1140
|
+
key: 3,
|
|
1141
|
+
class: "pw-api__signature"
|
|
1142
|
+
};
|
|
1143
|
+
var _hoisted_19$3 = ["title"];
|
|
1144
|
+
var _hoisted_20$3 = { key: 0 };
|
|
1145
|
+
var _hoisted_21$3 = {
|
|
1146
|
+
key: 1,
|
|
1147
|
+
class: "pw-api__empty"
|
|
1148
|
+
};
|
|
1149
|
+
var _hoisted_22$3 = { class: "pw-api__desc" };
|
|
1150
|
+
var _hoisted_23$3 = {
|
|
1151
|
+
key: 1,
|
|
1152
|
+
class: "pw-api__empty",
|
|
1153
|
+
title: "补 JSDoc 就会显示在这里"
|
|
1154
|
+
};
|
|
1155
|
+
var _hoisted_24$3 = { class: "pw-api__demo-cell" };
|
|
1156
|
+
var _hoisted_25$2 = { class: "pw-api__demo" };
|
|
1157
|
+
var _hoisted_26$2 = ["onClick"];
|
|
1158
|
+
var _hoisted_27$2 = {
|
|
1159
|
+
key: 0,
|
|
1160
|
+
class: "pw-api__empty"
|
|
1161
|
+
};
|
|
1162
|
+
var _hoisted_28$2 = {
|
|
1163
|
+
key: 1,
|
|
1164
|
+
class: "pw-api__types"
|
|
1165
|
+
};
|
|
1166
|
+
var _hoisted_29$2 = ["data-type"];
|
|
1167
|
+
var _hoisted_30$2 = { class: "pw-api__type-head" };
|
|
1168
|
+
var _hoisted_31$2 = { class: "pw-api__type-name" };
|
|
1169
|
+
var _hoisted_32$2 = { class: "pw-api__type-kind" };
|
|
1170
|
+
var _hoisted_33$2 = ["title"];
|
|
1171
|
+
var _hoisted_34$2 = ["title", "onClick"];
|
|
1172
|
+
// src/app/PlaygroundApiTable.vue
|
|
1173
|
+
var PlaygroundApiTable_default = /* @__PURE__ */ defineComponent({
|
|
1174
|
+
__name: "PlaygroundApiTable",
|
|
1175
|
+
props: {
|
|
1176
|
+
contract: {},
|
|
1177
|
+
scenes: {},
|
|
1178
|
+
focus: {},
|
|
1179
|
+
notes: {}
|
|
1180
|
+
},
|
|
1181
|
+
emits: ["jump"],
|
|
1182
|
+
setup(__props, { emit: __emit }) {
|
|
1183
|
+
const props = __props;
|
|
1184
|
+
const emit = __emit;
|
|
1185
|
+
const TAB_LABELS = [
|
|
1186
|
+
{
|
|
1187
|
+
key: "props",
|
|
1188
|
+
label: "Props"
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
key: "events",
|
|
1192
|
+
label: "Events"
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
key: "slots",
|
|
1196
|
+
label: "Slots"
|
|
1197
|
+
},
|
|
1198
|
+
{
|
|
1199
|
+
key: "exposed",
|
|
1200
|
+
label: "Exposes"
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
key: "types",
|
|
1204
|
+
label: "Types"
|
|
1205
|
+
}
|
|
1206
|
+
];
|
|
1207
|
+
const tab = ref("props");
|
|
1208
|
+
const typeDefs = computed(() => props.contract.types ?? []);
|
|
1209
|
+
const counts = computed(() => ({
|
|
1210
|
+
props: props.contract.props.length,
|
|
1211
|
+
events: props.contract.events.length,
|
|
1212
|
+
slots: props.contract.slots.length,
|
|
1213
|
+
exposed: props.contract.exposed.length,
|
|
1214
|
+
types: typeDefs.value.length
|
|
1215
|
+
}));
|
|
1216
|
+
const total = computed(() => counts.value.props + counts.value.events + counts.value.slots + counts.value.exposed);
|
|
1217
|
+
const undocumentedCount = computed(() => props.contract.undocumented.length);
|
|
1218
|
+
const tabs = computed(() => TAB_LABELS.filter((item) => counts.value[item.key] > 0).map((item) => ({
|
|
1219
|
+
...item,
|
|
1220
|
+
count: counts.value[item.key]
|
|
1221
|
+
})));
|
|
1222
|
+
/** 某个 API 有哪些场景演示 */
|
|
1223
|
+
const scenesOf = (kind, name) => props.scenes.filter((scene) => (scene.api ?? []).includes(name));
|
|
1224
|
+
const notesOf = (kind, name) => props.notes?.[`${kind}:${name}`] ?? "";
|
|
1225
|
+
/** 类型名 → 声明,供类型列点击跳转 */
|
|
1226
|
+
const typeNames = computed(() => typeDefs.value.map((item) => item.name));
|
|
1227
|
+
/** 类型字符串里引用到的具名类型(`TableColumn[]` → `TableColumn`) */
|
|
1228
|
+
const linkedType = (text) => typeNames.value.find((name) => new RegExp(`\\b${name}\\b`).test(text)) ?? "";
|
|
1229
|
+
const rows = computed(() => {
|
|
1230
|
+
if (tab.value === "props") return props.contract.props.map((item) => ({
|
|
1231
|
+
key: item.name,
|
|
1232
|
+
name: item.name,
|
|
1233
|
+
type: item.type,
|
|
1234
|
+
default: item.default,
|
|
1235
|
+
required: item.required,
|
|
1236
|
+
description: item.description || notesOf("props", item.name),
|
|
1237
|
+
signature: "",
|
|
1238
|
+
demos: scenesOf("props", item.name)
|
|
1239
|
+
}));
|
|
1240
|
+
if (tab.value === "events") return props.contract.events.map((item) => ({
|
|
1241
|
+
key: item.name,
|
|
1242
|
+
name: item.name,
|
|
1243
|
+
type: item.type,
|
|
1244
|
+
default: "",
|
|
1245
|
+
required: false,
|
|
1246
|
+
description: item.description || notesOf("events", item.name),
|
|
1247
|
+
signature: item.signature,
|
|
1248
|
+
demos: scenesOf("events", item.name)
|
|
1249
|
+
}));
|
|
1250
|
+
if (tab.value === "slots") return props.contract.slots.map((item) => ({
|
|
1251
|
+
key: item.name,
|
|
1252
|
+
name: item.name,
|
|
1253
|
+
type: item.type,
|
|
1254
|
+
default: "",
|
|
1255
|
+
required: false,
|
|
1256
|
+
description: item.description || notesOf("slots", item.name),
|
|
1257
|
+
signature: "",
|
|
1258
|
+
demos: scenesOf("slots", item.name)
|
|
1259
|
+
}));
|
|
1260
|
+
if (tab.value === "exposed") return props.contract.exposed.map((item) => ({
|
|
1261
|
+
key: item.name,
|
|
1262
|
+
name: item.name,
|
|
1263
|
+
type: item.type,
|
|
1264
|
+
default: "",
|
|
1265
|
+
required: false,
|
|
1266
|
+
description: item.description || notesOf("exposed", item.name),
|
|
1267
|
+
signature: "",
|
|
1268
|
+
demos: scenesOf("exposed", item.name)
|
|
1269
|
+
}));
|
|
1270
|
+
return [];
|
|
1271
|
+
});
|
|
1272
|
+
/** 类型页签里当前高亮的类型 */
|
|
1273
|
+
const focusedType = ref("");
|
|
1274
|
+
const openType = async (name) => {
|
|
1275
|
+
tab.value = "types";
|
|
1276
|
+
focusedType.value = name;
|
|
1277
|
+
await nextTick();
|
|
1278
|
+
rootEl.value?.querySelector(`[data-type="${CSS.escape(name)}"]`)?.scrollIntoView({
|
|
1279
|
+
block: "start",
|
|
1280
|
+
behavior: "smooth"
|
|
1281
|
+
});
|
|
1282
|
+
};
|
|
1283
|
+
/** 高亮来自场景内标签的点击 */
|
|
1284
|
+
const highlighted = ref("");
|
|
1285
|
+
const rootEl = ref(null);
|
|
1286
|
+
watch(() => props.focus, async (value) => {
|
|
1287
|
+
if (!value) return;
|
|
1288
|
+
const [kind, name] = value.split(":");
|
|
1289
|
+
if (kind && TAB_LABELS.some((item) => item.key === kind)) tab.value = kind;
|
|
1290
|
+
highlighted.value = name ?? "";
|
|
1291
|
+
await nextTick();
|
|
1292
|
+
rootEl.value?.querySelector(`[data-api="${CSS.escape(highlighted.value)}"]`)?.scrollIntoView({ block: "center" });
|
|
1293
|
+
}, { immediate: true });
|
|
1294
|
+
const copyType = async (text) => {
|
|
1295
|
+
if (!text) return;
|
|
1296
|
+
const ok = await copyText(text);
|
|
1297
|
+
toast(ok ? `已复制 ${text}` : "复制失败");
|
|
1298
|
+
};
|
|
1299
|
+
return (_ctx, _cache) => {
|
|
1300
|
+
return openBlock(), createElementBlock("section", {
|
|
1301
|
+
ref_key: "rootEl",
|
|
1302
|
+
ref: rootEl,
|
|
1303
|
+
class: "pw-api"
|
|
1304
|
+
}, [createElementVNode("header", _hoisted_1$15, [createElementVNode("div", _hoisted_2$13, [createElementVNode("span", _hoisted_3$13, toDisplayString(__props.contract.name), 1), createElementVNode("span", _hoisted_4$12, [createTextVNode(toDisplayString(total.value) + " 个 API ", 1), undocumentedCount.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[0] || (_cache[0] = createTextVNode(" · ", -1)), createElementVNode("span", _hoisted_5$11, toDisplayString(undocumentedCount.value) + " 个缺说明", 1)], 64)) : createCommentVNode("", true)])]), createElementVNode("div", _hoisted_6$9, [(openBlock(true), createElementBlock(Fragment, null, renderList(tabs.value, (item) => {
|
|
1305
|
+
return openBlock(), createElementBlock("button", {
|
|
1306
|
+
key: item.key,
|
|
1307
|
+
type: "button",
|
|
1308
|
+
class: normalizeClass(["pw-api__tab", { "is-active": tab.value === item.key }]),
|
|
1309
|
+
onClick: ($event) => tab.value = item.key
|
|
1310
|
+
}, [createTextVNode(toDisplayString(item.label) + " ", 1), createElementVNode("span", _hoisted_8$6, toDisplayString(item.count), 1)], 10, _hoisted_7$7);
|
|
1311
|
+
}), 128))])]), createElementVNode("div", _hoisted_9$6, [tab.value !== "types" ? (openBlock(), createElementBlock("table", {
|
|
1312
|
+
key: 0,
|
|
1313
|
+
class: normalizeClass(["pw-api__table", { "is-props": tab.value === "props" }])
|
|
1314
|
+
}, [createElementVNode("thead", null, [createElementVNode("tr", null, [
|
|
1315
|
+
_cache[1] || (_cache[1] = createElementVNode("th", { class: "pw-api__col-name" }, "名称", -1)),
|
|
1316
|
+
_cache[2] || (_cache[2] = createElementVNode("th", { class: "pw-api__col-type" }, "类型", -1)),
|
|
1317
|
+
tab.value === "props" ? (openBlock(), createElementBlock("th", _hoisted_10$5, "默认值")) : createCommentVNode("", true),
|
|
1318
|
+
_cache[3] || (_cache[3] = createElementVNode("th", null, "说明", -1)),
|
|
1319
|
+
_cache[4] || (_cache[4] = createElementVNode("th", { class: "pw-api__col-demo" }, "演示", -1))
|
|
1320
|
+
])]), createElementVNode("tbody", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(rows.value, (row) => {
|
|
1321
|
+
return openBlock(), createElementBlock("tr", {
|
|
1322
|
+
key: row.key,
|
|
1323
|
+
"data-api": row.name,
|
|
1324
|
+
class: normalizeClass({
|
|
1325
|
+
"is-missing": !row.description,
|
|
1326
|
+
"is-focus": row.name === highlighted.value
|
|
1327
|
+
})
|
|
1328
|
+
}, [
|
|
1329
|
+
createElementVNode("td", _hoisted_12$4, [createElementVNode("div", _hoisted_13$4, [createElementVNode("code", null, toDisplayString(row.name), 1), row.required ? (openBlock(), createElementBlock("span", _hoisted_14$4, "必填")) : createCommentVNode("", true)])]),
|
|
1330
|
+
createElementVNode("td", null, [linkedType(row.type) ? (openBlock(), createElementBlock("button", {
|
|
1331
|
+
key: 0,
|
|
1332
|
+
type: "button",
|
|
1333
|
+
class: "pw-api__type is-link",
|
|
1334
|
+
title: `查看 ${linkedType(row.type)} 定义`,
|
|
1335
|
+
onClick: ($event) => openType(linkedType(row.type))
|
|
1336
|
+
}, toDisplayString(row.type), 9, _hoisted_15$4)) : row.type ? (openBlock(), createElementBlock("button", {
|
|
1337
|
+
key: 1,
|
|
1338
|
+
type: "button",
|
|
1339
|
+
class: "pw-api__type",
|
|
1340
|
+
title: `复制 ${row.type}`,
|
|
1341
|
+
onClick: ($event) => copyType(row.type)
|
|
1342
|
+
}, toDisplayString(row.type), 9, _hoisted_16$4)) : (openBlock(), createElementBlock("span", _hoisted_17$4, "—")), row.signature && row.signature !== row.type ? (openBlock(), createElementBlock("div", _hoisted_18$3, toDisplayString(row.signature), 1)) : createCommentVNode("", true)]),
|
|
1343
|
+
tab.value === "props" ? (openBlock(), createElementBlock("td", {
|
|
1344
|
+
key: 0,
|
|
1345
|
+
class: "pw-api__default",
|
|
1346
|
+
title: row.default
|
|
1347
|
+
}, [row.default ? (openBlock(), createElementBlock("span", _hoisted_20$3, toDisplayString(row.default), 1)) : (openBlock(), createElementBlock("span", _hoisted_21$3, "—"))], 8, _hoisted_19$3)) : createCommentVNode("", true),
|
|
1348
|
+
createElementVNode("td", _hoisted_22$3, [row.description ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(row.description), 1)], 64)) : (openBlock(), createElementBlock("span", _hoisted_23$3, "缺说明"))]),
|
|
1349
|
+
createElementVNode("td", _hoisted_24$3, [createElementVNode("div", _hoisted_25$2, [(openBlock(true), createElementBlock(Fragment, null, renderList(row.demos, (demo) => {
|
|
1350
|
+
return openBlock(), createElementBlock("button", {
|
|
1351
|
+
key: demo.id,
|
|
1352
|
+
type: "button",
|
|
1353
|
+
class: "pw-api__demo-link",
|
|
1354
|
+
onClick: ($event) => emit("jump", demo.name)
|
|
1355
|
+
}, toDisplayString(demo.title), 9, _hoisted_26$2);
|
|
1356
|
+
}), 128)), !row.demos.length ? (openBlock(), createElementBlock("span", _hoisted_27$2, "无")) : createCommentVNode("", true)])])
|
|
1357
|
+
], 10, _hoisted_11$5);
|
|
1358
|
+
}), 128))])], 2)) : (openBlock(), createElementBlock("div", _hoisted_28$2, [(openBlock(true), createElementBlock(Fragment, null, renderList(typeDefs.value, (item) => {
|
|
1359
|
+
return openBlock(), createElementBlock("article", {
|
|
1360
|
+
key: item.name,
|
|
1361
|
+
class: normalizeClass(["pw-api__type-card", { "is-focus": item.name === focusedType.value }]),
|
|
1362
|
+
"data-type": item.name
|
|
1363
|
+
}, [createElementVNode("header", _hoisted_30$2, [
|
|
1364
|
+
createElementVNode("code", _hoisted_31$2, toDisplayString(item.name), 1),
|
|
1365
|
+
createElementVNode("span", _hoisted_32$2, toDisplayString(item.kind), 1),
|
|
1366
|
+
createElementVNode("span", {
|
|
1367
|
+
class: "pw-api__type-file",
|
|
1368
|
+
title: item.file
|
|
1369
|
+
}, toDisplayString(item.file), 9, _hoisted_33$2),
|
|
1370
|
+
createElementVNode("button", {
|
|
1371
|
+
type: "button",
|
|
1372
|
+
class: "pw-api__copy",
|
|
1373
|
+
title: `复制 ${item.name} 定义`,
|
|
1374
|
+
onClick: ($event) => copyType(item.code)
|
|
1375
|
+
}, [createVNode(unref(Copy), {
|
|
1376
|
+
size: 12,
|
|
1377
|
+
"stroke-width": 2
|
|
1378
|
+
}), _cache[5] || (_cache[5] = createTextVNode(" 复制 ", -1))], 8, _hoisted_34$2)
|
|
1379
|
+
]), createVNode(CodeBlock_default, {
|
|
1380
|
+
code: item.code,
|
|
1381
|
+
lang: "ts"
|
|
1382
|
+
}, null, 8, ["code"])], 10, _hoisted_29$2);
|
|
1383
|
+
}), 128))]))])], 512);
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
});
|
|
1387
|
+
// src/app/PlaygroundModuleApi.vue?vue&type=script&setup=true&lang.ts
|
|
1388
|
+
var _hoisted_1$14 = { class: "pw-api__head" };
|
|
1389
|
+
var _hoisted_2$12 = { class: "pw-api__title" };
|
|
1390
|
+
var _hoisted_3$12 = { class: "pw-api__name" };
|
|
1391
|
+
var _hoisted_4$11 = { class: "pw-api__meta" };
|
|
1392
|
+
var _hoisted_5$10 = { class: "pw-api__warn" };
|
|
1393
|
+
var _hoisted_6$8 = { class: "pw-api__rel" };
|
|
1394
|
+
var _hoisted_7$6 = { class: "pw-api__tabs" };
|
|
1395
|
+
var _hoisted_8$5 = ["onClick"];
|
|
1396
|
+
var _hoisted_9$5 = { class: "pw-api__count" };
|
|
1397
|
+
var _hoisted_10$4 = { class: "pw-api__body" };
|
|
1398
|
+
var _hoisted_11$4 = {
|
|
1399
|
+
key: 0,
|
|
1400
|
+
class: "pw-api__table is-functions"
|
|
1401
|
+
};
|
|
1402
|
+
var _hoisted_12$3 = ["data-api"];
|
|
1403
|
+
var _hoisted_13$3 = { class: "pw-api__name-cell" };
|
|
1404
|
+
var _hoisted_14$3 = { class: "pw-api__name-line" };
|
|
1405
|
+
var _hoisted_15$3 = { class: "pw-api__required" };
|
|
1406
|
+
var _hoisted_16$3 = { class: "pw-api__signature" };
|
|
1407
|
+
var _hoisted_17$3 = { class: "pw-api__params" };
|
|
1408
|
+
var _hoisted_18$2 = { class: "pw-api__param-type" };
|
|
1409
|
+
var _hoisted_19$2 = {
|
|
1410
|
+
key: 0,
|
|
1411
|
+
class: "pw-api__param-default"
|
|
1412
|
+
};
|
|
1413
|
+
var _hoisted_20$2 = {
|
|
1414
|
+
key: 1,
|
|
1415
|
+
class: "pw-api__param-desc"
|
|
1416
|
+
};
|
|
1417
|
+
var _hoisted_21$2 = {
|
|
1418
|
+
key: 0,
|
|
1419
|
+
class: "pw-api__empty"
|
|
1420
|
+
};
|
|
1421
|
+
var _hoisted_22$2 = ["title", "onClick"];
|
|
1422
|
+
var _hoisted_23$2 = ["title", "onClick"];
|
|
1423
|
+
var _hoisted_24$2 = {
|
|
1424
|
+
key: 2,
|
|
1425
|
+
class: "pw-api__empty"
|
|
1426
|
+
};
|
|
1427
|
+
var _hoisted_25$1 = { class: "pw-api__desc" };
|
|
1428
|
+
var _hoisted_26$1 = {
|
|
1429
|
+
key: 1,
|
|
1430
|
+
class: "pw-api__empty",
|
|
1431
|
+
title: "补 JSDoc 就会显示在这里"
|
|
1432
|
+
};
|
|
1433
|
+
var _hoisted_27$1 = { class: "pw-api__demo-cell" };
|
|
1434
|
+
var _hoisted_28$1 = { class: "pw-api__demo" };
|
|
1435
|
+
var _hoisted_29$1 = ["onClick"];
|
|
1436
|
+
var _hoisted_30$1 = {
|
|
1437
|
+
key: 0,
|
|
1438
|
+
class: "pw-api__empty"
|
|
1439
|
+
};
|
|
1440
|
+
var _hoisted_31$1 = {
|
|
1441
|
+
key: 1,
|
|
1442
|
+
class: "pw-api__table"
|
|
1443
|
+
};
|
|
1444
|
+
var _hoisted_32$1 = ["data-api"];
|
|
1445
|
+
var _hoisted_33$1 = { class: "pw-api__name-cell" };
|
|
1446
|
+
var _hoisted_34$1 = { class: "pw-api__name-line" };
|
|
1447
|
+
var _hoisted_35$1 = ["title", "onClick"];
|
|
1448
|
+
var _hoisted_36$1 = ["title", "onClick"];
|
|
1449
|
+
var _hoisted_37$1 = {
|
|
1450
|
+
key: 2,
|
|
1451
|
+
class: "pw-api__empty"
|
|
1452
|
+
};
|
|
1453
|
+
var _hoisted_38$1 = ["title"];
|
|
1454
|
+
var _hoisted_39$1 = { key: 0 };
|
|
1455
|
+
var _hoisted_40 = {
|
|
1456
|
+
key: 1,
|
|
1457
|
+
class: "pw-api__empty"
|
|
1458
|
+
};
|
|
1459
|
+
var _hoisted_41 = { class: "pw-api__desc" };
|
|
1460
|
+
var _hoisted_42 = {
|
|
1461
|
+
key: 1,
|
|
1462
|
+
class: "pw-api__empty",
|
|
1463
|
+
title: "补 JSDoc 就会显示在这里"
|
|
1464
|
+
};
|
|
1465
|
+
var _hoisted_43 = { class: "pw-api__demo-cell" };
|
|
1466
|
+
var _hoisted_44 = { class: "pw-api__demo" };
|
|
1467
|
+
var _hoisted_45 = ["onClick"];
|
|
1468
|
+
var _hoisted_46 = {
|
|
1469
|
+
key: 0,
|
|
1470
|
+
class: "pw-api__empty"
|
|
1471
|
+
};
|
|
1472
|
+
var _hoisted_47 = {
|
|
1473
|
+
key: 2,
|
|
1474
|
+
class: "pw-api__types"
|
|
1475
|
+
};
|
|
1476
|
+
var _hoisted_48 = ["data-type"];
|
|
1477
|
+
var _hoisted_49 = { class: "pw-api__type-head" };
|
|
1478
|
+
var _hoisted_50 = { class: "pw-api__type-name" };
|
|
1479
|
+
var _hoisted_51 = { class: "pw-api__type-kind" };
|
|
1480
|
+
var _hoisted_52 = ["title"];
|
|
1481
|
+
var _hoisted_53 = ["title", "onClick"];
|
|
1482
|
+
// src/app/PlaygroundModuleApi.vue
|
|
1483
|
+
var PlaygroundModuleApi_default = /* @__PURE__ */ defineComponent({
|
|
1484
|
+
__name: "PlaygroundModuleApi",
|
|
1485
|
+
props: {
|
|
1486
|
+
module: {},
|
|
1487
|
+
scenes: {}
|
|
1488
|
+
},
|
|
1489
|
+
emits: ["jump"],
|
|
1490
|
+
setup(__props, { emit: __emit }) {
|
|
1491
|
+
const props = __props;
|
|
1492
|
+
const emit = __emit;
|
|
1493
|
+
const tab = ref("functions");
|
|
1494
|
+
const tabs = computed(() => [
|
|
1495
|
+
{
|
|
1496
|
+
id: "functions",
|
|
1497
|
+
label: "Functions",
|
|
1498
|
+
count: props.module.functions.length
|
|
1499
|
+
},
|
|
1500
|
+
{
|
|
1501
|
+
id: "constants",
|
|
1502
|
+
label: "Constants",
|
|
1503
|
+
count: props.module.constants.length
|
|
1504
|
+
},
|
|
1505
|
+
{
|
|
1506
|
+
id: "types",
|
|
1507
|
+
label: "Types",
|
|
1508
|
+
count: props.module.types.length
|
|
1509
|
+
}
|
|
1510
|
+
].filter((item) => item.count > 0));
|
|
1511
|
+
/** 与组件 API 表一致:只列有内容的页签,当前页签失效时回落到第一个 */
|
|
1512
|
+
const activeTab = computed(() => tabs.value.some((item) => item.id === tab.value) ? tab.value : tabs.value[0]?.id ?? "functions");
|
|
1513
|
+
const total = computed(() => props.module.functions.length + props.module.constants.length);
|
|
1514
|
+
const missing = computed(() => props.module.uncovered.length);
|
|
1515
|
+
const typeNames = computed(() => props.module.types.map((item) => item.name));
|
|
1516
|
+
/** 类型字符串里引用到的具名类型(`FormatMoneyOptions` → 可跳转) */
|
|
1517
|
+
const linkedType = (text) => typeNames.value.find((name) => new RegExp(`\\b${name}\\b`).test(text)) ?? "";
|
|
1518
|
+
const rootEl = ref(null);
|
|
1519
|
+
const focusedType = ref("");
|
|
1520
|
+
const openType = async (name) => {
|
|
1521
|
+
tab.value = "types";
|
|
1522
|
+
focusedType.value = name;
|
|
1523
|
+
await nextTick();
|
|
1524
|
+
rootEl.value?.querySelector(`[data-type="${CSS.escape(name)}"]`)?.scrollIntoView({
|
|
1525
|
+
block: "start",
|
|
1526
|
+
behavior: "smooth"
|
|
1527
|
+
});
|
|
1528
|
+
};
|
|
1529
|
+
const copyType = async (text) => {
|
|
1530
|
+
if (!text) return;
|
|
1531
|
+
const ok = await copyText(text);
|
|
1532
|
+
toast(ok ? `已复制 ${text}` : "复制失败");
|
|
1533
|
+
};
|
|
1534
|
+
/** 场景 id → 标题(表里的「场景」列用标题更可读) */
|
|
1535
|
+
const sceneTitle = (id) => props.scenes?.find((scene) => scene.id === id)?.title ?? id.split("/").pop() ?? id;
|
|
1536
|
+
return (_ctx, _cache) => {
|
|
1537
|
+
return openBlock(), createElementBlock("section", {
|
|
1538
|
+
ref_key: "rootEl",
|
|
1539
|
+
ref: rootEl,
|
|
1540
|
+
class: "pw-api"
|
|
1541
|
+
}, [createElementVNode("header", _hoisted_1$14, [createElementVNode("div", _hoisted_2$12, [createElementVNode("span", _hoisted_3$12, toDisplayString(__props.module.name), 1), createElementVNode("span", _hoisted_4$11, [
|
|
1542
|
+
createTextVNode(toDisplayString(total.value) + " 个导出 ", 1),
|
|
1543
|
+
missing.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[0] || (_cache[0] = createTextVNode(" · ", -1)), createElementVNode("span", _hoisted_5$10, toDisplayString(missing.value) + " 个无场景", 1)], 64)) : createCommentVNode("", true),
|
|
1544
|
+
createElementVNode("span", _hoisted_6$8, toDisplayString(__props.module.rel), 1)
|
|
1545
|
+
])]), createElementVNode("div", _hoisted_7$6, [(openBlock(true), createElementBlock(Fragment, null, renderList(tabs.value, (item) => {
|
|
1546
|
+
return openBlock(), createElementBlock("button", {
|
|
1547
|
+
key: item.id,
|
|
1548
|
+
type: "button",
|
|
1549
|
+
class: normalizeClass(["pw-api__tab", { "is-active": activeTab.value === item.id }]),
|
|
1550
|
+
onClick: ($event) => tab.value = item.id
|
|
1551
|
+
}, [createTextVNode(toDisplayString(item.label) + " ", 1), createElementVNode("span", _hoisted_9$5, toDisplayString(item.count), 1)], 10, _hoisted_8$5);
|
|
1552
|
+
}), 128))])]), createElementVNode("div", _hoisted_10$4, [activeTab.value === "functions" ? (openBlock(), createElementBlock("table", _hoisted_11$4, [_cache[1] || (_cache[1] = createElementVNode("thead", null, [createElementVNode("tr", null, [
|
|
1553
|
+
createElementVNode("th", { class: "pw-api__col-name" }, "名称"),
|
|
1554
|
+
createElementVNode("th", null, "参数"),
|
|
1555
|
+
createElementVNode("th", { class: "pw-api__col-type" }, "返回"),
|
|
1556
|
+
createElementVNode("th", null, "说明"),
|
|
1557
|
+
createElementVNode("th", { class: "pw-api__col-demo" }, "场景")
|
|
1558
|
+
])], -1)), createElementVNode("tbody", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.module.functions, (fn) => {
|
|
1559
|
+
return openBlock(), createElementBlock("tr", {
|
|
1560
|
+
key: fn.name,
|
|
1561
|
+
"data-api": fn.name
|
|
1562
|
+
}, [
|
|
1563
|
+
createElementVNode("td", _hoisted_13$3, [createElementVNode("div", _hoisted_14$3, [createElementVNode("code", null, toDisplayString(fn.name), 1), createElementVNode("span", _hoisted_15$3, toDisplayString(fn.kind === "hook" ? "HOOK" : "FUNCTION"), 1)]), createElementVNode("div", _hoisted_16$3, toDisplayString(fn.signature), 1)]),
|
|
1564
|
+
createElementVNode("td", _hoisted_17$3, [(openBlock(true), createElementBlock(Fragment, null, renderList(fn.params, (param) => {
|
|
1565
|
+
return openBlock(), createElementBlock("div", {
|
|
1566
|
+
key: param.name,
|
|
1567
|
+
class: "pw-api__param"
|
|
1568
|
+
}, [
|
|
1569
|
+
createElementVNode("code", null, toDisplayString(param.name), 1),
|
|
1570
|
+
createElementVNode("span", _hoisted_18$2, toDisplayString(param.type) + toDisplayString(param.optional && !param.default ? "?" : ""), 1),
|
|
1571
|
+
param.default ? (openBlock(), createElementBlock("span", _hoisted_19$2, "= " + toDisplayString(param.default), 1)) : createCommentVNode("", true),
|
|
1572
|
+
param.description ? (openBlock(), createElementBlock("div", _hoisted_20$2, toDisplayString(param.description), 1)) : createCommentVNode("", true)
|
|
1573
|
+
]);
|
|
1574
|
+
}), 128)), !fn.params.length ? (openBlock(), createElementBlock("span", _hoisted_21$2, "无参数")) : createCommentVNode("", true)]),
|
|
1575
|
+
createElementVNode("td", null, [linkedType(fn.returns) ? (openBlock(), createElementBlock("button", {
|
|
1576
|
+
key: 0,
|
|
1577
|
+
type: "button",
|
|
1578
|
+
class: "pw-api__type is-link",
|
|
1579
|
+
title: `查看 ${linkedType(fn.returns)} 定义`,
|
|
1580
|
+
onClick: ($event) => openType(linkedType(fn.returns))
|
|
1581
|
+
}, toDisplayString(fn.returns), 9, _hoisted_22$2)) : fn.returns ? (openBlock(), createElementBlock("button", {
|
|
1582
|
+
key: 1,
|
|
1583
|
+
type: "button",
|
|
1584
|
+
class: "pw-api__type",
|
|
1585
|
+
title: `复制 ${fn.returns}`,
|
|
1586
|
+
onClick: ($event) => copyType(fn.returns)
|
|
1587
|
+
}, toDisplayString(fn.returns), 9, _hoisted_23$2)) : (openBlock(), createElementBlock("span", _hoisted_24$2, "—"))]),
|
|
1588
|
+
createElementVNode("td", _hoisted_25$1, [fn.description ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(fn.description), 1)], 64)) : (openBlock(), createElementBlock("span", _hoisted_26$1, "缺说明"))]),
|
|
1589
|
+
createElementVNode("td", _hoisted_27$1, [createElementVNode("div", _hoisted_28$1, [(openBlock(true), createElementBlock(Fragment, null, renderList(fn.coveredBy ?? [], (scene) => {
|
|
1590
|
+
return openBlock(), createElementBlock("button", {
|
|
1591
|
+
key: scene,
|
|
1592
|
+
type: "button",
|
|
1593
|
+
class: "pw-api__demo-link",
|
|
1594
|
+
onClick: ($event) => emit("jump", scene)
|
|
1595
|
+
}, toDisplayString(sceneTitle(scene)), 9, _hoisted_29$1);
|
|
1596
|
+
}), 128)), !(fn.coveredBy ?? []).length ? (openBlock(), createElementBlock("span", _hoisted_30$1, "无")) : createCommentVNode("", true)])])
|
|
1597
|
+
], 8, _hoisted_12$3);
|
|
1598
|
+
}), 128))])])) : activeTab.value === "constants" ? (openBlock(), createElementBlock("table", _hoisted_31$1, [_cache[2] || (_cache[2] = createElementVNode("thead", null, [createElementVNode("tr", null, [
|
|
1599
|
+
createElementVNode("th", { class: "pw-api__col-name" }, "名称"),
|
|
1600
|
+
createElementVNode("th", { class: "pw-api__col-type" }, "类型"),
|
|
1601
|
+
createElementVNode("th", { class: "pw-api__col-default" }, "值"),
|
|
1602
|
+
createElementVNode("th", null, "说明"),
|
|
1603
|
+
createElementVNode("th", { class: "pw-api__col-demo" }, "场景")
|
|
1604
|
+
])], -1)), createElementVNode("tbody", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.module.constants, (item) => {
|
|
1605
|
+
return openBlock(), createElementBlock("tr", {
|
|
1606
|
+
key: item.name,
|
|
1607
|
+
"data-api": item.name
|
|
1608
|
+
}, [
|
|
1609
|
+
createElementVNode("td", _hoisted_33$1, [createElementVNode("div", _hoisted_34$1, [createElementVNode("code", null, toDisplayString(item.name), 1)])]),
|
|
1610
|
+
createElementVNode("td", null, [linkedType(item.type) ? (openBlock(), createElementBlock("button", {
|
|
1611
|
+
key: 0,
|
|
1612
|
+
type: "button",
|
|
1613
|
+
class: "pw-api__type is-link",
|
|
1614
|
+
title: `查看 ${linkedType(item.type)} 定义`,
|
|
1615
|
+
onClick: ($event) => openType(linkedType(item.type))
|
|
1616
|
+
}, toDisplayString(item.type), 9, _hoisted_35$1)) : item.type ? (openBlock(), createElementBlock("button", {
|
|
1617
|
+
key: 1,
|
|
1618
|
+
type: "button",
|
|
1619
|
+
class: "pw-api__type",
|
|
1620
|
+
title: `复制 ${item.type}`,
|
|
1621
|
+
onClick: ($event) => copyType(item.type)
|
|
1622
|
+
}, toDisplayString(item.type), 9, _hoisted_36$1)) : (openBlock(), createElementBlock("span", _hoisted_37$1, "—"))]),
|
|
1623
|
+
createElementVNode("td", {
|
|
1624
|
+
class: "pw-api__default",
|
|
1625
|
+
title: item.value
|
|
1626
|
+
}, [item.value ? (openBlock(), createElementBlock("span", _hoisted_39$1, toDisplayString(item.value), 1)) : (openBlock(), createElementBlock("span", _hoisted_40, "—"))], 8, _hoisted_38$1),
|
|
1627
|
+
createElementVNode("td", _hoisted_41, [item.description ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(item.description), 1)], 64)) : (openBlock(), createElementBlock("span", _hoisted_42, "缺说明"))]),
|
|
1628
|
+
createElementVNode("td", _hoisted_43, [createElementVNode("div", _hoisted_44, [(openBlock(true), createElementBlock(Fragment, null, renderList(item.coveredBy ?? [], (scene) => {
|
|
1629
|
+
return openBlock(), createElementBlock("button", {
|
|
1630
|
+
key: scene,
|
|
1631
|
+
type: "button",
|
|
1632
|
+
class: "pw-api__demo-link",
|
|
1633
|
+
onClick: ($event) => emit("jump", scene)
|
|
1634
|
+
}, toDisplayString(sceneTitle(scene)), 9, _hoisted_45);
|
|
1635
|
+
}), 128)), !(item.coveredBy ?? []).length ? (openBlock(), createElementBlock("span", _hoisted_46, "无")) : createCommentVNode("", true)])])
|
|
1636
|
+
], 8, _hoisted_32$1);
|
|
1637
|
+
}), 128))])])) : (openBlock(), createElementBlock("div", _hoisted_47, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.module.types, (item) => {
|
|
1638
|
+
return openBlock(), createElementBlock("article", {
|
|
1639
|
+
key: item.name,
|
|
1640
|
+
class: normalizeClass(["pw-api__type-card", { "is-focus": item.name === focusedType.value }]),
|
|
1641
|
+
"data-type": item.name
|
|
1642
|
+
}, [createElementVNode("header", _hoisted_49, [
|
|
1643
|
+
createElementVNode("code", _hoisted_50, toDisplayString(item.name), 1),
|
|
1644
|
+
createElementVNode("span", _hoisted_51, toDisplayString(item.kind), 1),
|
|
1645
|
+
createElementVNode("span", {
|
|
1646
|
+
class: "pw-api__type-file",
|
|
1647
|
+
title: item.file
|
|
1648
|
+
}, toDisplayString(item.file), 9, _hoisted_52),
|
|
1649
|
+
createElementVNode("button", {
|
|
1650
|
+
type: "button",
|
|
1651
|
+
class: "pw-api__copy",
|
|
1652
|
+
title: `复制 ${item.name} 定义`,
|
|
1653
|
+
onClick: ($event) => copyType(item.code)
|
|
1654
|
+
}, [createVNode(unref(Copy), {
|
|
1655
|
+
size: 12,
|
|
1656
|
+
"stroke-width": 2
|
|
1657
|
+
}), _cache[3] || (_cache[3] = createTextVNode(" 复制 ", -1))], 8, _hoisted_53)
|
|
1658
|
+
]), createVNode(CodeBlock_default, {
|
|
1659
|
+
code: item.code,
|
|
1660
|
+
lang: "ts"
|
|
1661
|
+
}, null, 8, ["code"])], 10, _hoisted_48);
|
|
1662
|
+
}), 128))]))])], 512);
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
});
|
|
1666
|
+
// src/code/openInEditor.ts
|
|
1667
|
+
/** 内置编辑器协议;都是各编辑器官方注册的 URL scheme */
|
|
1668
|
+
var EDITOR_PRESETS = {
|
|
1669
|
+
vscode: {
|
|
1670
|
+
label: "VS Code",
|
|
1671
|
+
template: "vscode://file/{file}:{line}"
|
|
1672
|
+
},
|
|
1673
|
+
cursor: {
|
|
1674
|
+
label: "Cursor",
|
|
1675
|
+
template: "cursor://file/{file}:{line}"
|
|
1676
|
+
},
|
|
1677
|
+
windsurf: {
|
|
1678
|
+
label: "Windsurf",
|
|
1679
|
+
template: "windsurf://file/{file}:{line}"
|
|
1680
|
+
},
|
|
1681
|
+
trae: {
|
|
1682
|
+
label: "Trae",
|
|
1683
|
+
template: "trae://file/{file}:{line}"
|
|
1684
|
+
},
|
|
1685
|
+
zed: {
|
|
1686
|
+
label: "Zed",
|
|
1687
|
+
template: "zed://file/{file}:{line}"
|
|
1688
|
+
},
|
|
1689
|
+
webstorm: {
|
|
1690
|
+
label: "WebStorm",
|
|
1691
|
+
template: "webstorm://open?file={fileEnc}&line={line}"
|
|
1692
|
+
},
|
|
1693
|
+
idea: {
|
|
1694
|
+
label: "IntelliJ IDEA",
|
|
1695
|
+
template: "idea://open?file={fileEnc}&line={line}"
|
|
1696
|
+
},
|
|
1697
|
+
phpstorm: {
|
|
1698
|
+
label: "PhpStorm",
|
|
1699
|
+
template: "phpstorm://open?file={fileEnc}&line={line}"
|
|
1700
|
+
},
|
|
1701
|
+
sublime: {
|
|
1702
|
+
label: "Sublime Text",
|
|
1703
|
+
template: "subl://open?url=file://{fileEnc}&line={line}"
|
|
1704
|
+
},
|
|
1705
|
+
nova: {
|
|
1706
|
+
label: "Nova",
|
|
1707
|
+
template: "nova://open?path={fileEnc}"
|
|
1708
|
+
}
|
|
1709
|
+
};
|
|
1710
|
+
var DEFAULT_OPTIONS = [
|
|
1711
|
+
"vscode",
|
|
1712
|
+
"cursor",
|
|
1713
|
+
"windsurf",
|
|
1714
|
+
"trae",
|
|
1715
|
+
"zed",
|
|
1716
|
+
"webstorm",
|
|
1717
|
+
"idea",
|
|
1718
|
+
"sublime"
|
|
1719
|
+
];
|
|
1720
|
+
var STORAGE_KEY$1 = "rinova-playground:editor";
|
|
1721
|
+
var editorConfig = () => config.editor ?? {};
|
|
1722
|
+
var readStored = () => {
|
|
1723
|
+
try {
|
|
1724
|
+
const raw = localStorage.getItem(STORAGE_KEY$1);
|
|
1725
|
+
if (!raw) return null;
|
|
1726
|
+
if (!raw.startsWith("{")) return { key: raw };
|
|
1727
|
+
const parsed = JSON.parse(raw);
|
|
1728
|
+
return typeof parsed?.key === "string" ? parsed : null;
|
|
1729
|
+
} catch {
|
|
1730
|
+
return null;
|
|
1731
|
+
}
|
|
1732
|
+
};
|
|
1733
|
+
var fallbackChoice = () => {
|
|
1734
|
+
const editor = editorConfig();
|
|
1735
|
+
if (editor.preset === "custom" && editor.custom) return {
|
|
1736
|
+
key: "custom",
|
|
1737
|
+
template: editor.custom
|
|
1738
|
+
};
|
|
1739
|
+
if (editor.preset) return { key: editor.preset };
|
|
1740
|
+
return { key: DEFAULT_OPTIONS[0] };
|
|
1741
|
+
};
|
|
1742
|
+
var choice = ref(readStored() ?? fallbackChoice());
|
|
1743
|
+
/** 当前协议模板:内置 preset 的模板,或访问者自定义的模板 */
|
|
1744
|
+
var editorTemplate = () => {
|
|
1745
|
+
if (choice.value.key === "custom") return choice.value.template ?? editorConfig().custom ?? "";
|
|
1746
|
+
return EDITOR_PRESETS[choice.value.key]?.template ?? "";
|
|
1747
|
+
};
|
|
1748
|
+
var editorLabel = () => {
|
|
1749
|
+
if (choice.value.key === "custom") return editorTemplate().split("://")[0] || "自定义";
|
|
1750
|
+
return EDITOR_PRESETS[choice.value.key]?.label ?? choice.value.key;
|
|
1751
|
+
};
|
|
1752
|
+
var editorChoiceKey = computed(() => choice.value.key);
|
|
1753
|
+
/** 菜单里的候选:配置限定 → 内置;配置指定的 preset 始终在列表里 */
|
|
1754
|
+
var editorOptions = () => {
|
|
1755
|
+
const editor = editorConfig();
|
|
1756
|
+
const list = (editor.options?.length ? editor.options : DEFAULT_OPTIONS).filter((value) => value === "custom" ? Boolean(editor.custom) : Boolean(EDITOR_PRESETS[value])).map((value) => ({
|
|
1757
|
+
value,
|
|
1758
|
+
label: EDITOR_PRESETS[value]?.label ?? "自定义"
|
|
1759
|
+
}));
|
|
1760
|
+
const preset = editor.preset;
|
|
1761
|
+
if (preset && !list.some((item) => item.value === preset)) list.unshift({
|
|
1762
|
+
value: preset,
|
|
1763
|
+
label: preset === "custom" ? "自定义" : EDITOR_PRESETS[preset]?.label ?? preset
|
|
1764
|
+
});
|
|
1765
|
+
return list;
|
|
1766
|
+
};
|
|
1767
|
+
/** 是否允许访问者填自定义模板(任意 IDE 都能接) */
|
|
1768
|
+
var allowCustomEditor = () => editorConfig().allowCustom !== false;
|
|
1769
|
+
/** 访问者能否自己选:配置显式关闭,或只有一个候选且不允许自定义时隐藏菜单 */
|
|
1770
|
+
var canChooseEditor = () => editorConfig().allowChoice !== false && (editorOptions().length > 1 || allowCustomEditor());
|
|
1771
|
+
var setEditorChoice = (next) => {
|
|
1772
|
+
choice.value = next;
|
|
1773
|
+
try {
|
|
1774
|
+
localStorage.setItem(STORAGE_KEY$1, JSON.stringify(next));
|
|
1775
|
+
} catch {}
|
|
1776
|
+
};
|
|
1777
|
+
var projectRoot = () => String(config.root ?? "");
|
|
1778
|
+
var editorEnabled = () => Boolean(projectRoot()) && Boolean(editorTemplate());
|
|
1779
|
+
var fill = (template, abs, line) => template.replace(/\{fileEnc\}/g, encodeURIComponent(abs)).replace(/\{file\}/g, abs).replace(/\{line\}/g, String(line));
|
|
1780
|
+
/** 拼出「在 IDE 中打开」的 URL;拿不到绝对路径时返回空串 */
|
|
1781
|
+
var editorUrl = (rel, line = 1) => {
|
|
1782
|
+
const root = projectRoot();
|
|
1783
|
+
const template = editorTemplate();
|
|
1784
|
+
if (!root || !template) return "";
|
|
1785
|
+
return fill(template, `${root.replace(/\/+$/, "")}/${rel.replace(/^\.?\//, "")}`, line);
|
|
1786
|
+
};
|
|
1787
|
+
/** 打开本地 IDE;没有可用 URL 时降级为复制绝对路径 */
|
|
1788
|
+
var openInEditor = (rel, line = 1) => {
|
|
1789
|
+
const url = editorUrl(rel, line);
|
|
1790
|
+
if (!url) {
|
|
1791
|
+
toast("未配置本地编辑器(playground.config 的 editor.preset)");
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
try {
|
|
1795
|
+
const anchor = document.createElement("a");
|
|
1796
|
+
anchor.href = url;
|
|
1797
|
+
anchor.rel = "noopener";
|
|
1798
|
+
anchor.style.display = "none";
|
|
1799
|
+
document.body.appendChild(anchor);
|
|
1800
|
+
anchor.click();
|
|
1801
|
+
window.setTimeout(() => anchor.remove(), 0);
|
|
1802
|
+
} catch {
|
|
1803
|
+
toast("打开本地编辑器失败");
|
|
1804
|
+
}
|
|
1805
|
+
};
|
|
1806
|
+
/** 复制文件路径:`line > 0` 时带「:行」,不传就是纯路径 */
|
|
1807
|
+
var copyPath = async (rel, line = 0) => {
|
|
1808
|
+
const root = projectRoot();
|
|
1809
|
+
const abs = root ? `${root.replace(/\/+$/, "")}/${rel.replace(/^\.?\//, "")}` : rel;
|
|
1810
|
+
const text = line > 0 ? `${abs}:${line}` : abs;
|
|
1811
|
+
const ok = await copyText(text);
|
|
1812
|
+
toast(ok ? `已复制 ${text}` : "复制失败,请手动复制路径");
|
|
1813
|
+
};
|
|
1814
|
+
// src/app/PlaygroundEditorPicker.vue?vue&type=script&setup=true&lang.ts
|
|
1815
|
+
var _hoisted_1$13 = [
|
|
1816
|
+
"disabled",
|
|
1817
|
+
"data-tip",
|
|
1818
|
+
"aria-label"
|
|
1819
|
+
];
|
|
1820
|
+
var _hoisted_2$11 = {
|
|
1821
|
+
key: 0,
|
|
1822
|
+
class: "pw-picker__menu",
|
|
1823
|
+
role: "menu"
|
|
1824
|
+
};
|
|
1825
|
+
var _hoisted_3$11 = ["onClick"];
|
|
1826
|
+
var _hoisted_4$10 = { class: "pw-picker__actions" };
|
|
1827
|
+
var _hoisted_5$9 = ["disabled"];
|
|
1828
|
+
// src/app/PlaygroundEditorPicker.vue
|
|
1829
|
+
var PlaygroundEditorPicker_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
1830
|
+
__name: "PlaygroundEditorPicker",
|
|
1831
|
+
setup(__props) {
|
|
1832
|
+
const open = ref(false);
|
|
1833
|
+
const customMode = ref(false);
|
|
1834
|
+
const draft = ref("");
|
|
1835
|
+
const rootEl = ref(null);
|
|
1836
|
+
const draftEl = ref(null);
|
|
1837
|
+
const options = computed(() => editorOptions());
|
|
1838
|
+
const choosable = computed(() => canChooseEditor());
|
|
1839
|
+
const customAllowed = computed(() => allowCustomEditor());
|
|
1840
|
+
/** 没有 {file} 占位符的模板保存了也跳不过去,直接按住保存按钮并把提示换成原因 */
|
|
1841
|
+
const draftValid = computed(() => draft.value.includes("{file}"));
|
|
1842
|
+
const toggle = () => {
|
|
1843
|
+
if (!choosable.value) return;
|
|
1844
|
+
open.value = !open.value;
|
|
1845
|
+
if (!open.value) customMode.value = false;
|
|
1846
|
+
};
|
|
1847
|
+
const pick = (value) => {
|
|
1848
|
+
setEditorChoice({ key: value });
|
|
1849
|
+
open.value = false;
|
|
1850
|
+
customMode.value = false;
|
|
1851
|
+
};
|
|
1852
|
+
const startCustom = () => {
|
|
1853
|
+
customMode.value = true;
|
|
1854
|
+
draft.value = editorTemplate() || "myeditor://file/{file}:{line}";
|
|
1855
|
+
nextTick(() => {
|
|
1856
|
+
draftEl.value?.focus();
|
|
1857
|
+
draftEl.value?.select();
|
|
1858
|
+
});
|
|
1859
|
+
};
|
|
1860
|
+
const saveCustom = () => {
|
|
1861
|
+
const template = draft.value.trim();
|
|
1862
|
+
if (!template.includes("{file}")) return;
|
|
1863
|
+
setEditorChoice({
|
|
1864
|
+
key: "custom",
|
|
1865
|
+
template
|
|
1866
|
+
});
|
|
1867
|
+
open.value = false;
|
|
1868
|
+
customMode.value = false;
|
|
1869
|
+
};
|
|
1870
|
+
const close = () => {
|
|
1871
|
+
open.value = false;
|
|
1872
|
+
customMode.value = false;
|
|
1873
|
+
};
|
|
1874
|
+
/**
|
|
1875
|
+
* 点外面关菜单。必须用 pointerdown + 捕获阶段:
|
|
1876
|
+
* 用 click 时,菜单里的项在 click 处理中把 customMode 改掉,Vue 更新把该按钮移出文档,
|
|
1877
|
+
* 等事件冒泡到 document 时 target 已经 isConnected=false,contains() 返回 false,
|
|
1878
|
+
* 于是「点自定义协议」被当成点了外面,菜单直接关掉(Bug 现场)。
|
|
1879
|
+
*/
|
|
1880
|
+
const onDocPointerDown = (event) => {
|
|
1881
|
+
const target = event.target;
|
|
1882
|
+
if (!target || !rootEl.value?.contains(target)) close();
|
|
1883
|
+
};
|
|
1884
|
+
onMounted(() => document.addEventListener("pointerdown", onDocPointerDown, true));
|
|
1885
|
+
onBeforeUnmount(() => document.removeEventListener("pointerdown", onDocPointerDown, true));
|
|
1886
|
+
return (_ctx, _cache) => {
|
|
1887
|
+
return openBlock(), createElementBlock("div", {
|
|
1888
|
+
ref_key: "rootEl",
|
|
1889
|
+
ref: rootEl,
|
|
1890
|
+
class: "pw-picker"
|
|
1891
|
+
}, [createElementVNode("button", {
|
|
1892
|
+
type: "button",
|
|
1893
|
+
class: normalizeClass(["pw-picker__btn", { "is-active": open.value }]),
|
|
1894
|
+
disabled: !choosable.value,
|
|
1895
|
+
"data-tip": choosable.value ? `本地编辑器:${unref(editorLabel)()}(点这里改)` : `本地编辑器:${unref(editorLabel)()}`,
|
|
1896
|
+
"aria-label": `本地编辑器:${unref(editorLabel)()}`,
|
|
1897
|
+
onClick: toggle
|
|
1898
|
+
}, [createVNode(unref(SquareArrowOutUpRight), {
|
|
1899
|
+
size: 14,
|
|
1900
|
+
"stroke-width": 2
|
|
1901
|
+
})], 10, _hoisted_1$13), open.value ? (openBlock(), createElementBlock("div", _hoisted_2$11, [!customMode.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
1902
|
+
_cache[6] || (_cache[6] = createElementVNode("div", { class: "pw-picker__menu-label" }, "本地编辑器", -1)),
|
|
1903
|
+
unref(editorChoiceKey) === "custom" ? (openBlock(), createElementBlock("button", {
|
|
1904
|
+
key: 0,
|
|
1905
|
+
type: "button",
|
|
1906
|
+
class: "pw-picker__item",
|
|
1907
|
+
role: "menuitem",
|
|
1908
|
+
onClick: _cache[0] || (_cache[0] = ($event) => open.value = false)
|
|
1909
|
+
}, [createVNode(unref(Check), { size: 12 }), createElementVNode("span", null, "自定义 " + toDisplayString(unref(editorLabel)()), 1)])) : createCommentVNode("", true),
|
|
1910
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(options.value, (option) => {
|
|
1911
|
+
return openBlock(), createElementBlock("button", {
|
|
1912
|
+
key: option.value,
|
|
1913
|
+
type: "button",
|
|
1914
|
+
class: "pw-picker__item",
|
|
1915
|
+
role: "menuitem",
|
|
1916
|
+
onClick: ($event) => pick(option.value)
|
|
1917
|
+
}, [createVNode(unref(Check), {
|
|
1918
|
+
size: 12,
|
|
1919
|
+
class: normalizeClass({ "is-hidden": option.value !== unref(editorChoiceKey) })
|
|
1920
|
+
}, null, 8, ["class"]), createElementVNode("span", null, toDisplayString(option.label), 1)], 8, _hoisted_3$11);
|
|
1921
|
+
}), 128)),
|
|
1922
|
+
customAllowed.value ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [_cache[5] || (_cache[5] = createElementVNode("div", { class: "pw-picker__sep" }, null, -1)), createElementVNode("button", {
|
|
1923
|
+
type: "button",
|
|
1924
|
+
class: "pw-picker__item",
|
|
1925
|
+
role: "menuitem",
|
|
1926
|
+
onClick: startCustom
|
|
1927
|
+
}, [createVNode(unref(PencilLine), { size: 12 }), _cache[4] || (_cache[4] = createElementVNode("span", null, "自定义协议…", -1))])], 64)) : createCommentVNode("", true)
|
|
1928
|
+
], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
1929
|
+
_cache[17] || (_cache[17] = createElementVNode("div", { class: "pw-picker__menu-label" }, "自定义协议模板", -1)),
|
|
1930
|
+
withDirectives(createElementVNode("input", {
|
|
1931
|
+
ref_key: "draftEl",
|
|
1932
|
+
ref: draftEl,
|
|
1933
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => draft.value = $event),
|
|
1934
|
+
class: "pw-picker__input",
|
|
1935
|
+
placeholder: "myeditor://file/{file}:{line}",
|
|
1936
|
+
spellcheck: "false",
|
|
1937
|
+
onKeydown: [withKeys(saveCustom, ["enter"]), _cache[2] || (_cache[2] = withKeys(($event) => customMode.value = false, ["esc"]))]
|
|
1938
|
+
}, null, 544), [[vModelText, draft.value]]),
|
|
1939
|
+
createElementVNode("p", { class: normalizeClass(["pw-picker__hint", { "is-invalid": !draftValid.value }]) }, [draftValid.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
1940
|
+
_cache[7] || (_cache[7] = createTextVNode(" 占位符:", -1)),
|
|
1941
|
+
_cache[8] || (_cache[8] = createElementVNode("code", null, "{file}", -1)),
|
|
1942
|
+
_cache[9] || (_cache[9] = createTextVNode(" 绝对路径、", -1)),
|
|
1943
|
+
_cache[10] || (_cache[10] = createElementVNode("code", null, "{fileEnc}", -1)),
|
|
1944
|
+
_cache[11] || (_cache[11] = createTextVNode(" URL 编码、", -1)),
|
|
1945
|
+
_cache[12] || (_cache[12] = createElementVNode("code", null, "{line}", -1)),
|
|
1946
|
+
_cache[13] || (_cache[13] = createTextVNode(" 行号 ", -1))
|
|
1947
|
+
], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
1948
|
+
_cache[14] || (_cache[14] = createTextVNode("模板里必须有 ", -1)),
|
|
1949
|
+
_cache[15] || (_cache[15] = createElementVNode("code", null, "{file}", -1)),
|
|
1950
|
+
_cache[16] || (_cache[16] = createTextVNode(" 占位符,否则跳不过去", -1))
|
|
1951
|
+
], 64))], 2),
|
|
1952
|
+
createElementVNode("div", _hoisted_4$10, [createElementVNode("button", {
|
|
1953
|
+
type: "button",
|
|
1954
|
+
class: "pw-picker__action",
|
|
1955
|
+
onClick: _cache[3] || (_cache[3] = ($event) => customMode.value = false)
|
|
1956
|
+
}, "取消"), createElementVNode("button", {
|
|
1957
|
+
type: "button",
|
|
1958
|
+
class: "pw-picker__action pw-picker__action--primary",
|
|
1959
|
+
disabled: !draftValid.value,
|
|
1960
|
+
onClick: saveCustom
|
|
1961
|
+
}, " 保存 ", 8, _hoisted_5$9)])
|
|
1962
|
+
], 64))])) : createCommentVNode("", true)], 512);
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1965
|
+
}), [["__scopeId", "data-v-d6404e2a"]]);
|
|
1966
|
+
// src/app/PlaygroundPageIndex.vue?vue&type=script&setup=true&lang.ts
|
|
1967
|
+
var _hoisted_1$12 = {
|
|
1968
|
+
class: "pw-index",
|
|
1969
|
+
"aria-label": "场景索引"
|
|
1970
|
+
};
|
|
1971
|
+
var _hoisted_2$10 = { class: "pw-index__inner" };
|
|
1972
|
+
var _hoisted_3$10 = { class: "pw-index__label" };
|
|
1973
|
+
var _hoisted_4$9 = ["href", "onClick"];
|
|
1974
|
+
// src/app/PlaygroundPageIndex.vue
|
|
1975
|
+
var PlaygroundPageIndex_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
1976
|
+
__name: "PlaygroundPageIndex",
|
|
1977
|
+
props: {
|
|
1978
|
+
scenes: {},
|
|
1979
|
+
active: {}
|
|
1980
|
+
},
|
|
1981
|
+
emits: ["jump"],
|
|
1982
|
+
setup(__props, { emit: __emit }) {
|
|
1983
|
+
const props = __props;
|
|
1984
|
+
const emit = __emit;
|
|
1985
|
+
const SECTION_LABELS = [
|
|
1986
|
+
{
|
|
1987
|
+
key: "overview",
|
|
1988
|
+
label: "概要"
|
|
1989
|
+
},
|
|
1990
|
+
{
|
|
1991
|
+
key: "props",
|
|
1992
|
+
label: "Props"
|
|
1993
|
+
},
|
|
1994
|
+
{
|
|
1995
|
+
key: "events",
|
|
1996
|
+
label: "Events"
|
|
1997
|
+
},
|
|
1998
|
+
{
|
|
1999
|
+
key: "exposes",
|
|
2000
|
+
label: "Exposes"
|
|
2001
|
+
},
|
|
2002
|
+
{
|
|
2003
|
+
key: "slots",
|
|
2004
|
+
label: "Slots"
|
|
2005
|
+
}
|
|
2006
|
+
];
|
|
2007
|
+
const groups = computed(() => SECTION_LABELS.map((section) => ({
|
|
2008
|
+
...section,
|
|
2009
|
+
items: props.scenes.filter((scene) => !scene.hidden && scene.section === section.key)
|
|
2010
|
+
})).filter((group) => group.items.length > 0));
|
|
2011
|
+
return (_ctx, _cache) => {
|
|
2012
|
+
return openBlock(), createElementBlock("aside", _hoisted_1$12, [createElementVNode("nav", _hoisted_2$10, [(openBlock(true), createElementBlock(Fragment, null, renderList(groups.value, (group) => {
|
|
2013
|
+
return openBlock(), createElementBlock("div", {
|
|
2014
|
+
key: group.key,
|
|
2015
|
+
class: "pw-index__group"
|
|
2016
|
+
}, [createElementVNode("div", _hoisted_3$10, toDisplayString(group.label), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(group.items, (scene) => {
|
|
2017
|
+
return openBlock(), createElementBlock("a", {
|
|
2018
|
+
key: scene.id,
|
|
2019
|
+
href: `#${scene.name}`,
|
|
2020
|
+
class: normalizeClass(["pw-index__link", { "is-active": scene.name === __props.active }]),
|
|
2021
|
+
onClick: withModifiers(($event) => emit("jump", scene.name), ["prevent"])
|
|
2022
|
+
}, toDisplayString(scene.title), 11, _hoisted_4$9);
|
|
2023
|
+
}), 128))]);
|
|
2024
|
+
}), 128))])]);
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
}), [["__scopeId", "data-v-b676ec15"]]);
|
|
2028
|
+
// src/components/PwSceneBoundary.vue?vue&type=script&setup=true&lang.ts
|
|
2029
|
+
var _hoisted_1$11 = {
|
|
2030
|
+
key: 0,
|
|
2031
|
+
class: "pw-boundary"
|
|
2032
|
+
};
|
|
2033
|
+
var _hoisted_2$9 = {
|
|
2034
|
+
key: 1,
|
|
2035
|
+
class: "pw-boundary pw-boundary--error",
|
|
2036
|
+
role: "alert"
|
|
2037
|
+
};
|
|
2038
|
+
var _hoisted_3$9 = { class: "pw-boundary__head" };
|
|
2039
|
+
var _hoisted_4$8 = { class: "pw-boundary__message" };
|
|
2040
|
+
var _hoisted_5$8 = {
|
|
2041
|
+
key: 0,
|
|
2042
|
+
class: "pw-boundary__file"
|
|
2043
|
+
};
|
|
2044
|
+
var _hoisted_6$7 = {
|
|
2045
|
+
key: 1,
|
|
2046
|
+
class: "pw-boundary__stack"
|
|
2047
|
+
};
|
|
2048
|
+
// src/components/PwSceneBoundary.vue
|
|
2049
|
+
var PwSceneBoundary_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
2050
|
+
__name: "PwSceneBoundary",
|
|
2051
|
+
props: {
|
|
2052
|
+
title: {},
|
|
2053
|
+
file: {}
|
|
2054
|
+
},
|
|
2055
|
+
setup(__props) {
|
|
2056
|
+
const props = __props;
|
|
2057
|
+
const message = ref("");
|
|
2058
|
+
const stack = ref("");
|
|
2059
|
+
onErrorCaptured((error) => {
|
|
2060
|
+
message.value = error instanceof Error ? error.message : String(error);
|
|
2061
|
+
stack.value = error instanceof Error ? error.stack ?? "" : "";
|
|
2062
|
+
return false;
|
|
2063
|
+
});
|
|
2064
|
+
return (_ctx, _cache) => {
|
|
2065
|
+
return !message.value ? (openBlock(), createElementBlock("div", _hoisted_1$11, [renderSlot(_ctx.$slots, "default", {}, void 0, true)])) : (openBlock(), createElementBlock("div", _hoisted_2$9, [
|
|
2066
|
+
createElementVNode("p", _hoisted_3$9, "场景渲染失败" + toDisplayString(props.title ? `:${props.title}` : ""), 1),
|
|
2067
|
+
createElementVNode("p", _hoisted_4$8, toDisplayString(message.value), 1),
|
|
2068
|
+
props.file ? (openBlock(), createElementBlock("p", _hoisted_5$8, toDisplayString(props.file), 1)) : createCommentVNode("", true),
|
|
2069
|
+
stack.value ? (openBlock(), createElementBlock("details", _hoisted_6$7, [_cache[0] || (_cache[0] = createElementVNode("summary", null, "调用栈", -1)), createElementVNode("pre", null, toDisplayString(stack.value), 1)])) : createCommentVNode("", true)
|
|
2070
|
+
]));
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
2073
|
+
}), [["__scopeId", "data-v-2b6db55b"]]);
|
|
2074
|
+
// src/app/PlaygroundPluginSlot.vue?vue&type=script&setup=true&lang.ts
|
|
2075
|
+
var _hoisted_1$10 = ["data-pw-plugin-slot"];
|
|
2076
|
+
// src/app/PlaygroundPluginSlot.vue
|
|
2077
|
+
var PlaygroundPluginSlot_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
2078
|
+
__name: "PlaygroundPluginSlot",
|
|
2079
|
+
props: {
|
|
2080
|
+
entries: {},
|
|
2081
|
+
slot: {}
|
|
2082
|
+
},
|
|
2083
|
+
setup(__props) {
|
|
2084
|
+
return (_ctx, _cache) => {
|
|
2085
|
+
return __props.entries.length ? (openBlock(), createElementBlock("div", {
|
|
2086
|
+
key: 0,
|
|
2087
|
+
class: normalizeClass(["pw-plugin-slot", `pw-plugin-slot--${__props.slot}`]),
|
|
2088
|
+
"data-pw-plugin-slot": __props.slot
|
|
2089
|
+
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.entries, (entry) => {
|
|
2090
|
+
return openBlock(), createBlock(PwSceneBoundary_default, {
|
|
2091
|
+
key: entry.key,
|
|
2092
|
+
title: `插件 UI(${entry.plugin})`
|
|
2093
|
+
}, {
|
|
2094
|
+
default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(entry.component), mergeProps({ ref_for: true }, entry.props ?? {}), null, 16))]),
|
|
2095
|
+
_: 2
|
|
2096
|
+
}, 1032, ["title"]);
|
|
2097
|
+
}), 128))], 10, _hoisted_1$10)) : createCommentVNode("", true);
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
}), [["__scopeId", "data-v-e9b00509"]]);
|
|
2101
|
+
// src/components/PwSwitch.vue?vue&type=script&setup=true&lang.ts
|
|
2102
|
+
var _hoisted_1$9 = ["aria-checked", "disabled"];
|
|
2103
|
+
// src/components/PwSwitch.vue
|
|
2104
|
+
var PwSwitch_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
2105
|
+
__name: "PwSwitch",
|
|
2106
|
+
props: {
|
|
2107
|
+
modelValue: { type: Boolean },
|
|
2108
|
+
disabled: { type: Boolean }
|
|
2109
|
+
},
|
|
2110
|
+
emits: ["update:modelValue"],
|
|
2111
|
+
setup(__props, { emit: __emit }) {
|
|
2112
|
+
const emit = __emit;
|
|
2113
|
+
return (_ctx, _cache) => {
|
|
2114
|
+
return openBlock(), createElementBlock("button", {
|
|
2115
|
+
type: "button",
|
|
2116
|
+
role: "switch",
|
|
2117
|
+
class: normalizeClass(["pw-switch", { "is-on": __props.modelValue }]),
|
|
2118
|
+
"aria-checked": __props.modelValue,
|
|
2119
|
+
disabled: __props.disabled,
|
|
2120
|
+
onClick: _cache[0] || (_cache[0] = ($event) => emit("update:modelValue", !__props.modelValue))
|
|
2121
|
+
}, [..._cache[1] || (_cache[1] = [createElementVNode("span", { class: "pw-switch__thumb" }, null, -1)])], 10, _hoisted_1$9);
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
}), [["__scopeId", "data-v-64fffbcb"]]);
|
|
2125
|
+
// src/app/PlaygroundSceneArgs.vue?vue&type=script&setup=true&lang.ts
|
|
2126
|
+
var _hoisted_1$8 = {
|
|
2127
|
+
key: 0,
|
|
2128
|
+
class: "pw-args"
|
|
2129
|
+
};
|
|
2130
|
+
var _hoisted_2$8 = { class: "pw-args__head" };
|
|
2131
|
+
var _hoisted_3$8 = {
|
|
2132
|
+
key: 0,
|
|
2133
|
+
class: "pw-args__slot-head"
|
|
2134
|
+
};
|
|
2135
|
+
var _hoisted_4$7 = { class: "pw-args__slot-name" };
|
|
2136
|
+
var _hoisted_5$7 = ["onClick"];
|
|
2137
|
+
var _hoisted_6$6 = { class: "pw-args__grid" };
|
|
2138
|
+
var _hoisted_7$5 = ["title"];
|
|
2139
|
+
var _hoisted_8$4 = ["value", "onChange"];
|
|
2140
|
+
var _hoisted_9$4 = ["value"];
|
|
2141
|
+
// src/app/PlaygroundSceneArgs.vue
|
|
2142
|
+
var PlaygroundSceneArgs_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
2143
|
+
__name: "PlaygroundSceneArgs",
|
|
2144
|
+
props: { sceneName: {} },
|
|
2145
|
+
setup(__props) {
|
|
2146
|
+
const props = __props;
|
|
2147
|
+
/** 只显示本场景声明的输入槽位 */
|
|
2148
|
+
const slots = computed(() => currentArgsSlots.value.filter((slot) => !slot.sceneId || slot.sceneId === props.sceneName));
|
|
2149
|
+
const multiSlot = computed(() => slots.value.length > 1);
|
|
2150
|
+
const fieldsOf = (binding) => Object.entries(binding.values).map(([key, value]) => ({
|
|
2151
|
+
key,
|
|
2152
|
+
value,
|
|
2153
|
+
label: binding.options.labels?.[key] ?? key,
|
|
2154
|
+
kind: typeof value === "boolean" ? "boolean" : typeof value === "number" ? "number" : "string",
|
|
2155
|
+
options: binding.options.options?.[key]
|
|
2156
|
+
}));
|
|
2157
|
+
const setValue = (binding, key, value) => {
|
|
2158
|
+
binding.values[key] = value;
|
|
2159
|
+
};
|
|
2160
|
+
const setString = (binding, key, raw) => setValue(binding, key, raw);
|
|
2161
|
+
const setNumber = (binding, key, raw) => {
|
|
2162
|
+
const parsed = Number(raw);
|
|
2163
|
+
setValue(binding, key, Number.isFinite(parsed) ? parsed : 0);
|
|
2164
|
+
};
|
|
2165
|
+
const resetSlot = (binding) => {
|
|
2166
|
+
for (const [key, value] of Object.entries(binding.defaults)) binding.values[key] = value;
|
|
2167
|
+
};
|
|
2168
|
+
return (_ctx, _cache) => {
|
|
2169
|
+
return slots.value.length ? (openBlock(), createElementBlock("div", _hoisted_1$8, [createElementVNode("header", _hoisted_2$8, [_cache[1] || (_cache[1] = createElementVNode("span", { class: "pw-args__title" }, "输入", -1)), !multiSlot.value && slots.value[0] ? (openBlock(), createElementBlock("button", {
|
|
2170
|
+
key: 0,
|
|
2171
|
+
type: "button",
|
|
2172
|
+
class: "pw-args__reset",
|
|
2173
|
+
"data-tip": "恢复默认值",
|
|
2174
|
+
"aria-label": "恢复默认值",
|
|
2175
|
+
onClick: _cache[0] || (_cache[0] = ($event) => resetSlot(slots.value[0]))
|
|
2176
|
+
}, [createVNode(unref(RotateCcw), { size: 12 })])) : createCommentVNode("", true)]), (openBlock(true), createElementBlock(Fragment, null, renderList(slots.value, (binding) => {
|
|
2177
|
+
return openBlock(), createElementBlock("section", {
|
|
2178
|
+
key: `${binding.sceneId}:${binding.key}`,
|
|
2179
|
+
class: "pw-args__slot"
|
|
2180
|
+
}, [multiSlot.value ? (openBlock(), createElementBlock("header", _hoisted_3$8, [createElementVNode("span", _hoisted_4$7, toDisplayString(binding.label || binding.key), 1), createElementVNode("button", {
|
|
2181
|
+
type: "button",
|
|
2182
|
+
class: "pw-args__reset",
|
|
2183
|
+
"data-tip": "只恢复这一块",
|
|
2184
|
+
"aria-label": "只恢复这一块",
|
|
2185
|
+
onClick: ($event) => resetSlot(binding)
|
|
2186
|
+
}, [createVNode(unref(RotateCcw), { size: 12 })], 8, _hoisted_5$7)])) : createCommentVNode("", true), createElementVNode("div", _hoisted_6$6, [(openBlock(true), createElementBlock(Fragment, null, renderList(fieldsOf(binding), (field) => {
|
|
2187
|
+
return openBlock(), createElementBlock("div", {
|
|
2188
|
+
key: field.key,
|
|
2189
|
+
class: "pw-args__field"
|
|
2190
|
+
}, [createElementVNode("label", {
|
|
2191
|
+
class: "pw-args__label",
|
|
2192
|
+
title: field.key
|
|
2193
|
+
}, toDisplayString(field.label), 9, _hoisted_7$5), field.kind === "boolean" ? (openBlock(), createBlock(PwSwitch_default, {
|
|
2194
|
+
key: 0,
|
|
2195
|
+
"model-value": field.value === true,
|
|
2196
|
+
"onUpdate:modelValue": ($event) => setValue(binding, field.key, $event)
|
|
2197
|
+
}, null, 8, ["model-value", "onUpdate:modelValue"])) : field.options ? (openBlock(), createElementBlock("select", {
|
|
2198
|
+
key: 1,
|
|
2199
|
+
class: "pw-args__select",
|
|
2200
|
+
value: String(field.value),
|
|
2201
|
+
onChange: ($event) => setString(binding, field.key, $event.target.value)
|
|
2202
|
+
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(field.options, (option) => {
|
|
2203
|
+
return openBlock(), createElementBlock("option", {
|
|
2204
|
+
key: String(option),
|
|
2205
|
+
value: String(option)
|
|
2206
|
+
}, toDisplayString(option), 9, _hoisted_9$4);
|
|
2207
|
+
}), 128))], 40, _hoisted_8$4)) : (openBlock(), createBlock(PwInput_default, {
|
|
2208
|
+
key: 2,
|
|
2209
|
+
size: "sm",
|
|
2210
|
+
type: field.kind === "number" ? "number" : "text",
|
|
2211
|
+
"model-value": String(field.value ?? ""),
|
|
2212
|
+
"onUpdate:modelValue": ($event) => field.kind === "number" ? setNumber(binding, field.key, $event) : setString(binding, field.key, $event)
|
|
2213
|
+
}, null, 8, [
|
|
2214
|
+
"type",
|
|
2215
|
+
"model-value",
|
|
2216
|
+
"onUpdate:modelValue"
|
|
2217
|
+
]))]);
|
|
2218
|
+
}), 128))])]);
|
|
2219
|
+
}), 128))])) : createCommentVNode("", true);
|
|
2220
|
+
};
|
|
2221
|
+
}
|
|
2222
|
+
}), [["__scopeId", "data-v-d7e75f4f"]]);
|
|
2223
|
+
// src/app/PlaygroundSceneSource.vue?vue&type=script&setup=true&lang.ts
|
|
2224
|
+
var _hoisted_1$7 = { class: "pw-source__head" };
|
|
2225
|
+
var _hoisted_2$7 = { class: "pw-source__file" };
|
|
2226
|
+
var _hoisted_3$7 = { class: "pw-source__actions" };
|
|
2227
|
+
var _hoisted_4$6 = ["data-tip"];
|
|
2228
|
+
var _hoisted_5$6 = ["data-tip"];
|
|
2229
|
+
var _hoisted_6$5 = ["data-tip", "aria-label"];
|
|
2230
|
+
var _hoisted_7$4 = {
|
|
2231
|
+
key: 0,
|
|
2232
|
+
class: "pw-source__body"
|
|
2233
|
+
};
|
|
2234
|
+
var _hoisted_8$3 = {
|
|
2235
|
+
key: 0,
|
|
2236
|
+
class: "pw-source__find"
|
|
2237
|
+
};
|
|
2238
|
+
var _hoisted_9$3 = {
|
|
2239
|
+
class: "pw-source__find-count",
|
|
2240
|
+
"aria-live": "polite"
|
|
2241
|
+
};
|
|
2242
|
+
var _hoisted_10$3 = ["disabled"];
|
|
2243
|
+
var _hoisted_11$3 = ["disabled"];
|
|
2244
|
+
var _hoisted_12$2 = ["aria-pressed"];
|
|
2245
|
+
var _hoisted_13$2 = ["aria-pressed"];
|
|
2246
|
+
var _hoisted_14$2 = {
|
|
2247
|
+
key: 1,
|
|
2248
|
+
class: "pw-source__tabs"
|
|
2249
|
+
};
|
|
2250
|
+
var _hoisted_15$2 = ["onClick"];
|
|
2251
|
+
var _hoisted_16$2 = { class: "pw-source__code" };
|
|
2252
|
+
var _hoisted_17$2 = {
|
|
2253
|
+
key: 0,
|
|
2254
|
+
class: "pw-source__hint"
|
|
2255
|
+
};
|
|
2256
|
+
// src/app/PlaygroundSceneSource.vue
|
|
2257
|
+
var PlaygroundSceneSource_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
2258
|
+
__name: "PlaygroundSceneSource",
|
|
2259
|
+
props: { scene: {} },
|
|
2260
|
+
setup(__props) {
|
|
2261
|
+
const props = __props;
|
|
2262
|
+
const rootEl = ref(null);
|
|
2263
|
+
const open = ref(false);
|
|
2264
|
+
const activeIndex = ref(0);
|
|
2265
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2266
|
+
const codes = ref({});
|
|
2267
|
+
const loading = ref(false);
|
|
2268
|
+
const copied = ref(false);
|
|
2269
|
+
const pathCopied = ref(false);
|
|
2270
|
+
const sources = computed(() => props.scene.sources);
|
|
2271
|
+
const active = computed(() => sources.value[activeIndex.value] ?? sources.value[0]);
|
|
2272
|
+
const fileName = computed(() => active.value?.rel.split("/").pop() ?? "");
|
|
2273
|
+
const lang = computed(() => {
|
|
2274
|
+
if (fileName.value.endsWith(".md")) return "markdown";
|
|
2275
|
+
if (fileName.value.endsWith(".ts")) return "ts";
|
|
2276
|
+
if (fileName.value.endsWith(".json")) return "json";
|
|
2277
|
+
if (fileName.value.endsWith(".css")) return "css";
|
|
2278
|
+
if (fileName.value.endsWith(".less")) return "less";
|
|
2279
|
+
return "vue";
|
|
2280
|
+
});
|
|
2281
|
+
const code = computed(() => active.value ? codes.value[active.value.rel] ?? "" : "");
|
|
2282
|
+
const codeRef = ref(null);
|
|
2283
|
+
const findOpen = ref(false);
|
|
2284
|
+
const findInput = ref(null);
|
|
2285
|
+
const query = ref("");
|
|
2286
|
+
/** 防抖后的关键字:真正传给 CodeBlock 的那个 */
|
|
2287
|
+
const applied = ref("");
|
|
2288
|
+
const caseSensitive = ref(false);
|
|
2289
|
+
const wholeWord = ref(false);
|
|
2290
|
+
const total = ref(0);
|
|
2291
|
+
const hitIndex = ref(-1);
|
|
2292
|
+
let debounce = 0;
|
|
2293
|
+
const countText = computed(() => {
|
|
2294
|
+
if (!applied.value) return "";
|
|
2295
|
+
if (!total.value) return "无结果";
|
|
2296
|
+
return `${hitIndex.value + 1}/${total.value}`;
|
|
2297
|
+
});
|
|
2298
|
+
const openFind = async () => {
|
|
2299
|
+
if (!open.value) return;
|
|
2300
|
+
findOpen.value = true;
|
|
2301
|
+
await nextTick();
|
|
2302
|
+
findInput.value?.focus();
|
|
2303
|
+
findInput.value?.select();
|
|
2304
|
+
};
|
|
2305
|
+
const closeFind = () => {
|
|
2306
|
+
findOpen.value = false;
|
|
2307
|
+
query.value = "";
|
|
2308
|
+
applied.value = "";
|
|
2309
|
+
total.value = 0;
|
|
2310
|
+
hitIndex.value = -1;
|
|
2311
|
+
};
|
|
2312
|
+
const toggleFind = () => {
|
|
2313
|
+
if (findOpen.value) closeFind();
|
|
2314
|
+
else openFind();
|
|
2315
|
+
};
|
|
2316
|
+
watch(query, () => {
|
|
2317
|
+
window.clearTimeout(debounce);
|
|
2318
|
+
if (!query.value) {
|
|
2319
|
+
applied.value = "";
|
|
2320
|
+
total.value = 0;
|
|
2321
|
+
hitIndex.value = -1;
|
|
2322
|
+
return;
|
|
2323
|
+
}
|
|
2324
|
+
debounce = window.setTimeout(() => {
|
|
2325
|
+
applied.value = query.value;
|
|
2326
|
+
}, 120);
|
|
2327
|
+
});
|
|
2328
|
+
/** CodeBlock 报回匹配数:新结果从第一个开始,并滚到那里 */
|
|
2329
|
+
const onCount = (value) => {
|
|
2330
|
+
total.value = value;
|
|
2331
|
+
hitIndex.value = value > 0 ? 0 : -1;
|
|
2332
|
+
if (value > 0) nextTick(() => codeRef.value?.scrollToHit(0));
|
|
2333
|
+
};
|
|
2334
|
+
const step = (delta) => {
|
|
2335
|
+
if (!total.value) return;
|
|
2336
|
+
hitIndex.value = (hitIndex.value + delta + total.value) % total.value;
|
|
2337
|
+
nextTick(() => codeRef.value?.scrollToHit(hitIndex.value));
|
|
2338
|
+
};
|
|
2339
|
+
const onFindKeydown = (event) => {
|
|
2340
|
+
if (event.key === "Enter") {
|
|
2341
|
+
event.preventDefault();
|
|
2342
|
+
step(event.shiftKey ? -1 : 1);
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
2345
|
+
if (event.key === "Escape") {
|
|
2346
|
+
event.preventDefault();
|
|
2347
|
+
closeFind();
|
|
2348
|
+
}
|
|
2349
|
+
};
|
|
2350
|
+
/** 卡片内有焦点时 ⌘/Ctrl+F 拦下来做源码搜索,不弹浏览器查找 */
|
|
2351
|
+
const onWindowKeydown = (event) => {
|
|
2352
|
+
if (!(event.metaKey || event.ctrlKey) || event.key.toLowerCase() !== "f") return;
|
|
2353
|
+
if (!open.value || !rootEl.value) return;
|
|
2354
|
+
const focused = document.activeElement;
|
|
2355
|
+
if (!focused || !rootEl.value.contains(focused)) return;
|
|
2356
|
+
event.preventDefault();
|
|
2357
|
+
openFind();
|
|
2358
|
+
};
|
|
2359
|
+
onMounted(() => window.addEventListener("keydown", onWindowKeydown));
|
|
2360
|
+
onBeforeUnmount(() => {
|
|
2361
|
+
window.removeEventListener("keydown", onWindowKeydown);
|
|
2362
|
+
window.clearTimeout(debounce);
|
|
2363
|
+
});
|
|
2364
|
+
/** 首次展开或切 Tab 时才读源码 */
|
|
2365
|
+
const load = async (rel) => {
|
|
2366
|
+
if (codes.value[rel]) return;
|
|
2367
|
+
const source = sources.value.find((item) => item.rel === rel);
|
|
2368
|
+
if (!source) return;
|
|
2369
|
+
loading.value = true;
|
|
2370
|
+
try {
|
|
2371
|
+
const raw = cache.get(rel) ?? await source.raw();
|
|
2372
|
+
cache.set(rel, raw);
|
|
2373
|
+
codes.value = {
|
|
2374
|
+
...codes.value,
|
|
2375
|
+
[rel]: raw
|
|
2376
|
+
};
|
|
2377
|
+
} catch {
|
|
2378
|
+
codes.value = {
|
|
2379
|
+
...codes.value,
|
|
2380
|
+
[rel]: "// 源码读取失败"
|
|
2381
|
+
};
|
|
2382
|
+
} finally {
|
|
2383
|
+
loading.value = false;
|
|
2384
|
+
}
|
|
2385
|
+
};
|
|
2386
|
+
const toggle = async () => {
|
|
2387
|
+
open.value = !open.value;
|
|
2388
|
+
if (!open.value) closeFind();
|
|
2389
|
+
if (open.value && active.value) await load(active.value.rel);
|
|
2390
|
+
};
|
|
2391
|
+
const selectTab = async (index) => {
|
|
2392
|
+
activeIndex.value = index;
|
|
2393
|
+
hitIndex.value = -1;
|
|
2394
|
+
if (open.value && active.value) await load(active.value.rel);
|
|
2395
|
+
};
|
|
2396
|
+
const copy = async () => {
|
|
2397
|
+
if (!code.value && active.value) await load(active.value.rel);
|
|
2398
|
+
if (!code.value) {
|
|
2399
|
+
toast("复制失败:源码还没读到");
|
|
2400
|
+
return;
|
|
2401
|
+
}
|
|
2402
|
+
if (!await copyText(code.value)) {
|
|
2403
|
+
toast("复制失败");
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
copied.value = true;
|
|
2407
|
+
toast(`已复制 ${fileName.value}`);
|
|
2408
|
+
window.setTimeout(() => copied.value = false, 1500);
|
|
2409
|
+
};
|
|
2410
|
+
/** 复制当前 Tab 对应文件的绝对路径(没配 root 时退回仓库相对路径) */
|
|
2411
|
+
const copyFilePath = async () => {
|
|
2412
|
+
await copyPath(active.value?.rel ?? props.scene.rel);
|
|
2413
|
+
pathCopied.value = true;
|
|
2414
|
+
window.setTimeout(() => pathCopied.value = false, 1500);
|
|
2415
|
+
};
|
|
2416
|
+
return (_ctx, _cache) => {
|
|
2417
|
+
return openBlock(), createElementBlock("section", {
|
|
2418
|
+
ref_key: "rootEl",
|
|
2419
|
+
ref: rootEl,
|
|
2420
|
+
class: normalizeClass(["pw-source", { "is-open": open.value }])
|
|
2421
|
+
}, [createElementVNode("header", _hoisted_1$7, [createElementVNode("button", {
|
|
2422
|
+
type: "button",
|
|
2423
|
+
class: "pw-source__toggle",
|
|
2424
|
+
onClick: toggle
|
|
2425
|
+
}, [
|
|
2426
|
+
createVNode(unref(ChevronRight), {
|
|
2427
|
+
size: 14,
|
|
2428
|
+
class: "pw-source__chevron"
|
|
2429
|
+
}),
|
|
2430
|
+
createVNode(unref(FileCodeCorner), {
|
|
2431
|
+
size: 14,
|
|
2432
|
+
class: "pw-source__icon"
|
|
2433
|
+
}),
|
|
2434
|
+
_cache[6] || (_cache[6] = createElementVNode("span", { class: "pw-source__label" }, "源码", -1)),
|
|
2435
|
+
createElementVNode("span", _hoisted_2$7, toDisplayString(fileName.value), 1)
|
|
2436
|
+
]), createElementVNode("div", _hoisted_3$7, [
|
|
2437
|
+
open.value ? (openBlock(), createElementBlock("button", {
|
|
2438
|
+
key: 0,
|
|
2439
|
+
type: "button",
|
|
2440
|
+
class: normalizeClass(["pw-source__action", { "is-on": findOpen.value }]),
|
|
2441
|
+
"data-tip": "在源码中搜索(⌘F)",
|
|
2442
|
+
"aria-label": "在源码中搜索",
|
|
2443
|
+
onClick: toggleFind
|
|
2444
|
+
}, [createVNode(unref(Search), { size: 14 })], 2)) : createCommentVNode("", true),
|
|
2445
|
+
createElementVNode("button", {
|
|
2446
|
+
type: "button",
|
|
2447
|
+
class: "pw-source__action",
|
|
2448
|
+
"data-tip": copied.value ? "已复制源码" : "复制源码",
|
|
2449
|
+
"aria-label": "复制源码",
|
|
2450
|
+
onClick: copy
|
|
2451
|
+
}, [copied.value ? (openBlock(), createBlock(unref(Check), {
|
|
2452
|
+
key: 0,
|
|
2453
|
+
size: 14
|
|
2454
|
+
})) : (openBlock(), createBlock(unref(Copy), {
|
|
2455
|
+
key: 1,
|
|
2456
|
+
size: 14
|
|
2457
|
+
}))], 8, _hoisted_4$6),
|
|
2458
|
+
createElementVNode("button", {
|
|
2459
|
+
type: "button",
|
|
2460
|
+
class: "pw-source__action",
|
|
2461
|
+
"data-tip": pathCopied.value ? "已复制路径" : "复制文件路径",
|
|
2462
|
+
"aria-label": "复制文件路径",
|
|
2463
|
+
onClick: copyFilePath
|
|
2464
|
+
}, [pathCopied.value ? (openBlock(), createBlock(unref(Check), {
|
|
2465
|
+
key: 0,
|
|
2466
|
+
size: 14
|
|
2467
|
+
})) : (openBlock(), createBlock(unref(FileSymlink), {
|
|
2468
|
+
key: 1,
|
|
2469
|
+
size: 14,
|
|
2470
|
+
"stroke-width": 2
|
|
2471
|
+
}))], 8, _hoisted_5$6),
|
|
2472
|
+
unref(editorEnabled)() ? (openBlock(), createElementBlock("button", {
|
|
2473
|
+
key: 1,
|
|
2474
|
+
type: "button",
|
|
2475
|
+
class: "pw-source__action",
|
|
2476
|
+
"data-tip": `用 ${unref(editorLabel)()} 打开`,
|
|
2477
|
+
"aria-label": `用 ${unref(editorLabel)()} 打开`,
|
|
2478
|
+
onClick: _cache[0] || (_cache[0] = ($event) => unref(openInEditor)(active.value?.rel ?? __props.scene.rel))
|
|
2479
|
+
}, [createVNode(unref(SquareArrowOutUpRight), {
|
|
2480
|
+
size: 14,
|
|
2481
|
+
"stroke-width": 2
|
|
2482
|
+
})], 8, _hoisted_6$5)) : createCommentVNode("", true)
|
|
2483
|
+
])]), open.value ? (openBlock(), createElementBlock("div", _hoisted_7$4, [
|
|
2484
|
+
findOpen.value ? (openBlock(), createElementBlock("div", _hoisted_8$3, [
|
|
2485
|
+
createVNode(unref(Search), {
|
|
2486
|
+
size: 13,
|
|
2487
|
+
class: "pw-source__find-icon"
|
|
2488
|
+
}),
|
|
2489
|
+
withDirectives(createElementVNode("input", {
|
|
2490
|
+
ref_key: "findInput",
|
|
2491
|
+
ref: findInput,
|
|
2492
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => query.value = $event),
|
|
2493
|
+
type: "text",
|
|
2494
|
+
class: "pw-source__find-input",
|
|
2495
|
+
placeholder: "在当前文件中搜索",
|
|
2496
|
+
"aria-label": "在源码中搜索",
|
|
2497
|
+
spellcheck: "false",
|
|
2498
|
+
onKeydown: onFindKeydown
|
|
2499
|
+
}, null, 544), [[vModelText, query.value]]),
|
|
2500
|
+
createElementVNode("span", _hoisted_9$3, toDisplayString(countText.value), 1),
|
|
2501
|
+
createElementVNode("button", {
|
|
2502
|
+
type: "button",
|
|
2503
|
+
class: "pw-source__find-btn",
|
|
2504
|
+
disabled: !total.value,
|
|
2505
|
+
"data-tip": "上一个匹配(Shift+Enter)",
|
|
2506
|
+
"aria-label": "上一个匹配",
|
|
2507
|
+
onClick: _cache[2] || (_cache[2] = ($event) => step(-1))
|
|
2508
|
+
}, [createVNode(unref(ChevronUp), { size: 14 })], 8, _hoisted_10$3),
|
|
2509
|
+
createElementVNode("button", {
|
|
2510
|
+
type: "button",
|
|
2511
|
+
class: "pw-source__find-btn",
|
|
2512
|
+
disabled: !total.value,
|
|
2513
|
+
"data-tip": "下一个匹配(Enter)",
|
|
2514
|
+
"aria-label": "下一个匹配",
|
|
2515
|
+
onClick: _cache[3] || (_cache[3] = ($event) => step(1))
|
|
2516
|
+
}, [createVNode(unref(ChevronDown), { size: 14 })], 8, _hoisted_11$3),
|
|
2517
|
+
createElementVNode("button", {
|
|
2518
|
+
type: "button",
|
|
2519
|
+
class: normalizeClass(["pw-source__find-btn", { "is-on": caseSensitive.value }]),
|
|
2520
|
+
"aria-pressed": caseSensitive.value,
|
|
2521
|
+
"data-tip": "区分大小写",
|
|
2522
|
+
"aria-label": "区分大小写",
|
|
2523
|
+
onClick: _cache[4] || (_cache[4] = ($event) => caseSensitive.value = !caseSensitive.value)
|
|
2524
|
+
}, [createVNode(unref(CaseSensitive), { size: 14 })], 10, _hoisted_12$2),
|
|
2525
|
+
createElementVNode("button", {
|
|
2526
|
+
type: "button",
|
|
2527
|
+
class: normalizeClass(["pw-source__find-btn", { "is-on": wholeWord.value }]),
|
|
2528
|
+
"aria-pressed": wholeWord.value,
|
|
2529
|
+
"data-tip": "全词匹配",
|
|
2530
|
+
"aria-label": "全词匹配",
|
|
2531
|
+
onClick: _cache[5] || (_cache[5] = ($event) => wholeWord.value = !wholeWord.value)
|
|
2532
|
+
}, [createVNode(unref(WholeWord), { size: 14 })], 10, _hoisted_13$2),
|
|
2533
|
+
createElementVNode("button", {
|
|
2534
|
+
type: "button",
|
|
2535
|
+
class: "pw-source__find-btn",
|
|
2536
|
+
"data-tip": "关闭搜索(Esc)",
|
|
2537
|
+
"aria-label": "关闭搜索",
|
|
2538
|
+
onClick: closeFind
|
|
2539
|
+
}, [createVNode(unref(X), { size: 14 })])
|
|
2540
|
+
])) : createCommentVNode("", true),
|
|
2541
|
+
sources.value.length > 1 ? (openBlock(), createElementBlock("div", _hoisted_14$2, [(openBlock(true), createElementBlock(Fragment, null, renderList(sources.value, (source, index) => {
|
|
2542
|
+
return openBlock(), createElementBlock("button", {
|
|
2543
|
+
key: source.rel,
|
|
2544
|
+
type: "button",
|
|
2545
|
+
class: normalizeClass(["pw-source__tab", { "is-active": index === activeIndex.value }]),
|
|
2546
|
+
onClick: ($event) => selectTab(index)
|
|
2547
|
+
}, toDisplayString(source.rel.split("/").pop()), 11, _hoisted_15$2);
|
|
2548
|
+
}), 128))])) : createCommentVNode("", true),
|
|
2549
|
+
createElementVNode("div", _hoisted_16$2, [loading.value ? (openBlock(), createElementBlock("p", _hoisted_17$2, "读取中…")) : code.value ? (openBlock(), createBlock(CodeBlock_default, {
|
|
2550
|
+
key: 1,
|
|
2551
|
+
ref_key: "codeRef",
|
|
2552
|
+
ref: codeRef,
|
|
2553
|
+
code: code.value,
|
|
2554
|
+
lang: lang.value,
|
|
2555
|
+
query: applied.value,
|
|
2556
|
+
"case-sensitive": caseSensitive.value,
|
|
2557
|
+
"whole-word": wholeWord.value,
|
|
2558
|
+
active: hitIndex.value,
|
|
2559
|
+
onCount
|
|
2560
|
+
}, null, 8, [
|
|
2561
|
+
"code",
|
|
2562
|
+
"lang",
|
|
2563
|
+
"query",
|
|
2564
|
+
"case-sensitive",
|
|
2565
|
+
"whole-word",
|
|
2566
|
+
"active"
|
|
2567
|
+
])) : createCommentVNode("", true)])
|
|
2568
|
+
])) : createCommentVNode("", true)], 2);
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
}), [["__scopeId", "data-v-9eb680ec"]]);
|
|
2572
|
+
// src/app/PlaygroundSceneOutput.vue?vue&type=script&setup=true&lang.ts
|
|
2573
|
+
var _hoisted_1$6 = {
|
|
2574
|
+
key: 0,
|
|
2575
|
+
class: "pw-output"
|
|
2576
|
+
};
|
|
2577
|
+
var _hoisted_2$6 = { class: "pw-output__head" };
|
|
2578
|
+
var _hoisted_3$6 = {
|
|
2579
|
+
key: 0,
|
|
2580
|
+
class: "pw-output__tabs"
|
|
2581
|
+
};
|
|
2582
|
+
var _hoisted_4$5 = ["onClick"];
|
|
2583
|
+
var _hoisted_5$5 = {
|
|
2584
|
+
key: 1,
|
|
2585
|
+
class: "pw-output__slot"
|
|
2586
|
+
};
|
|
2587
|
+
var _hoisted_6$4 = { class: "pw-output__body" };
|
|
2588
|
+
// src/app/PlaygroundSceneOutput.vue
|
|
2589
|
+
var PlaygroundSceneOutput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
2590
|
+
__name: "PlaygroundSceneOutput",
|
|
2591
|
+
props: { sceneName: {} },
|
|
2592
|
+
setup(__props) {
|
|
2593
|
+
const props = __props;
|
|
2594
|
+
const slots = computed(() => currentOutputSlots.value.filter((slot) => !slot.sceneId || slot.sceneId === props.sceneName));
|
|
2595
|
+
const active = ref(0);
|
|
2596
|
+
const snapshots = ref([]);
|
|
2597
|
+
let timer = null;
|
|
2598
|
+
const read = () => {
|
|
2599
|
+
const next = slots.value.map((binding) => {
|
|
2600
|
+
try {
|
|
2601
|
+
const value = binding.getter();
|
|
2602
|
+
return {
|
|
2603
|
+
label: binding.label || binding.key,
|
|
2604
|
+
text: JSON.stringify(value, null, 2) ?? String(value)
|
|
2605
|
+
};
|
|
2606
|
+
} catch (error) {
|
|
2607
|
+
return {
|
|
2608
|
+
label: binding.label || binding.key,
|
|
2609
|
+
text: `读取失败:${String(error)}`
|
|
2610
|
+
};
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
if (next.length !== snapshots.value.length || next.some((item, index) => item.text !== snapshots.value[index]?.text || item.label !== snapshots.value[index]?.label)) snapshots.value = next;
|
|
2614
|
+
if (active.value >= snapshots.value.length) active.value = 0;
|
|
2615
|
+
};
|
|
2616
|
+
onMounted(() => {
|
|
2617
|
+
read();
|
|
2618
|
+
timer = window.setInterval(read, 500);
|
|
2619
|
+
});
|
|
2620
|
+
onBeforeUnmount(() => {
|
|
2621
|
+
if (timer !== null) window.clearInterval(timer);
|
|
2622
|
+
});
|
|
2623
|
+
return (_ctx, _cache) => {
|
|
2624
|
+
return slots.value.length ? (openBlock(), createElementBlock("section", _hoisted_1$6, [createElementVNode("header", _hoisted_2$6, [_cache[0] || (_cache[0] = createElementVNode("span", { class: "pw-output__title" }, "输出", -1)), snapshots.value.length > 1 ? (openBlock(), createElementBlock("div", _hoisted_3$6, [(openBlock(true), createElementBlock(Fragment, null, renderList(snapshots.value, (item, index) => {
|
|
2625
|
+
return openBlock(), createElementBlock("button", {
|
|
2626
|
+
key: item.label,
|
|
2627
|
+
type: "button",
|
|
2628
|
+
class: normalizeClass(["pw-output__tab", { "is-active": index === active.value }]),
|
|
2629
|
+
onClick: ($event) => active.value = index
|
|
2630
|
+
}, toDisplayString(item.label), 11, _hoisted_4$5);
|
|
2631
|
+
}), 128))])) : snapshots.value[0]?.label && snapshots.value[0]?.label !== "default" ? (openBlock(), createElementBlock("span", _hoisted_5$5, toDisplayString(snapshots.value[0]?.label), 1)) : createCommentVNode("", true)]), createElementVNode("div", _hoisted_6$4, [snapshots.value[active.value] ? (openBlock(), createBlock(CodeBlock_default, {
|
|
2632
|
+
key: 0,
|
|
2633
|
+
code: snapshots.value[active.value].text,
|
|
2634
|
+
lang: "json"
|
|
2635
|
+
}, null, 8, ["code"])) : createCommentVNode("", true)])])) : createCommentVNode("", true);
|
|
2636
|
+
};
|
|
2637
|
+
}
|
|
2638
|
+
}), [["__scopeId", "data-v-31716e5f"]]);
|
|
2639
|
+
// src/app/PlaygroundPluginWrappers.vue
|
|
2640
|
+
var PlaygroundPluginWrappers_default = /* @__PURE__ */ defineComponent({
|
|
2641
|
+
__name: "PlaygroundPluginWrappers",
|
|
2642
|
+
props: { entries: {} },
|
|
2643
|
+
setup(__props) {
|
|
2644
|
+
const props = __props;
|
|
2645
|
+
const head = computed(() => props.entries[0]);
|
|
2646
|
+
const rest = computed(() => props.entries.slice(1));
|
|
2647
|
+
return (_ctx, _cache) => {
|
|
2648
|
+
const _component_PlaygroundPluginWrappers = resolveComponent("PlaygroundPluginWrappers", true);
|
|
2649
|
+
return !head.value ? renderSlot(_ctx.$slots, "default", {}, void 0, void 0, 0) : (openBlock(), createBlock(resolveDynamicComponent(head.value.component), normalizeProps(mergeProps({ key: 1 }, head.value.props ?? {})), {
|
|
2650
|
+
default: withCtx(() => [createVNode(_component_PlaygroundPluginWrappers, { entries: rest.value }, {
|
|
2651
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
|
|
2652
|
+
_: 3
|
|
2653
|
+
}, 8, ["entries"])]),
|
|
2654
|
+
_: 3
|
|
2655
|
+
}, 16));
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
});
|
|
2659
|
+
// src/app/PlaygroundSceneSection.vue?vue&type=script&setup=true&lang.ts
|
|
2660
|
+
var _hoisted_1$5 = ["id"];
|
|
2661
|
+
var _hoisted_2$5 = { class: "pw-scene__head" };
|
|
2662
|
+
var _hoisted_3$5 = { class: "pw-scene__title" };
|
|
2663
|
+
var _hoisted_4$4 = ["href"];
|
|
2664
|
+
var _hoisted_5$4 = { class: "pw-scene__actions" };
|
|
2665
|
+
var _hoisted_6$3 = ["data-tip"];
|
|
2666
|
+
var _hoisted_7$3 = {
|
|
2667
|
+
key: 0,
|
|
2668
|
+
class: "pw-scene__desc"
|
|
2669
|
+
};
|
|
2670
|
+
var _hoisted_8$2 = {
|
|
2671
|
+
key: 0,
|
|
2672
|
+
class: "pw-scene__doc"
|
|
2673
|
+
};
|
|
2674
|
+
var _hoisted_9$2 = {
|
|
2675
|
+
key: 1,
|
|
2676
|
+
class: "pw-scene__skeleton",
|
|
2677
|
+
"aria-hidden": "true"
|
|
2678
|
+
};
|
|
2679
|
+
var _hoisted_10$2 = ["data-pw-stage"];
|
|
2680
|
+
var _hoisted_11$2 = {
|
|
2681
|
+
key: 1,
|
|
2682
|
+
class: "pw-scene__skeleton",
|
|
2683
|
+
"aria-hidden": "true"
|
|
2684
|
+
};
|
|
2685
|
+
// src/app/PlaygroundSceneSection.vue
|
|
2686
|
+
var PlaygroundSceneSection_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
2687
|
+
__name: "PlaygroundSceneSection",
|
|
2688
|
+
props: {
|
|
2689
|
+
scene: {},
|
|
2690
|
+
theme: {}
|
|
2691
|
+
},
|
|
2692
|
+
emits: ["focus", "inspect"],
|
|
2693
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
2694
|
+
const props = __props;
|
|
2695
|
+
const emit = __emit;
|
|
2696
|
+
/** 让场景内的 useSceneArgs / useSceneOutput 知道自己属于哪个场景 */
|
|
2697
|
+
provideSceneContext(props.scene.name);
|
|
2698
|
+
const rootEl = ref(null);
|
|
2699
|
+
const component = shallowRef(null);
|
|
2700
|
+
const mounted = ref(false);
|
|
2701
|
+
computed(() => props.scene.rel.split("/").pop() ?? props.scene.rel);
|
|
2702
|
+
/**
|
|
2703
|
+
* 文档场景:不套演示画布,按文档排版直接铺在页面上。
|
|
2704
|
+
* 由插件在扫描时定好(Markdown 场景恒为 true,`.vue` 页看 pageMeta.doc),运行时不再猜。
|
|
2705
|
+
*/
|
|
2706
|
+
const isDoc = computed(() => props.scene.doc);
|
|
2707
|
+
/** 分享:把地址栏 hash 同步成这个场景,再复制「当前页地址 + 锚点」 */
|
|
2708
|
+
const share = async () => {
|
|
2709
|
+
emit("focus", props.scene.name);
|
|
2710
|
+
const { origin, pathname, search } = window.location;
|
|
2711
|
+
const ok = await copyText(`${origin}${pathname}${search}#${encodeURIComponent(props.scene.name)}`);
|
|
2712
|
+
toast(ok ? "已复制场景链接" : "复制失败,请手动复制地址");
|
|
2713
|
+
};
|
|
2714
|
+
/** 演示画布主题:默认浅色,'auto' 跟随外壳;组件不一定支持暗色 */
|
|
2715
|
+
const stageTheme = computed(() => {
|
|
2716
|
+
const stage = config.theme?.stage ?? "light";
|
|
2717
|
+
return stage === "auto" ? props.theme ?? "light" : stage;
|
|
2718
|
+
});
|
|
2719
|
+
/** 该场景演示的 API:名字 + 类型/默认值(从契约里查) */
|
|
2720
|
+
const contractOfPage = computed(() => {
|
|
2721
|
+
const name = pageComponents[props.scene.pageId];
|
|
2722
|
+
return name ? api[name] ?? null : null;
|
|
2723
|
+
});
|
|
2724
|
+
const apiItems = computed(() => (props.scene.api ?? []).map((name) => {
|
|
2725
|
+
const source = contractOfPage.value;
|
|
2726
|
+
const prop = source?.props.find((item) => item.name === name);
|
|
2727
|
+
const event = source?.events.find((item) => item.name === name);
|
|
2728
|
+
return {
|
|
2729
|
+
name,
|
|
2730
|
+
type: prop?.type ?? event?.type,
|
|
2731
|
+
default: prop?.default
|
|
2732
|
+
};
|
|
2733
|
+
}));
|
|
2734
|
+
/** section → API 表里的 tab 名 */
|
|
2735
|
+
const SECTION_KIND = {
|
|
2736
|
+
props: "props",
|
|
2737
|
+
events: "events",
|
|
2738
|
+
exposes: "exposed",
|
|
2739
|
+
slots: "slots"
|
|
2740
|
+
};
|
|
2741
|
+
const inspect = (name) => {
|
|
2742
|
+
emit("inspect", `${SECTION_KIND[props.scene.section] ?? "props"}:${name}`);
|
|
2743
|
+
};
|
|
2744
|
+
/** 本场景是否声明了输入 / 输出;决定「输入 | 输出」并排还是单栏 */
|
|
2745
|
+
const ownsSlot = (sceneId) => !sceneId || sceneId === props.scene.name;
|
|
2746
|
+
const hasArgs = computed(() => currentArgsSlots.value.some((slot) => ownsSlot(slot.sceneId)));
|
|
2747
|
+
const hasOutput = computed(() => currentOutputSlots.value.some((slot) => ownsSlot(slot.sceneId)));
|
|
2748
|
+
const singleColumn = computed(() => !hasArgs.value || !hasOutput.value);
|
|
2749
|
+
let observer = null;
|
|
2750
|
+
/** 进入视口附近才挂载,一页十几个场景也只跑 2~3 个实例 */
|
|
2751
|
+
const mount = async () => {
|
|
2752
|
+
if (mounted.value) return;
|
|
2753
|
+
mounted.value = true;
|
|
2754
|
+
try {
|
|
2755
|
+
const mod = await props.scene.load();
|
|
2756
|
+
component.value = mod.default;
|
|
2757
|
+
} catch (error) {
|
|
2758
|
+
console.warn(`[rinova-playground] 场景加载失败:${props.scene.rel}`, error);
|
|
2759
|
+
}
|
|
2760
|
+
};
|
|
2761
|
+
onMounted(() => {
|
|
2762
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
2763
|
+
mount();
|
|
2764
|
+
return;
|
|
2765
|
+
}
|
|
2766
|
+
observer = new IntersectionObserver((entries) => {
|
|
2767
|
+
if (entries.some((entry) => entry.isIntersecting)) {
|
|
2768
|
+
mount();
|
|
2769
|
+
observer?.disconnect();
|
|
2770
|
+
}
|
|
2771
|
+
}, { rootMargin: "600px 0px" });
|
|
2772
|
+
if (rootEl.value) observer.observe(rootEl.value);
|
|
2773
|
+
});
|
|
2774
|
+
onBeforeUnmount(() => observer?.disconnect());
|
|
2775
|
+
/** 页内索引点击落到本场景时,立即挂载 */
|
|
2776
|
+
__expose({ mount });
|
|
2777
|
+
return (_ctx, _cache) => {
|
|
2778
|
+
return openBlock(), createElementBlock("section", {
|
|
2779
|
+
id: __props.scene.name,
|
|
2780
|
+
ref_key: "rootEl",
|
|
2781
|
+
ref: rootEl,
|
|
2782
|
+
class: normalizeClass(["pw-scene", { "is-doc": isDoc.value }])
|
|
2783
|
+
}, [
|
|
2784
|
+
createElementVNode("header", _hoisted_2$5, [createElementVNode("h2", _hoisted_3$5, [createElementVNode("a", {
|
|
2785
|
+
href: `#${__props.scene.name}`,
|
|
2786
|
+
class: "pw-scene__anchor",
|
|
2787
|
+
onClick: _cache[0] || (_cache[0] = withModifiers(($event) => emit("focus", __props.scene.name), ["prevent"]))
|
|
2788
|
+
}, [_cache[1] || (_cache[1] = createElementVNode("span", {
|
|
2789
|
+
class: "pw-scene__hash",
|
|
2790
|
+
"aria-hidden": "true"
|
|
2791
|
+
}, "#", -1)), createTextVNode(toDisplayString(__props.scene.title), 1)], 8, _hoisted_4$4)]), createElementVNode("div", _hoisted_5$4, [createElementVNode("button", {
|
|
2792
|
+
type: "button",
|
|
2793
|
+
class: "pw-scene__action",
|
|
2794
|
+
"data-tip": `复制「${__props.scene.title}」的链接`,
|
|
2795
|
+
"aria-label": "复制场景链接",
|
|
2796
|
+
onClick: share
|
|
2797
|
+
}, [createVNode(unref(Share2), {
|
|
2798
|
+
size: 12,
|
|
2799
|
+
"stroke-width": 2
|
|
2800
|
+
}), _cache[2] || (_cache[2] = createElementVNode("span", null, "分享", -1))], 8, _hoisted_6$3)])]),
|
|
2801
|
+
__props.scene.description ? (openBlock(), createElementBlock("p", _hoisted_7$3, toDisplayString(__props.scene.description), 1)) : createCommentVNode("", true),
|
|
2802
|
+
apiItems.value.length ? (openBlock(), createBlock(PwApiBrief_default, {
|
|
2803
|
+
key: 1,
|
|
2804
|
+
items: apiItems.value,
|
|
2805
|
+
onInspect: inspect
|
|
2806
|
+
}, null, 8, ["items"])) : createCommentVNode("", true),
|
|
2807
|
+
createElementVNode("div", { class: normalizeClass(["pw-scene__preview", { "is-doc": isDoc.value }]) }, [isDoc.value ? (openBlock(), createElementBlock("div", _hoisted_8$2, [createVNode(PwSceneBoundary_default, {
|
|
2808
|
+
title: __props.scene.title,
|
|
2809
|
+
file: __props.scene.rel
|
|
2810
|
+
}, {
|
|
2811
|
+
default: withCtx(() => [component.value ? (openBlock(), createBlock(resolveDynamicComponent(component.value), { key: 0 })) : (openBlock(), createElementBlock("div", _hoisted_9$2))]),
|
|
2812
|
+
_: 1
|
|
2813
|
+
}, 8, ["title", "file"])])) : (openBlock(), createElementBlock("div", {
|
|
2814
|
+
key: 1,
|
|
2815
|
+
class: "pw-scene__canvas",
|
|
2816
|
+
"data-pw-stage": stageTheme.value
|
|
2817
|
+
}, [createVNode(PwSceneBoundary_default, {
|
|
2818
|
+
title: __props.scene.title,
|
|
2819
|
+
file: __props.scene.rel
|
|
2820
|
+
}, {
|
|
2821
|
+
default: withCtx(() => [createVNode(PlaygroundPluginWrappers_default, { entries: unref(pluginWrapperEntries) }, {
|
|
2822
|
+
default: withCtx(() => [component.value ? (openBlock(), createBlock(resolveDynamicComponent(component.value), { key: 0 })) : (openBlock(), createElementBlock("div", _hoisted_11$2))]),
|
|
2823
|
+
_: 1
|
|
2824
|
+
}, 8, ["entries"])]),
|
|
2825
|
+
_: 1
|
|
2826
|
+
}, 8, ["title", "file"])], 8, _hoisted_10$2))], 2),
|
|
2827
|
+
hasArgs.value || hasOutput.value ? (openBlock(), createElementBlock("div", {
|
|
2828
|
+
key: 2,
|
|
2829
|
+
class: normalizeClass(["pw-scene__io", { "is-single": singleColumn.value }])
|
|
2830
|
+
}, [hasArgs.value ? (openBlock(), createBlock(PlaygroundSceneArgs_default, {
|
|
2831
|
+
key: 0,
|
|
2832
|
+
"scene-name": __props.scene.name
|
|
2833
|
+
}, null, 8, ["scene-name"])) : createCommentVNode("", true), hasOutput.value ? (openBlock(), createBlock(PlaygroundSceneOutput_default, {
|
|
2834
|
+
key: 1,
|
|
2835
|
+
"scene-name": __props.scene.name
|
|
2836
|
+
}, null, 8, ["scene-name"])) : createCommentVNode("", true)], 2)) : createCommentVNode("", true),
|
|
2837
|
+
__props.scene.showSource ? (openBlock(), createBlock(PlaygroundSceneSource_default, {
|
|
2838
|
+
key: 3,
|
|
2839
|
+
scene: __props.scene
|
|
2840
|
+
}, null, 8, ["scene"])) : createCommentVNode("", true)
|
|
2841
|
+
], 10, _hoisted_1$5);
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2844
|
+
}), [["__scopeId", "data-v-d0fca3aa"]]);
|
|
2845
|
+
// src/app/viewport.ts
|
|
2846
|
+
var VIEWPORT_PRESETS = [
|
|
2847
|
+
{
|
|
2848
|
+
id: "auto",
|
|
2849
|
+
label: "自适应",
|
|
2850
|
+
px: 0,
|
|
2851
|
+
shell: "none"
|
|
2852
|
+
},
|
|
2853
|
+
{
|
|
2854
|
+
id: "mobile",
|
|
2855
|
+
label: "手机",
|
|
2856
|
+
px: 390,
|
|
2857
|
+
shell: "phone"
|
|
2858
|
+
},
|
|
2859
|
+
{
|
|
2860
|
+
id: "tablet",
|
|
2861
|
+
label: "平板",
|
|
2862
|
+
px: 820,
|
|
2863
|
+
shell: "tablet"
|
|
2864
|
+
},
|
|
2865
|
+
{
|
|
2866
|
+
id: "desktop",
|
|
2867
|
+
label: "桌面",
|
|
2868
|
+
px: 1280,
|
|
2869
|
+
shell: "desktop"
|
|
2870
|
+
}
|
|
2871
|
+
];
|
|
2872
|
+
/** 自定义宽度的可输入范围,超出就夹住 */
|
|
2873
|
+
var CUSTOM_WIDTH = {
|
|
2874
|
+
min: 320,
|
|
2875
|
+
max: 1920
|
|
2876
|
+
};
|
|
2877
|
+
/** 自定义宽度按大小自动挑外壳,避免 380px 却套桌面窗口 */
|
|
2878
|
+
var shellOf = (px) => {
|
|
2879
|
+
if (px < 640) return "phone";
|
|
2880
|
+
if (px < 1024) return "tablet";
|
|
2881
|
+
return "desktop";
|
|
2882
|
+
};
|
|
2883
|
+
var clampWidth = (px) => Math.min(CUSTOM_WIDTH.max, Math.max(CUSTOM_WIDTH.min, Math.round(px)));
|
|
2884
|
+
var resolveViewport = (state) => {
|
|
2885
|
+
if (state.id === "custom") {
|
|
2886
|
+
const px = clampWidth(state.px || 480);
|
|
2887
|
+
return {
|
|
2888
|
+
id: "custom",
|
|
2889
|
+
px,
|
|
2890
|
+
shell: shellOf(px),
|
|
2891
|
+
label: `自定义 ${px}px`
|
|
2892
|
+
};
|
|
2893
|
+
}
|
|
2894
|
+
const preset = VIEWPORT_PRESETS.find((item) => item.id === state.id) ?? VIEWPORT_PRESETS[0];
|
|
2895
|
+
return {
|
|
2896
|
+
...preset,
|
|
2897
|
+
label: preset.px ? `${preset.label} ${preset.px}px` : preset.label
|
|
2898
|
+
};
|
|
2899
|
+
};
|
|
2900
|
+
var DEFAULT_VIEWPORT = {
|
|
2901
|
+
id: "auto",
|
|
2902
|
+
px: 0
|
|
2903
|
+
};
|
|
2904
|
+
// src/app/PlaygroundPage.vue?vue&type=script&setup=true&lang.ts
|
|
2905
|
+
var _hoisted_1$4 = { class: "pw-page" };
|
|
2906
|
+
var _hoisted_2$4 = { class: "pw-page__head" };
|
|
2907
|
+
var _hoisted_3$4 = { class: "pw-page__lead" };
|
|
2908
|
+
var _hoisted_4$3 = ["data-tip", "aria-label"];
|
|
2909
|
+
var _hoisted_5$3 = ["title"];
|
|
2910
|
+
var _hoisted_6$2 = { class: "pw-page__group" };
|
|
2911
|
+
var _hoisted_7$2 = { class: "pw-page__name" };
|
|
2912
|
+
var _hoisted_8$1 = { class: "pw-page__count" };
|
|
2913
|
+
var _hoisted_9$1 = { class: "pw-page__center" };
|
|
2914
|
+
var _hoisted_10$1 = {
|
|
2915
|
+
key: 0,
|
|
2916
|
+
class: "pw-width",
|
|
2917
|
+
role: "group",
|
|
2918
|
+
"aria-label": "预览宽度"
|
|
2919
|
+
};
|
|
2920
|
+
var _hoisted_11$1 = [
|
|
2921
|
+
"data-tip",
|
|
2922
|
+
"aria-label",
|
|
2923
|
+
"aria-pressed",
|
|
2924
|
+
"onClick"
|
|
2925
|
+
];
|
|
2926
|
+
var _hoisted_12$1 = ["aria-pressed"];
|
|
2927
|
+
var _hoisted_13$1 = {
|
|
2928
|
+
key: 0,
|
|
2929
|
+
class: "pw-width__custom"
|
|
2930
|
+
};
|
|
2931
|
+
var _hoisted_14$1 = { class: "pw-page__actions" };
|
|
2932
|
+
var _hoisted_15$1 = {
|
|
2933
|
+
class: "pw-view",
|
|
2934
|
+
role: "tablist",
|
|
2935
|
+
"aria-label": "视图"
|
|
2936
|
+
};
|
|
2937
|
+
var _hoisted_16$1 = ["data-tip", "aria-label"];
|
|
2938
|
+
var _hoisted_17$1 = [
|
|
2939
|
+
"data-tip",
|
|
2940
|
+
"aria-label",
|
|
2941
|
+
"aria-pressed"
|
|
2942
|
+
];
|
|
2943
|
+
var _hoisted_18$1 = { class: "pw-page__work" };
|
|
2944
|
+
var _hoisted_19$1 = {
|
|
2945
|
+
key: 0,
|
|
2946
|
+
class: "pw-device__bar",
|
|
2947
|
+
"aria-hidden": "true"
|
|
2948
|
+
};
|
|
2949
|
+
var _hoisted_20$1 = {
|
|
2950
|
+
key: 1,
|
|
2951
|
+
class: "pw-device__sensor",
|
|
2952
|
+
"aria-hidden": "true"
|
|
2953
|
+
};
|
|
2954
|
+
var _hoisted_21$1 = {
|
|
2955
|
+
key: 0,
|
|
2956
|
+
class: "pw-page__desc"
|
|
2957
|
+
};
|
|
2958
|
+
var _hoisted_22$1 = {
|
|
2959
|
+
key: 1,
|
|
2960
|
+
class: "pw-page__main"
|
|
2961
|
+
};
|
|
2962
|
+
var _hoisted_23$1 = {
|
|
2963
|
+
key: 4,
|
|
2964
|
+
class: "pw-page__api-empty"
|
|
2965
|
+
};
|
|
2966
|
+
var _hoisted_24$1 = {
|
|
2967
|
+
key: 0,
|
|
2968
|
+
class: "pw-device__caption"
|
|
2969
|
+
};
|
|
2970
|
+
var INDEX_KEY = "rinova-playground:index-open";
|
|
2971
|
+
// src/app/PlaygroundPage.vue
|
|
2972
|
+
var PlaygroundPage_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
2973
|
+
__name: "PlaygroundPage",
|
|
2974
|
+
props: {
|
|
2975
|
+
page: {},
|
|
2976
|
+
scenes: {},
|
|
2977
|
+
initialActive: {},
|
|
2978
|
+
viewport: {},
|
|
2979
|
+
theme: {},
|
|
2980
|
+
sidebarOpen: { type: Boolean }
|
|
2981
|
+
},
|
|
2982
|
+
emits: [
|
|
2983
|
+
"update:viewport",
|
|
2984
|
+
"toggle-theme",
|
|
2985
|
+
"toggle-sidebar",
|
|
2986
|
+
"collapse-sidebar",
|
|
2987
|
+
"update:active",
|
|
2988
|
+
"focus"
|
|
2989
|
+
],
|
|
2990
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
2991
|
+
const props = __props;
|
|
2992
|
+
const emit = __emit;
|
|
2993
|
+
const indexOpen = ref(localStorage.getItem(INDEX_KEY) !== "0");
|
|
2994
|
+
const toggleIndex = () => {
|
|
2995
|
+
indexOpen.value = !indexOpen.value;
|
|
2996
|
+
localStorage.setItem(INDEX_KEY, indexOpen.value ? "1" : "0");
|
|
2997
|
+
};
|
|
2998
|
+
const scrollEl = ref(null);
|
|
2999
|
+
const initialScene = () => {
|
|
3000
|
+
const fallback = props.scenes[0]?.name ?? "";
|
|
3001
|
+
const wanted = props.initialActive;
|
|
3002
|
+
return wanted && props.scenes.some((scene) => scene.name === wanted) ? wanted : fallback;
|
|
3003
|
+
};
|
|
3004
|
+
const active = ref(initialScene());
|
|
3005
|
+
/** 场景视图 / API 视图 */
|
|
3006
|
+
const view = ref("scenes");
|
|
3007
|
+
const focusApi = ref("");
|
|
3008
|
+
const contract = computed(() => {
|
|
3009
|
+
const name = props.page.component ?? pageComponents[props.page.id];
|
|
3010
|
+
return name ? api[name] ?? null : null;
|
|
3011
|
+
});
|
|
3012
|
+
/** 非组件模块(函数 / hooks / 常量)的契约 */
|
|
3013
|
+
const moduleContract = computed(() => {
|
|
3014
|
+
const name = props.page.module ?? pageModules[props.page.id];
|
|
3015
|
+
return name ? modules[name] ?? null : null;
|
|
3016
|
+
});
|
|
3017
|
+
/** 场景里的 API 标签被点击:切到 API 视图并定位到那一行 */
|
|
3018
|
+
const openApi = (key) => {
|
|
3019
|
+
focusApi.value = "";
|
|
3020
|
+
view.value = "api";
|
|
3021
|
+
nextTick(() => {
|
|
3022
|
+
focusApi.value = key;
|
|
3023
|
+
});
|
|
3024
|
+
};
|
|
3025
|
+
/** API 表的「演示」列:切回场景视图并滚过去 */
|
|
3026
|
+
const openScene = (name) => {
|
|
3027
|
+
view.value = "scenes";
|
|
3028
|
+
nextTick(() => jump(name));
|
|
3029
|
+
};
|
|
3030
|
+
const sectionRefs = /* @__PURE__ */ new Map();
|
|
3031
|
+
/** 程序化滚动期间暂停滚动联动,避免高亮跑到上一节 */
|
|
3032
|
+
let suppressScrollSpyUntil = 0;
|
|
3033
|
+
const setSectionRef = (name) => (instance) => {
|
|
3034
|
+
if (instance) sectionRefs.set(name, instance);
|
|
3035
|
+
else sectionRefs.delete(name);
|
|
3036
|
+
};
|
|
3037
|
+
const icons = {
|
|
3038
|
+
auto: Maximize,
|
|
3039
|
+
mobile: Smartphone,
|
|
3040
|
+
tablet: Tablet,
|
|
3041
|
+
desktop: Monitor
|
|
3042
|
+
};
|
|
3043
|
+
const presets = VIEWPORT_PRESETS.map((preset) => ({
|
|
3044
|
+
...preset,
|
|
3045
|
+
icon: icons[preset.id]
|
|
3046
|
+
}));
|
|
3047
|
+
const current = computed(() => resolveViewport(props.viewport));
|
|
3048
|
+
/** 只有固定宽度才套设备外壳;自适应就是铺满。API 视图是表格,尺寸能力整体不适用 */
|
|
3049
|
+
const shell = computed(() => view.value === "api" ? "none" : current.value.px ? current.value.shell : "none");
|
|
3050
|
+
const deviceEl = ref(null);
|
|
3051
|
+
/** 设备实际渲染宽度:目标宽度可能被可用空间夹住,caption 要显示真实值 */
|
|
3052
|
+
const measuredWidth = ref(0);
|
|
3053
|
+
let deviceObserver = null;
|
|
3054
|
+
const deviceLabel = computed(() => {
|
|
3055
|
+
const width = `${measuredWidth.value}px`;
|
|
3056
|
+
if (props.viewport.id === "custom") return `自定义 · ${width}`;
|
|
3057
|
+
return `${VIEWPORT_PRESETS.find((item) => item.id === props.viewport.id)?.label ?? "预览"} ${width}`;
|
|
3058
|
+
});
|
|
3059
|
+
const customOpen = ref(props.viewport.id === "custom");
|
|
3060
|
+
const customValue = ref(String(props.viewport.id === "custom" ? props.viewport.px : 480));
|
|
3061
|
+
const pick = (id) => {
|
|
3062
|
+
customOpen.value = id === "custom";
|
|
3063
|
+
if (id === "custom") {
|
|
3064
|
+
applyCustom();
|
|
3065
|
+
return;
|
|
3066
|
+
}
|
|
3067
|
+
emit("update:viewport", {
|
|
3068
|
+
id,
|
|
3069
|
+
px: 0
|
|
3070
|
+
});
|
|
3071
|
+
};
|
|
3072
|
+
const applyCustom = () => {
|
|
3073
|
+
const px = clampWidth(Number(customValue.value) || CUSTOM_WIDTH.min);
|
|
3074
|
+
customValue.value = String(px);
|
|
3075
|
+
emit("update:viewport", {
|
|
3076
|
+
id: "custom",
|
|
3077
|
+
px
|
|
3078
|
+
});
|
|
3079
|
+
};
|
|
3080
|
+
const onCustomInput = (value) => {
|
|
3081
|
+
customValue.value = value;
|
|
3082
|
+
};
|
|
3083
|
+
const onCustomEnter = () => {
|
|
3084
|
+
applyCustom();
|
|
3085
|
+
};
|
|
3086
|
+
const visibleCount = computed(() => props.scenes.filter((scene) => !scene.hidden).length);
|
|
3087
|
+
/** 自适应铺满;固定宽度用所选值(设备外壳另外画) */
|
|
3088
|
+
const maxWidth = computed(() => current.value.px ? `${current.value.px}px` : "none");
|
|
3089
|
+
/** 滚动联动:取最后一个顶边越过阈值的场景 */
|
|
3090
|
+
onMounted(() => {
|
|
3091
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
3092
|
+
deviceObserver = new ResizeObserver((entries) => {
|
|
3093
|
+
const entry = entries[0];
|
|
3094
|
+
const width = entry?.borderBoxSize?.[0]?.inlineSize ?? entry?.contentRect.width ?? 0;
|
|
3095
|
+
measuredWidth.value = Math.round(width);
|
|
3096
|
+
});
|
|
3097
|
+
if (deviceEl.value) deviceObserver.observe(deviceEl.value);
|
|
3098
|
+
});
|
|
3099
|
+
onBeforeUnmount(() => deviceObserver?.disconnect());
|
|
3100
|
+
const onScroll = () => {
|
|
3101
|
+
const container = scrollEl.value;
|
|
3102
|
+
if (!container) return;
|
|
3103
|
+
if (Date.now() < suppressScrollSpyUntil) return;
|
|
3104
|
+
const threshold = container.getBoundingClientRect().top + 96;
|
|
3105
|
+
let current = props.scenes[0]?.name ?? "";
|
|
3106
|
+
for (const scene of props.scenes) {
|
|
3107
|
+
const el = document.getElementById(scene.name);
|
|
3108
|
+
if (!el) continue;
|
|
3109
|
+
if (el.getBoundingClientRect().top <= threshold) current = scene.name;
|
|
3110
|
+
}
|
|
3111
|
+
if (current && current !== active.value) {
|
|
3112
|
+
active.value = current;
|
|
3113
|
+
emit("update:active", current);
|
|
3114
|
+
}
|
|
3115
|
+
};
|
|
3116
|
+
const jump = async (name) => {
|
|
3117
|
+
const el = document.getElementById(name);
|
|
3118
|
+
const container = scrollEl.value;
|
|
3119
|
+
if (!el || !container) return;
|
|
3120
|
+
active.value = name;
|
|
3121
|
+
emit("update:active", name);
|
|
3122
|
+
emit("focus", name);
|
|
3123
|
+
suppressScrollSpyUntil = Date.now() + 900;
|
|
3124
|
+
await sectionRefs.get(name)?.mount();
|
|
3125
|
+
await nextTick();
|
|
3126
|
+
const delta = el.getBoundingClientRect().top - container.getBoundingClientRect().top;
|
|
3127
|
+
container.scrollTo({
|
|
3128
|
+
top: container.scrollTop + delta - 8,
|
|
3129
|
+
behavior: "smooth"
|
|
3130
|
+
});
|
|
3131
|
+
};
|
|
3132
|
+
watch(active, () => nextTick(onScroll));
|
|
3133
|
+
__expose({ jumpTo: jump });
|
|
3134
|
+
onMounted(() => {
|
|
3135
|
+
const hash = decodeURIComponent(window.location.hash.replace(/^#/, ""));
|
|
3136
|
+
const wanted = (hash && props.scenes.some((scene) => scene.name === hash) ? hash : "") || active.value;
|
|
3137
|
+
if (!wanted || wanted === props.scenes[0]?.name) return;
|
|
3138
|
+
active.value = wanted;
|
|
3139
|
+
emit("update:active", wanted);
|
|
3140
|
+
nextTick(() => jump(wanted));
|
|
3141
|
+
});
|
|
3142
|
+
return (_ctx, _cache) => {
|
|
3143
|
+
return openBlock(), createElementBlock("div", _hoisted_1$4, [createElementVNode("header", _hoisted_2$4, [
|
|
3144
|
+
createElementVNode("div", _hoisted_3$4, [createElementVNode("button", {
|
|
3145
|
+
type: "button",
|
|
3146
|
+
class: "pw-head-btn",
|
|
3147
|
+
"data-tip": __props.sidebarOpen ? "收起侧栏" : "展开侧栏",
|
|
3148
|
+
"aria-label": __props.sidebarOpen ? "收起侧栏" : "展开侧栏",
|
|
3149
|
+
onClick: _cache[0] || (_cache[0] = ($event) => emit("toggle-sidebar"))
|
|
3150
|
+
}, [__props.sidebarOpen ? (openBlock(), createBlock(unref(PanelLeftClose), {
|
|
3151
|
+
key: 0,
|
|
3152
|
+
size: 14
|
|
3153
|
+
})) : (openBlock(), createBlock(unref(PanelLeftOpen), {
|
|
3154
|
+
key: 1,
|
|
3155
|
+
size: 14
|
|
3156
|
+
}))], 8, _hoisted_4$3), createElementVNode("nav", {
|
|
3157
|
+
class: "pw-page__crumb",
|
|
3158
|
+
title: `${__props.page.group} › ${__props.page.title}`,
|
|
3159
|
+
"aria-label": "当前页面位置"
|
|
3160
|
+
}, [
|
|
3161
|
+
__props.page.group !== __props.page.title ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createElementVNode("span", _hoisted_6$2, toDisplayString(__props.page.group), 1), createVNode(unref(ChevronRight), {
|
|
3162
|
+
size: 12,
|
|
3163
|
+
"stroke-width": 2,
|
|
3164
|
+
class: "pw-page__sep",
|
|
3165
|
+
"aria-hidden": "true"
|
|
3166
|
+
})], 64)) : createCommentVNode("", true),
|
|
3167
|
+
createElementVNode("span", _hoisted_7$2, toDisplayString(__props.page.title), 1),
|
|
3168
|
+
createElementVNode("span", _hoisted_8$1, toDisplayString(visibleCount.value) + " 个场景", 1)
|
|
3169
|
+
], 8, _hoisted_5$3)]),
|
|
3170
|
+
createElementVNode("div", _hoisted_9$1, [view.value === "scenes" ? (openBlock(), createElementBlock("div", _hoisted_10$1, [
|
|
3171
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(presets), (item) => {
|
|
3172
|
+
return openBlock(), createElementBlock("button", {
|
|
3173
|
+
key: item.id,
|
|
3174
|
+
type: "button",
|
|
3175
|
+
class: normalizeClass(["pw-width__btn", { "is-active": __props.viewport.id === item.id }]),
|
|
3176
|
+
"data-tip": item.px ? `${item.label} ${item.px}px` : item.label,
|
|
3177
|
+
"aria-label": item.px ? `${item.label} ${item.px}px` : item.label,
|
|
3178
|
+
"aria-pressed": __props.viewport.id === item.id,
|
|
3179
|
+
onClick: ($event) => pick(item.id)
|
|
3180
|
+
}, [(openBlock(), createBlock(resolveDynamicComponent(item.icon), { size: 14 }))], 10, _hoisted_11$1);
|
|
3181
|
+
}), 128)),
|
|
3182
|
+
createElementVNode("button", {
|
|
3183
|
+
type: "button",
|
|
3184
|
+
class: normalizeClass(["pw-width__btn", { "is-active": __props.viewport.id === "custom" }]),
|
|
3185
|
+
"data-tip": "自定义宽度",
|
|
3186
|
+
"aria-label": "自定义宽度",
|
|
3187
|
+
"aria-pressed": __props.viewport.id === "custom",
|
|
3188
|
+
onClick: _cache[1] || (_cache[1] = ($event) => pick("custom"))
|
|
3189
|
+
}, [createVNode(unref(SlidersHorizontal), { size: 14 })], 10, _hoisted_12$1),
|
|
3190
|
+
customOpen.value ? (openBlock(), createElementBlock("label", _hoisted_13$1, [createVNode(PwInput_default, {
|
|
3191
|
+
size: "sm",
|
|
3192
|
+
type: "number",
|
|
3193
|
+
"model-value": customValue.value,
|
|
3194
|
+
placeholder: `${unref(CUSTOM_WIDTH).min}–${unref(CUSTOM_WIDTH).max}`,
|
|
3195
|
+
"onUpdate:modelValue": onCustomInput,
|
|
3196
|
+
onKeydown: withKeys(onCustomEnter, ["enter"]),
|
|
3197
|
+
onBlur: onCustomEnter
|
|
3198
|
+
}, null, 8, ["model-value", "placeholder"]), _cache[5] || (_cache[5] = createElementVNode("span", { class: "pw-width__unit" }, "px", -1))])) : createCommentVNode("", true)
|
|
3199
|
+
])) : createCommentVNode("", true)]),
|
|
3200
|
+
createElementVNode("div", _hoisted_14$1, [
|
|
3201
|
+
createVNode(PlaygroundPluginSlot_default, {
|
|
3202
|
+
entries: unref(pluginHeaderSlots),
|
|
3203
|
+
slot: "header"
|
|
3204
|
+
}, null, 8, ["entries"]),
|
|
3205
|
+
createElementVNode("div", _hoisted_15$1, [createElementVNode("button", {
|
|
3206
|
+
type: "button",
|
|
3207
|
+
class: normalizeClass(["pw-view__btn", { "is-active": view.value === "scenes" }]),
|
|
3208
|
+
onClick: _cache[2] || (_cache[2] = ($event) => view.value = "scenes")
|
|
3209
|
+
}, " 场景 ", 2), contract.value || moduleContract.value ? (openBlock(), createElementBlock("button", {
|
|
3210
|
+
key: 0,
|
|
3211
|
+
type: "button",
|
|
3212
|
+
class: normalizeClass(["pw-view__btn", { "is-active": view.value === "api" }]),
|
|
3213
|
+
onClick: _cache[3] || (_cache[3] = ($event) => view.value = "api")
|
|
3214
|
+
}, " API ", 2)) : createCommentVNode("", true)]),
|
|
3215
|
+
createVNode(PlaygroundEditorPicker_default),
|
|
3216
|
+
createElementVNode("button", {
|
|
3217
|
+
type: "button",
|
|
3218
|
+
class: "pw-head-btn",
|
|
3219
|
+
"data-tip": __props.theme === "dark" ? "切到浅色" : "切到深色",
|
|
3220
|
+
"aria-label": __props.theme === "dark" ? "切到浅色" : "切到深色",
|
|
3221
|
+
onClick: _cache[4] || (_cache[4] = ($event) => emit("toggle-theme", $event))
|
|
3222
|
+
}, [__props.theme === "light" ? (openBlock(), createBlock(unref(Moon), {
|
|
3223
|
+
key: 0,
|
|
3224
|
+
size: 14
|
|
3225
|
+
})) : (openBlock(), createBlock(unref(Sun), {
|
|
3226
|
+
key: 1,
|
|
3227
|
+
size: 14
|
|
3228
|
+
}))], 8, _hoisted_16$1),
|
|
3229
|
+
view.value === "scenes" ? (openBlock(), createElementBlock("button", {
|
|
3230
|
+
key: 0,
|
|
3231
|
+
type: "button",
|
|
3232
|
+
class: "pw-head-btn pw-head-btn--index",
|
|
3233
|
+
"data-tip": indexOpen.value ? "收起索引" : "展开索引",
|
|
3234
|
+
"aria-label": indexOpen.value ? "收起索引" : "展开索引",
|
|
3235
|
+
"aria-pressed": indexOpen.value,
|
|
3236
|
+
onClick: toggleIndex
|
|
3237
|
+
}, [indexOpen.value ? (openBlock(), createBlock(unref(PanelRightClose), {
|
|
3238
|
+
key: 0,
|
|
3239
|
+
size: 14
|
|
3240
|
+
})) : (openBlock(), createBlock(unref(PanelRightOpen), {
|
|
3241
|
+
key: 1,
|
|
3242
|
+
size: 14
|
|
3243
|
+
}))], 8, _hoisted_17$1)) : createCommentVNode("", true)
|
|
3244
|
+
])
|
|
3245
|
+
]), createElementVNode("div", _hoisted_18$1, [createElementVNode("div", {
|
|
3246
|
+
class: normalizeClass(["pw-device-wrap", shell.value === "none" ? "is-auto" : `pw-device-wrap--${shell.value}`]),
|
|
3247
|
+
style: normalizeStyle(shell.value === "none" ? void 0 : { "--pw-device-w": `${current.value.px}px` })
|
|
3248
|
+
}, [createElementVNode("div", {
|
|
3249
|
+
ref_key: "deviceEl",
|
|
3250
|
+
ref: deviceEl,
|
|
3251
|
+
class: "pw-device"
|
|
3252
|
+
}, [
|
|
3253
|
+
shell.value === "desktop" ? (openBlock(), createElementBlock("div", _hoisted_19$1, [..._cache[6] || (_cache[6] = [
|
|
3254
|
+
createElementVNode("i", null, null, -1),
|
|
3255
|
+
createElementVNode("i", null, null, -1),
|
|
3256
|
+
createElementVNode("i", null, null, -1)
|
|
3257
|
+
])])) : createCommentVNode("", true),
|
|
3258
|
+
shell.value === "phone" ? (openBlock(), createElementBlock("span", _hoisted_20$1)) : createCommentVNode("", true),
|
|
3259
|
+
createElementVNode("div", {
|
|
3260
|
+
ref_key: "scrollEl",
|
|
3261
|
+
ref: scrollEl,
|
|
3262
|
+
class: normalizeClass(["pw-page__scroll", { "is-api": view.value === "api" }]),
|
|
3263
|
+
onScrollPassive: onScroll
|
|
3264
|
+
}, [createElementVNode("div", { class: normalizeClass(["pw-page__body", {
|
|
3265
|
+
"is-device": shell.value !== "none",
|
|
3266
|
+
"is-phone": shell.value === "phone"
|
|
3267
|
+
}]) }, [createElementVNode("div", {
|
|
3268
|
+
class: "pw-page__frame",
|
|
3269
|
+
style: normalizeStyle({ maxWidth: view.value === "api" ? "none" : maxWidth.value })
|
|
3270
|
+
}, [__props.page.description && view.value === "scenes" ? (openBlock(), createElementBlock("p", _hoisted_21$1, toDisplayString(__props.page.description), 1)) : createCommentVNode("", true), view.value === "scenes" ? (openBlock(), createElementBlock("div", _hoisted_22$1, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.scenes, (scene) => {
|
|
3271
|
+
return openBlock(), createBlock(PlaygroundSceneSection_default, {
|
|
3272
|
+
key: scene.id,
|
|
3273
|
+
ref_for: true,
|
|
3274
|
+
ref: setSectionRef(scene.name),
|
|
3275
|
+
scene,
|
|
3276
|
+
theme: __props.theme,
|
|
3277
|
+
onFocus: jump,
|
|
3278
|
+
onInspect: openApi
|
|
3279
|
+
}, null, 8, ["scene", "theme"]);
|
|
3280
|
+
}), 128))])) : contract.value ? (openBlock(), createBlock(PlaygroundApiTable_default, {
|
|
3281
|
+
key: 2,
|
|
3282
|
+
contract: contract.value,
|
|
3283
|
+
scenes: __props.scenes,
|
|
3284
|
+
focus: focusApi.value,
|
|
3285
|
+
notes: __props.page.apiNotes,
|
|
3286
|
+
onJump: openScene
|
|
3287
|
+
}, null, 8, [
|
|
3288
|
+
"contract",
|
|
3289
|
+
"scenes",
|
|
3290
|
+
"focus",
|
|
3291
|
+
"notes"
|
|
3292
|
+
])) : moduleContract.value ? (openBlock(), createBlock(PlaygroundModuleApi_default, {
|
|
3293
|
+
key: 3,
|
|
3294
|
+
module: moduleContract.value,
|
|
3295
|
+
scenes: __props.scenes,
|
|
3296
|
+
onJump: openScene
|
|
3297
|
+
}, null, 8, ["module", "scenes"])) : (openBlock(), createElementBlock("p", _hoisted_23$1, [..._cache[7] || (_cache[7] = [
|
|
3298
|
+
createTextVNode(" 这一页没有绑定组件。在 ", -1),
|
|
3299
|
+
createElementVNode("code", null, "<页>/meta.ts", -1),
|
|
3300
|
+
createTextVNode(" 里写 ", -1),
|
|
3301
|
+
createElementVNode("code", null, "component: 'MyButton'", -1),
|
|
3302
|
+
createTextVNode(" 就会生成组件 API 表; 写 ", -1),
|
|
3303
|
+
createElementVNode("code", null, "module: 'money'", -1),
|
|
3304
|
+
createTextVNode(" 则生成函数 / 常量 API 表。 ", -1)
|
|
3305
|
+
])]))], 4)], 2)], 34)
|
|
3306
|
+
], 512), shell.value !== "none" ? (openBlock(), createElementBlock("p", _hoisted_24$1, toDisplayString(deviceLabel.value), 1)) : createCommentVNode("", true)], 6), view.value === "scenes" ? (openBlock(), createElementBlock("div", {
|
|
3307
|
+
key: 0,
|
|
3308
|
+
class: normalizeClass(["pw-index-slot", { "is-collapsed": !indexOpen.value }])
|
|
3309
|
+
}, [createVNode(PlaygroundPageIndex_default, {
|
|
3310
|
+
scenes: __props.scenes,
|
|
3311
|
+
active: active.value,
|
|
3312
|
+
onJump: jump
|
|
3313
|
+
}, null, 8, ["scenes", "active"])], 2)) : createCommentVNode("", true)])]);
|
|
3314
|
+
};
|
|
3315
|
+
}
|
|
3316
|
+
}), [["__scopeId", "data-v-089ab888"]]);
|
|
3317
|
+
// src/components/PwPortal.vue
|
|
3318
|
+
var PwPortal_default = /* @__PURE__ */ defineComponent({
|
|
3319
|
+
__name: "PwPortal",
|
|
3320
|
+
props: { to: { default: "body" } },
|
|
3321
|
+
setup(__props) {
|
|
3322
|
+
return (_ctx, _cache) => {
|
|
3323
|
+
return openBlock(), createBlock(Teleport, { to: __props.to }, [renderSlot(_ctx.$slots, "default")], 8, ["to"]);
|
|
3324
|
+
};
|
|
3325
|
+
}
|
|
3326
|
+
});
|
|
3327
|
+
// src/app/PlaygroundPluginPanel.vue?vue&type=script&setup=true&lang.ts
|
|
3328
|
+
var _hoisted_1$3 = ["aria-label"];
|
|
3329
|
+
var _hoisted_2$3 = { class: "pw-plugin-panel__head" };
|
|
3330
|
+
var _hoisted_3$3 = { class: "pw-plugin-panel__title" };
|
|
3331
|
+
var _hoisted_4$2 = { class: "pw-plugin-panel__from" };
|
|
3332
|
+
var _hoisted_5$2 = { class: "pw-plugin-panel__body" };
|
|
3333
|
+
// src/app/PlaygroundPluginPanel.vue
|
|
3334
|
+
var PlaygroundPluginPanel_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
3335
|
+
__name: "PlaygroundPluginPanel",
|
|
3336
|
+
setup(__props) {
|
|
3337
|
+
const drawerEl = ref(null);
|
|
3338
|
+
const restoreTo = ref(null);
|
|
3339
|
+
const active = computed(() => pluginPanelEntries.value.find((entry) => entry.id === pluginOpenPanelId.value) ?? null);
|
|
3340
|
+
watch(active, async (entry, previous) => {
|
|
3341
|
+
if (entry && !previous) {
|
|
3342
|
+
const focused = document.activeElement;
|
|
3343
|
+
restoreTo.value = focused instanceof HTMLElement ? focused : null;
|
|
3344
|
+
await nextTick();
|
|
3345
|
+
drawerEl.value?.focus();
|
|
3346
|
+
return;
|
|
3347
|
+
}
|
|
3348
|
+
if (!entry && previous) {
|
|
3349
|
+
restoreTo.value?.focus?.();
|
|
3350
|
+
restoreTo.value = null;
|
|
3351
|
+
}
|
|
3352
|
+
});
|
|
3353
|
+
const onKeydown = (event) => {
|
|
3354
|
+
if (event.key !== "Escape" || !active.value) return;
|
|
3355
|
+
event.stopPropagation();
|
|
3356
|
+
closePlaygroundPanel();
|
|
3357
|
+
};
|
|
3358
|
+
onMounted(() => document.addEventListener("keydown", onKeydown, true));
|
|
3359
|
+
onBeforeUnmount(() => document.removeEventListener("keydown", onKeydown, true));
|
|
3360
|
+
return (_ctx, _cache) => {
|
|
3361
|
+
return active.value ? (openBlock(), createBlock(PwPortal_default, { key: 0 }, {
|
|
3362
|
+
default: withCtx(() => [createElementVNode("div", {
|
|
3363
|
+
class: "pw-plugin-panel",
|
|
3364
|
+
role: "dialog",
|
|
3365
|
+
"aria-modal": "true",
|
|
3366
|
+
"aria-label": active.value.title
|
|
3367
|
+
}, [createElementVNode("div", {
|
|
3368
|
+
class: "pw-plugin-panel__mask",
|
|
3369
|
+
onClick: _cache[0] || (_cache[0] = ($event) => unref(closePlaygroundPanel)())
|
|
3370
|
+
}), createElementVNode("aside", {
|
|
3371
|
+
ref_key: "drawerEl",
|
|
3372
|
+
ref: drawerEl,
|
|
3373
|
+
class: "pw-plugin-panel__drawer",
|
|
3374
|
+
tabindex: "-1"
|
|
3375
|
+
}, [createElementVNode("header", _hoisted_2$3, [
|
|
3376
|
+
createElementVNode("h2", _hoisted_3$3, toDisplayString(active.value.title), 1),
|
|
3377
|
+
createElementVNode("span", _hoisted_4$2, "插件 " + toDisplayString(active.value.plugin), 1),
|
|
3378
|
+
createElementVNode("button", {
|
|
3379
|
+
type: "button",
|
|
3380
|
+
class: "pw-plugin-panel__close",
|
|
3381
|
+
"aria-label": "关闭",
|
|
3382
|
+
onClick: _cache[1] || (_cache[1] = ($event) => unref(closePlaygroundPanel)())
|
|
3383
|
+
}, [createVNode(unref(X), {
|
|
3384
|
+
size: 14,
|
|
3385
|
+
"stroke-width": 2
|
|
3386
|
+
})])
|
|
3387
|
+
]), createElementVNode("div", _hoisted_5$2, [createVNode(PwSceneBoundary_default, { title: `插件面板(${active.value.plugin})` }, {
|
|
3388
|
+
default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(active.value.component), normalizeProps(guardReactiveProps(active.value.props ?? {})), null, 16))]),
|
|
3389
|
+
_: 1
|
|
3390
|
+
}, 8, ["title"])])], 512)], 8, _hoisted_1$3)]),
|
|
3391
|
+
_: 1
|
|
3392
|
+
})) : createCommentVNode("", true);
|
|
3393
|
+
};
|
|
3394
|
+
}
|
|
3395
|
+
}), [["__scopeId", "data-v-dffb0514"]]);
|
|
3396
|
+
// src/app/PlaygroundSidebar.vue?vue&type=script&setup=true&lang.ts
|
|
3397
|
+
var _hoisted_1$2 = {
|
|
3398
|
+
class: "pw-sidebar",
|
|
3399
|
+
"aria-label": "组件导航"
|
|
3400
|
+
};
|
|
3401
|
+
var _hoisted_2$2 = { class: "pw-sidebar__brand" };
|
|
3402
|
+
var _hoisted_3$2 = { class: "pw-sidebar__title" };
|
|
3403
|
+
var _hoisted_4$1 = { class: "pw-sidebar__search" };
|
|
3404
|
+
var _hoisted_5$1 = {
|
|
3405
|
+
key: 0,
|
|
3406
|
+
class: "pw-hits"
|
|
3407
|
+
};
|
|
3408
|
+
var _hoisted_6$1 = {
|
|
3409
|
+
key: 0,
|
|
3410
|
+
class: "pw-hits__group"
|
|
3411
|
+
};
|
|
3412
|
+
var _hoisted_7$1 = { class: "pw-hits__label" };
|
|
3413
|
+
var _hoisted_8 = { class: "pw-hits__count" };
|
|
3414
|
+
var _hoisted_9 = ["title", "onClick"];
|
|
3415
|
+
var _hoisted_10 = { class: "pw-hit__title" };
|
|
3416
|
+
var _hoisted_11 = {
|
|
3417
|
+
key: 0,
|
|
3418
|
+
class: "pw-nav__hit"
|
|
3419
|
+
};
|
|
3420
|
+
var _hoisted_12 = { class: "pw-hit__meta" };
|
|
3421
|
+
var _hoisted_13 = {
|
|
3422
|
+
key: 1,
|
|
3423
|
+
class: "pw-hits__group"
|
|
3424
|
+
};
|
|
3425
|
+
var _hoisted_14 = { class: "pw-hits__label" };
|
|
3426
|
+
var _hoisted_15 = { class: "pw-hits__count" };
|
|
3427
|
+
var _hoisted_16 = ["title", "onClick"];
|
|
3428
|
+
var _hoisted_17 = { class: "pw-hit__title" };
|
|
3429
|
+
var _hoisted_18 = {
|
|
3430
|
+
key: 0,
|
|
3431
|
+
class: "pw-nav__hit"
|
|
3432
|
+
};
|
|
3433
|
+
var _hoisted_19 = { class: "pw-hit__meta" };
|
|
3434
|
+
var _hoisted_20 = { class: "pw-hit__meta-text" };
|
|
3435
|
+
var _hoisted_21 = {
|
|
3436
|
+
key: 0,
|
|
3437
|
+
class: "pw-hit__reason"
|
|
3438
|
+
};
|
|
3439
|
+
var _hoisted_22 = {
|
|
3440
|
+
key: 0,
|
|
3441
|
+
class: "pw-hits__more"
|
|
3442
|
+
};
|
|
3443
|
+
var _hoisted_23 = {
|
|
3444
|
+
key: 2,
|
|
3445
|
+
class: "pw-sidebar__empty"
|
|
3446
|
+
};
|
|
3447
|
+
var _hoisted_24 = [
|
|
3448
|
+
"disabled",
|
|
3449
|
+
"aria-expanded",
|
|
3450
|
+
"title",
|
|
3451
|
+
"onClick"
|
|
3452
|
+
];
|
|
3453
|
+
var _hoisted_25 = { class: "pw-nav-group__name" };
|
|
3454
|
+
var _hoisted_26 = {
|
|
3455
|
+
key: 0,
|
|
3456
|
+
class: "pw-nav__hit"
|
|
3457
|
+
};
|
|
3458
|
+
var _hoisted_27 = {
|
|
3459
|
+
key: 0,
|
|
3460
|
+
class: "pw-nav-group__count"
|
|
3461
|
+
};
|
|
3462
|
+
var _hoisted_28 = { class: "pw-nav-group__chevron-box" };
|
|
3463
|
+
var _hoisted_29 = { class: "pw-nav-group__list" };
|
|
3464
|
+
var _hoisted_30 = [
|
|
3465
|
+
"aria-current",
|
|
3466
|
+
"title",
|
|
3467
|
+
"onClick"
|
|
3468
|
+
];
|
|
3469
|
+
var _hoisted_31 = { class: "pw-nav-item__title" };
|
|
3470
|
+
var _hoisted_32 = {
|
|
3471
|
+
key: 0,
|
|
3472
|
+
class: "pw-nav__hit"
|
|
3473
|
+
};
|
|
3474
|
+
var _hoisted_33 = [
|
|
3475
|
+
"aria-expanded",
|
|
3476
|
+
"aria-label",
|
|
3477
|
+
"data-tip",
|
|
3478
|
+
"onClick"
|
|
3479
|
+
];
|
|
3480
|
+
var _hoisted_34 = { class: "pw-nav-children" };
|
|
3481
|
+
var _hoisted_35 = [
|
|
3482
|
+
"aria-current",
|
|
3483
|
+
"title",
|
|
3484
|
+
"onClick"
|
|
3485
|
+
];
|
|
3486
|
+
var _hoisted_36 = { class: "pw-nav-item__title" };
|
|
3487
|
+
var _hoisted_37 = {
|
|
3488
|
+
key: 0,
|
|
3489
|
+
class: "pw-nav__hit"
|
|
3490
|
+
};
|
|
3491
|
+
var _hoisted_38 = {
|
|
3492
|
+
key: 1,
|
|
3493
|
+
class: "pw-sidebar__empty"
|
|
3494
|
+
};
|
|
3495
|
+
var _hoisted_39 = { class: "pw-sidebar__footer" };
|
|
3496
|
+
/** 场景命中一次最多列这么多,剩下的用一句话提示收窄范围 */
|
|
3497
|
+
var SCENE_HIT_LIMIT = 50;
|
|
3498
|
+
// src/app/PlaygroundSidebar.vue
|
|
3499
|
+
var PlaygroundSidebar_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
3500
|
+
__name: "PlaygroundSidebar",
|
|
3501
|
+
props: {
|
|
3502
|
+
title: {},
|
|
3503
|
+
groups: {},
|
|
3504
|
+
activePageId: {},
|
|
3505
|
+
sceneCount: {},
|
|
3506
|
+
scenes: {}
|
|
3507
|
+
},
|
|
3508
|
+
emits: ["select", "selectScene"],
|
|
3509
|
+
setup(__props, { emit: __emit }) {
|
|
3510
|
+
const props = __props;
|
|
3511
|
+
const emit = __emit;
|
|
3512
|
+
const query = ref("");
|
|
3513
|
+
const navRef = ref(null);
|
|
3514
|
+
/** 分组展开状态:默认只展开活动页所在的那个组(没有活动页时展开第一个组) */
|
|
3515
|
+
const expandedGroups = ref(/* @__PURE__ */ new Set());
|
|
3516
|
+
/** 带子组件的组件页展开状态 */
|
|
3517
|
+
const expandedPages = ref(/* @__PURE__ */ new Set());
|
|
3518
|
+
const searching = computed(() => query.value.trim() !== "");
|
|
3519
|
+
const groupOfPage = (id) => props.groups.find((group) => group.pages.some((page) => page.id === id || page.children.some((child) => child.id === id)));
|
|
3520
|
+
const parentOf = (id) => props.groups.flatMap((group) => group.pages).find((page) => page.children.some((child) => child.id === id));
|
|
3521
|
+
/** 打开某一页时,把它所在分组与父组件页一起展开,保证能看见当前页 */
|
|
3522
|
+
const reveal = (id) => {
|
|
3523
|
+
if (!id) return;
|
|
3524
|
+
const self = props.groups.flatMap((group) => group.pages).find((page) => page.id === id);
|
|
3525
|
+
const group = groupOfPage(id);
|
|
3526
|
+
if (group && !expandedGroups.value.has(group.name)) expandedGroups.value = /* @__PURE__ */ new Set([...expandedGroups.value, group.name]);
|
|
3527
|
+
const parent = parentOf(id);
|
|
3528
|
+
if (parent && !expandedPages.value.has(parent.id)) expandedPages.value = /* @__PURE__ */ new Set([...expandedPages.value, parent.id]);
|
|
3529
|
+
if (self && self.children.length && !expandedPages.value.has(self.id)) expandedPages.value = /* @__PURE__ */ new Set([...expandedPages.value, self.id]);
|
|
3530
|
+
};
|
|
3531
|
+
watch(() => props.activePageId, (id) => reveal(id), { immediate: true });
|
|
3532
|
+
onMounted(() => {
|
|
3533
|
+
if (expandedGroups.value.size) return;
|
|
3534
|
+
const first = props.groups[0];
|
|
3535
|
+
if (first) expandedGroups.value = /* @__PURE__ */ new Set([first.name]);
|
|
3536
|
+
});
|
|
3537
|
+
const toggleGroup = (name) => {
|
|
3538
|
+
if (searching.value) return;
|
|
3539
|
+
const next = new Set(expandedGroups.value);
|
|
3540
|
+
if (next.has(name)) next.delete(name);
|
|
3541
|
+
else next.add(name);
|
|
3542
|
+
expandedGroups.value = next;
|
|
3543
|
+
};
|
|
3544
|
+
/** 点父页本身:先进入该页,再有子组件就顺手展开,不必再点右侧箭头 */
|
|
3545
|
+
const openPage = (page) => {
|
|
3546
|
+
emit("select", page.id);
|
|
3547
|
+
if (page.children.length && !expandedPages.value.has(page.id)) togglePage(page.id);
|
|
3548
|
+
};
|
|
3549
|
+
const togglePage = (id) => {
|
|
3550
|
+
const next = new Set(expandedPages.value);
|
|
3551
|
+
if (next.has(id)) next.delete(id);
|
|
3552
|
+
else next.add(id);
|
|
3553
|
+
expandedPages.value = next;
|
|
3554
|
+
};
|
|
3555
|
+
const pageExpanded = (id) => searching.value || expandedPages.value.has(id);
|
|
3556
|
+
const groupExpanded = (name) => searching.value || expandedGroups.value.has(name);
|
|
3557
|
+
/** 父页展开且当前页就是它的子页时,父行也要有“包含当前页”的弱提示 */
|
|
3558
|
+
const pageHasActiveChild = (page) => page.children.some((child) => child.id === props.activePageId);
|
|
3559
|
+
/** 把命中片段切出来,模板里用 <mark> 标出,用户能看懂为什么这一项被搜出来 */
|
|
3560
|
+
const splitMatch = (text) => {
|
|
3561
|
+
const needle = query.value.trim();
|
|
3562
|
+
if (!needle) return [{
|
|
3563
|
+
text,
|
|
3564
|
+
hit: false
|
|
3565
|
+
}];
|
|
3566
|
+
const lower = text.toLowerCase();
|
|
3567
|
+
const target = needle.toLowerCase();
|
|
3568
|
+
const parts = [];
|
|
3569
|
+
let cursor = 0;
|
|
3570
|
+
for (;;) {
|
|
3571
|
+
const index = lower.indexOf(target, cursor);
|
|
3572
|
+
if (index < 0) {
|
|
3573
|
+
if (cursor < text.length) parts.push({
|
|
3574
|
+
text: text.slice(cursor),
|
|
3575
|
+
hit: false
|
|
3576
|
+
});
|
|
3577
|
+
return parts;
|
|
3578
|
+
}
|
|
3579
|
+
if (index > cursor) parts.push({
|
|
3580
|
+
text: text.slice(cursor, index),
|
|
3581
|
+
hit: false
|
|
3582
|
+
});
|
|
3583
|
+
parts.push({
|
|
3584
|
+
text: text.slice(index, index + target.length),
|
|
3585
|
+
hit: true
|
|
3586
|
+
});
|
|
3587
|
+
cursor = index + target.length;
|
|
3588
|
+
}
|
|
3589
|
+
};
|
|
3590
|
+
const groupHasActive = (group) => group.pages.some((page) => page.id === props.activePageId || page.children.some((child) => child.id === props.activePageId));
|
|
3591
|
+
const matches = (node, needle) => node.title.toLowerCase().includes(needle) || node.id.toLowerCase().includes(needle);
|
|
3592
|
+
/** 搜索时把命中的页 / 子页提出来并全部展开;平时按折叠状态渲染 */
|
|
3593
|
+
const filteredGroups = computed(() => {
|
|
3594
|
+
const needle = query.value.trim().toLowerCase();
|
|
3595
|
+
if (!needle) return props.groups;
|
|
3596
|
+
return props.groups.map((group) => {
|
|
3597
|
+
const groupHit = group.name.toLowerCase().includes(needle);
|
|
3598
|
+
const pages = group.pages.map((page) => {
|
|
3599
|
+
const pageHit = groupHit || matches(page, needle);
|
|
3600
|
+
const children = page.children.filter((child) => groupHit || pageHit || matches(child, needle));
|
|
3601
|
+
return pageHit || children.length ? {
|
|
3602
|
+
...page,
|
|
3603
|
+
children
|
|
3604
|
+
} : null;
|
|
3605
|
+
}).filter((page) => page !== null);
|
|
3606
|
+
return pages.length ? {
|
|
3607
|
+
name: group.name,
|
|
3608
|
+
pages
|
|
3609
|
+
} : null;
|
|
3610
|
+
}).filter((group) => group !== null);
|
|
3611
|
+
});
|
|
3612
|
+
const pageCount = computed(() => props.groups.reduce((sum, group) => sum + group.pages.reduce((inner, page) => inner + 1 + page.children.length, 0), 0));
|
|
3613
|
+
const needle = computed(() => query.value.trim().toLowerCase());
|
|
3614
|
+
const hitContains = (text, q) => typeof text === "string" && text.toLowerCase().includes(q);
|
|
3615
|
+
/** 页面命中:页标题 / 页 id / 分组名 */
|
|
3616
|
+
const pageHits = computed(() => {
|
|
3617
|
+
const q = needle.value;
|
|
3618
|
+
if (!q) return [];
|
|
3619
|
+
return props.groups.flatMap((group) => group.pages.filter((page) => hitContains(page.title, q) || hitContains(page.id, q) || hitContains(group.name, q)).map((page) => ({
|
|
3620
|
+
key: `page:${page.id}`,
|
|
3621
|
+
kind: "page",
|
|
3622
|
+
title: page.title,
|
|
3623
|
+
meta: `${group.name} · ${page.count + page.children.reduce((sum, child) => sum + child.count, 0)} 个场景`,
|
|
3624
|
+
pageId: page.id
|
|
3625
|
+
})));
|
|
3626
|
+
});
|
|
3627
|
+
/** 场景命中:场景标题 / 文件名 / 路径 / 说明 / 标签 / 覆盖的 API 名;隐藏场景不参与 */
|
|
3628
|
+
const sceneHitList = computed(() => {
|
|
3629
|
+
const q = needle.value;
|
|
3630
|
+
if (!q) return [];
|
|
3631
|
+
return props.scenes.filter((scene) => !scene.hidden).filter((scene) => hitContains(scene.title, q) || hitContains(scene.name, q) || hitContains(scene.rel, q) || hitContains(scene.description, q) || (scene.api ?? []).some((name) => hitContains(name, q)) || (scene.tags ?? []).some((tag) => hitContains(tag, q))).map((scene) => {
|
|
3632
|
+
const apiHit = (scene.api ?? []).find((name) => hitContains(name, q));
|
|
3633
|
+
return {
|
|
3634
|
+
key: `scene:${scene.id}`,
|
|
3635
|
+
kind: "scene",
|
|
3636
|
+
title: scene.title,
|
|
3637
|
+
meta: `${scene.group} · ${scene.page}`,
|
|
3638
|
+
pageId: scene.pageId,
|
|
3639
|
+
sceneName: scene.name,
|
|
3640
|
+
reason: apiHit ? `覆盖 ${apiHit}` : void 0
|
|
3641
|
+
};
|
|
3642
|
+
});
|
|
3643
|
+
});
|
|
3644
|
+
const sceneHits = computed(() => sceneHitList.value.slice(0, SCENE_HIT_LIMIT));
|
|
3645
|
+
const sceneHitRest = computed(() => Math.max(0, sceneHitList.value.length - sceneHits.value.length));
|
|
3646
|
+
/** 点命中项:页面直接切页,场景交给 App 切页后再滚到那一个场景 */
|
|
3647
|
+
const openHit = (hit) => {
|
|
3648
|
+
if (hit.sceneName) emit("selectScene", hit.pageId, hit.sceneName);
|
|
3649
|
+
else emit("select", hit.pageId);
|
|
3650
|
+
query.value = "";
|
|
3651
|
+
};
|
|
3652
|
+
const visibleItems = () => [...navRef.value?.querySelectorAll(".pw-nav-item, .pw-hit") ?? []].filter((el) => el.offsetParent !== null);
|
|
3653
|
+
/** 搜索框:Enter 直接进第一条命中,Esc 先清空再退出 */
|
|
3654
|
+
const onSearchKeydown = (event) => {
|
|
3655
|
+
if (event.key === "Enter") {
|
|
3656
|
+
const first = visibleItems()[0];
|
|
3657
|
+
if (first) {
|
|
3658
|
+
event.preventDefault();
|
|
3659
|
+
first.click();
|
|
3660
|
+
}
|
|
3661
|
+
return;
|
|
3662
|
+
}
|
|
3663
|
+
if (event.key !== "Escape") return;
|
|
3664
|
+
if (query.value) {
|
|
3665
|
+
query.value = "";
|
|
3666
|
+
event.preventDefault();
|
|
3667
|
+
return;
|
|
3668
|
+
}
|
|
3669
|
+
event.target?.blur();
|
|
3670
|
+
};
|
|
3671
|
+
/** 当前页切换、分组展开后,把它滚进侧栏视口(长列表时不会“高亮在屏幕外”) */
|
|
3672
|
+
watch([
|
|
3673
|
+
() => props.activePageId,
|
|
3674
|
+
expandedGroups,
|
|
3675
|
+
expandedPages,
|
|
3676
|
+
query
|
|
3677
|
+
], async () => {
|
|
3678
|
+
await nextTick();
|
|
3679
|
+
navRef.value?.querySelector(".pw-nav-item.is-active")?.scrollIntoView({ block: "nearest" });
|
|
3680
|
+
}, { flush: "post" });
|
|
3681
|
+
return (_ctx, _cache) => {
|
|
3682
|
+
return openBlock(), createElementBlock("aside", _hoisted_1$2, [
|
|
3683
|
+
createElementVNode("div", _hoisted_2$2, [_cache[1] || (_cache[1] = createElementVNode("span", {
|
|
3684
|
+
class: "pw-sidebar__logo",
|
|
3685
|
+
"aria-hidden": "true"
|
|
3686
|
+
}, null, -1)), createElementVNode("span", _hoisted_3$2, toDisplayString(__props.title), 1)]),
|
|
3687
|
+
createElementVNode("div", _hoisted_4$1, [createVNode(PwInput_default, {
|
|
3688
|
+
modelValue: query.value,
|
|
3689
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => query.value = $event),
|
|
3690
|
+
size: "sm",
|
|
3691
|
+
placeholder: "搜索页面 / 场景",
|
|
3692
|
+
onKeydown: onSearchKeydown
|
|
3693
|
+
}, {
|
|
3694
|
+
prefix: withCtx(() => [createVNode(unref(Search), {
|
|
3695
|
+
size: 13,
|
|
3696
|
+
class: "pw-sidebar__search-icon"
|
|
3697
|
+
})]),
|
|
3698
|
+
_: 1
|
|
3699
|
+
}, 8, ["modelValue"])]),
|
|
3700
|
+
createElementVNode("nav", {
|
|
3701
|
+
ref_key: "navRef",
|
|
3702
|
+
ref: navRef,
|
|
3703
|
+
class: "pw-sidebar__nav",
|
|
3704
|
+
"aria-label": "组件页列表"
|
|
3705
|
+
}, [
|
|
3706
|
+
searching.value ? (openBlock(), createElementBlock("div", _hoisted_5$1, [
|
|
3707
|
+
pageHits.value.length ? (openBlock(), createElementBlock("section", _hoisted_6$1, [createElementVNode("p", _hoisted_7$1, [_cache[2] || (_cache[2] = createTextVNode("页面", -1)), createElementVNode("span", _hoisted_8, toDisplayString(pageHits.value.length), 1)]), (openBlock(true), createElementBlock(Fragment, null, renderList(pageHits.value, (hit) => {
|
|
3708
|
+
return openBlock(), createElementBlock("button", {
|
|
3709
|
+
key: hit.key,
|
|
3710
|
+
type: "button",
|
|
3711
|
+
class: "pw-hit",
|
|
3712
|
+
title: `${hit.meta} · ${hit.title}`,
|
|
3713
|
+
onClick: ($event) => openHit(hit)
|
|
3714
|
+
}, [createElementVNode("span", _hoisted_10, [(openBlock(true), createElementBlock(Fragment, null, renderList(splitMatch(hit.title), (part, index) => {
|
|
3715
|
+
return openBlock(), createElementBlock(Fragment, { key: index }, [part.hit ? (openBlock(), createElementBlock("mark", _hoisted_11, toDisplayString(part.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
3716
|
+
}), 128))]), createElementVNode("span", _hoisted_12, toDisplayString(hit.meta), 1)], 8, _hoisted_9);
|
|
3717
|
+
}), 128))])) : createCommentVNode("", true),
|
|
3718
|
+
sceneHits.value.length ? (openBlock(), createElementBlock("section", _hoisted_13, [
|
|
3719
|
+
createElementVNode("p", _hoisted_14, [_cache[3] || (_cache[3] = createTextVNode("场景", -1)), createElementVNode("span", _hoisted_15, toDisplayString(sceneHitList.value.length), 1)]),
|
|
3720
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(sceneHits.value, (hit) => {
|
|
3721
|
+
return openBlock(), createElementBlock("button", {
|
|
3722
|
+
key: hit.key,
|
|
3723
|
+
type: "button",
|
|
3724
|
+
class: "pw-hit",
|
|
3725
|
+
title: `${hit.meta} · ${hit.title}${hit.reason ? ` · ${hit.reason}` : ""}`,
|
|
3726
|
+
onClick: ($event) => openHit(hit)
|
|
3727
|
+
}, [createElementVNode("span", _hoisted_17, [(openBlock(true), createElementBlock(Fragment, null, renderList(splitMatch(hit.title), (part, index) => {
|
|
3728
|
+
return openBlock(), createElementBlock(Fragment, { key: index }, [part.hit ? (openBlock(), createElementBlock("mark", _hoisted_18, toDisplayString(part.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
3729
|
+
}), 128))]), createElementVNode("span", _hoisted_19, [createElementVNode("span", _hoisted_20, toDisplayString(hit.meta), 1), hit.reason ? (openBlock(), createElementBlock("em", _hoisted_21, toDisplayString(hit.reason), 1)) : createCommentVNode("", true)])], 8, _hoisted_16);
|
|
3730
|
+
}), 128)),
|
|
3731
|
+
sceneHitRest.value ? (openBlock(), createElementBlock("p", _hoisted_22, "还有 " + toDisplayString(sceneHitRest.value) + " 条场景命中,多输几个字缩小范围", 1)) : createCommentVNode("", true)
|
|
3732
|
+
])) : createCommentVNode("", true),
|
|
3733
|
+
!pageHits.value.length && !sceneHits.value.length ? (openBlock(), createElementBlock("p", _hoisted_23, [
|
|
3734
|
+
createTextVNode(" 没有匹配「" + toDisplayString(query.value.trim()) + "」", 1),
|
|
3735
|
+
_cache[4] || (_cache[4] = createElementVNode("kbd", { class: "pw-sidebar__kbd" }, "Esc", -1)),
|
|
3736
|
+
_cache[5] || (_cache[5] = createTextVNode(" 清空 ", -1))
|
|
3737
|
+
])) : createCommentVNode("", true)
|
|
3738
|
+
])) : createCommentVNode("", true),
|
|
3739
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(filteredGroups.value, (group) => {
|
|
3740
|
+
return withDirectives((openBlock(), createElementBlock("section", {
|
|
3741
|
+
key: group.name,
|
|
3742
|
+
class: "pw-nav-group"
|
|
3743
|
+
}, [createElementVNode("button", {
|
|
3744
|
+
type: "button",
|
|
3745
|
+
class: normalizeClass(["pw-nav-group__title", { "has-active": !groupExpanded(group.name) && groupHasActive(group) }]),
|
|
3746
|
+
disabled: searching.value,
|
|
3747
|
+
"aria-expanded": groupExpanded(group.name),
|
|
3748
|
+
title: searching.value ? `${group.name}(搜索中)` : groupExpanded(group.name) ? `收起 ${group.name}` : `展开 ${group.name}`,
|
|
3749
|
+
onClick: ($event) => toggleGroup(group.name)
|
|
3750
|
+
}, [
|
|
3751
|
+
createElementVNode("span", _hoisted_25, [(openBlock(true), createElementBlock(Fragment, null, renderList(splitMatch(group.name), (part, index) => {
|
|
3752
|
+
return openBlock(), createElementBlock(Fragment, { key: index }, [part.hit ? (openBlock(), createElementBlock("mark", _hoisted_26, toDisplayString(part.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
3753
|
+
}), 128))]),
|
|
3754
|
+
!groupExpanded(group.name) ? (openBlock(), createElementBlock("span", _hoisted_27, toDisplayString(group.pages.length), 1)) : createCommentVNode("", true),
|
|
3755
|
+
createElementVNode("span", _hoisted_28, [createVNode(unref(ChevronRight), {
|
|
3756
|
+
size: 12,
|
|
3757
|
+
class: normalizeClass(["pw-nav-group__chevron", { "is-open": groupExpanded(group.name) }])
|
|
3758
|
+
}, null, 8, ["class"])])
|
|
3759
|
+
], 10, _hoisted_24), withDirectives(createElementVNode("div", _hoisted_29, [(openBlock(true), createElementBlock(Fragment, null, renderList(group.pages, (page) => {
|
|
3760
|
+
return openBlock(), createElementBlock(Fragment, { key: page.id }, [createElementVNode("div", { class: normalizeClass(["pw-nav-row", { "is-branch-active": pageHasActiveChild(page) }]) }, [createElementVNode("button", {
|
|
3761
|
+
type: "button",
|
|
3762
|
+
class: normalizeClass(["pw-nav-item", {
|
|
3763
|
+
"is-active": page.id === __props.activePageId,
|
|
3764
|
+
"has-children": page.children.length
|
|
3765
|
+
}]),
|
|
3766
|
+
"aria-current": page.id === __props.activePageId ? "page" : void 0,
|
|
3767
|
+
title: `${page.title} · ${page.count} 个场景`,
|
|
3768
|
+
onClick: ($event) => openPage(page)
|
|
3769
|
+
}, [createElementVNode("span", _hoisted_31, [(openBlock(true), createElementBlock(Fragment, null, renderList(splitMatch(page.title), (part, index) => {
|
|
3770
|
+
return openBlock(), createElementBlock(Fragment, { key: index }, [part.hit ? (openBlock(), createElementBlock("mark", _hoisted_32, toDisplayString(part.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
3771
|
+
}), 128))])], 10, _hoisted_30), page.children.length ? (openBlock(), createElementBlock("button", {
|
|
3772
|
+
key: 0,
|
|
3773
|
+
type: "button",
|
|
3774
|
+
class: "pw-nav-toggle",
|
|
3775
|
+
"aria-expanded": pageExpanded(page.id),
|
|
3776
|
+
"aria-label": `${pageExpanded(page.id) ? "收起" : "展开"} ${page.title} 的子组件`,
|
|
3777
|
+
"data-tip": pageExpanded(page.id) ? `收起 ${page.title} 的子组件` : `展开 ${page.title} 的子组件`,
|
|
3778
|
+
onClick: ($event) => togglePage(page.id)
|
|
3779
|
+
}, [createVNode(unref(ChevronRight), {
|
|
3780
|
+
size: 12,
|
|
3781
|
+
class: normalizeClass({ "is-open": pageExpanded(page.id) })
|
|
3782
|
+
}, null, 8, ["class"])], 8, _hoisted_33)) : createCommentVNode("", true)], 2), withDirectives(createElementVNode("div", _hoisted_34, [(openBlock(true), createElementBlock(Fragment, null, renderList(page.children, (child) => {
|
|
3783
|
+
return openBlock(), createElementBlock("button", {
|
|
3784
|
+
key: child.id,
|
|
3785
|
+
type: "button",
|
|
3786
|
+
class: normalizeClass(["pw-nav-item is-child", { "is-active": child.id === __props.activePageId }]),
|
|
3787
|
+
"aria-current": child.id === __props.activePageId ? "page" : void 0,
|
|
3788
|
+
title: `${child.title} · ${child.count} 个场景`,
|
|
3789
|
+
onClick: ($event) => emit("select", child.id)
|
|
3790
|
+
}, [createElementVNode("span", _hoisted_36, [(openBlock(true), createElementBlock(Fragment, null, renderList(splitMatch(child.title), (part, index) => {
|
|
3791
|
+
return openBlock(), createElementBlock(Fragment, { key: index }, [part.hit ? (openBlock(), createElementBlock("mark", _hoisted_37, toDisplayString(part.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
3792
|
+
}), 128))])], 10, _hoisted_35);
|
|
3793
|
+
}), 128))], 512), [[vShow, pageExpanded(page.id)]])], 64);
|
|
3794
|
+
}), 128))], 512), [[vShow, groupExpanded(group.name)]])])), [[vShow, !searching.value]]);
|
|
3795
|
+
}), 128)),
|
|
3796
|
+
!searching.value && !filteredGroups.value.length ? (openBlock(), createElementBlock("p", _hoisted_38, [..._cache[6] || (_cache[6] = [
|
|
3797
|
+
createTextVNode("没有匹配的页面", -1),
|
|
3798
|
+
createElementVNode("kbd", { class: "pw-sidebar__kbd" }, "Esc", -1),
|
|
3799
|
+
createTextVNode(" 清空", -1)
|
|
3800
|
+
])])) : createCommentVNode("", true)
|
|
3801
|
+
], 512),
|
|
3802
|
+
createElementVNode("footer", _hoisted_39, [createVNode(PlaygroundPluginSlot_default, {
|
|
3803
|
+
entries: unref(pluginFooterSlots),
|
|
3804
|
+
slot: "sidebarFooter"
|
|
3805
|
+
}, null, 8, ["entries"]), createElementVNode("span", null, toDisplayString(pageCount.value) + " 个页面 · " + toDisplayString(__props.sceneCount) + " 个场景", 1)])
|
|
3806
|
+
]);
|
|
3807
|
+
};
|
|
3808
|
+
}
|
|
3809
|
+
}), [["__scopeId", "data-v-e1a77ffa"]]);
|
|
3810
|
+
// src/app/App.vue?vue&type=script&setup=true&lang.ts
|
|
3811
|
+
var _hoisted_1$1 = { class: "pw-main" };
|
|
3812
|
+
var _hoisted_2$1 = { class: "pw-content" };
|
|
3813
|
+
var _hoisted_3$1 = {
|
|
3814
|
+
key: 1,
|
|
3815
|
+
class: "pw-empty"
|
|
3816
|
+
};
|
|
3817
|
+
var STORAGE_KEY = "rinova-playground:ui-theme";
|
|
3818
|
+
var SIDEBAR_KEY = "rinova-playground:sidebar-open";
|
|
3819
|
+
var VIEWPORT_KEY = "rinova-playground:viewport";
|
|
3820
|
+
// src/app/App.vue
|
|
3821
|
+
var App_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
3822
|
+
__name: "App",
|
|
3823
|
+
setup(__props) {
|
|
3824
|
+
const title = config.title ?? "Playground";
|
|
3825
|
+
const isPage = (value) => value.group !== "";
|
|
3826
|
+
/** 每个页下的场景(hidden 不进索引与侧栏,但锚点仍可直达) */
|
|
3827
|
+
const scenesOfPage = (pageId) => scenes.filter((scene) => scene.pageId === pageId);
|
|
3828
|
+
/** 侧栏计数与页内索引只算可见场景 */
|
|
3829
|
+
const visibleScenesOfPage = (pageId) => scenesOfPage(pageId).filter((scene) => !scene.hidden);
|
|
3830
|
+
const pageGroups = computed(() => {
|
|
3831
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3832
|
+
for (const page of pages.filter(isPage)) {
|
|
3833
|
+
let group = groups.get(page.group);
|
|
3834
|
+
if (!group) {
|
|
3835
|
+
group = {
|
|
3836
|
+
name: page.group,
|
|
3837
|
+
groupOrder: page.groupOrder ?? Number.MAX_SAFE_INTEGER,
|
|
3838
|
+
pages: [],
|
|
3839
|
+
nodes: /* @__PURE__ */ new Map()
|
|
3840
|
+
};
|
|
3841
|
+
groups.set(page.group, group);
|
|
3842
|
+
}
|
|
3843
|
+
const node = {
|
|
3844
|
+
id: page.id,
|
|
3845
|
+
title: page.title,
|
|
3846
|
+
count: visibleScenesOfPage(page.id).length,
|
|
3847
|
+
depth: page.depth ?? 1,
|
|
3848
|
+
children: []
|
|
3849
|
+
};
|
|
3850
|
+
group.nodes.set(page.id, node);
|
|
3851
|
+
const parent = page.parentId ? group.nodes.get(page.parentId) : void 0;
|
|
3852
|
+
if (parent) parent.children.push(node);
|
|
3853
|
+
else group.pages.push(node);
|
|
3854
|
+
}
|
|
3855
|
+
const prune = (node) => {
|
|
3856
|
+
node.children = node.children.map(prune).filter((child) => child !== null);
|
|
3857
|
+
if (node.count === 0 && node.children.length === 0) return null;
|
|
3858
|
+
return node;
|
|
3859
|
+
};
|
|
3860
|
+
return [...groups.values()].sort((a, b) => a.groupOrder - b.groupOrder).map((group) => ({
|
|
3861
|
+
name: group.name,
|
|
3862
|
+
pages: group.pages.map(prune).filter((page) => page !== null)
|
|
3863
|
+
})).filter((group) => group.pages.length > 0);
|
|
3864
|
+
});
|
|
3865
|
+
const visibleCount = computed(() => scenes.filter((scene) => !scene.hidden).length);
|
|
3866
|
+
const firstPageId = computed(() => pages.filter(isPage).find((page) => visibleScenesOfPage(page.id).length > 0)?.id ?? "");
|
|
3867
|
+
const activePageId = ref(firstPageId.value);
|
|
3868
|
+
const activePage = computed(() => pages.find((page) => page.id === activePageId.value) ?? pages.filter(isPage)[0]);
|
|
3869
|
+
const activeScenes = computed(() => activePage.value ? scenesOfPage(activePage.value.id) : []);
|
|
3870
|
+
const activeSceneName = ref(activeScenes.value[0]?.name ?? "");
|
|
3871
|
+
computed(() => activeScenes.value.find((scene) => scene.name === activeSceneName.value) ?? activeScenes.value[0]);
|
|
3872
|
+
const pageRef = ref(null);
|
|
3873
|
+
/**
|
|
3874
|
+
* 从全局搜索点一个场景:同页直接滚过去;跨页先记下要落到的场景,
|
|
3875
|
+
* 切页的 watch 会把它当作初始场景,页面挂载后再由 PlaygroundPage 滚到位置。
|
|
3876
|
+
*/
|
|
3877
|
+
const pendingScene = ref("");
|
|
3878
|
+
const jumpToScene = (pageId, sceneName) => {
|
|
3879
|
+
if (pageId === activePageId.value) {
|
|
3880
|
+
pageRef.value?.jumpTo(sceneName);
|
|
3881
|
+
return;
|
|
3882
|
+
}
|
|
3883
|
+
pendingScene.value = sceneName;
|
|
3884
|
+
activePageId.value = pageId;
|
|
3885
|
+
};
|
|
3886
|
+
/** 左侧栏展开 / 收起,存 localStorage */
|
|
3887
|
+
const readSidebarOpen = () => localStorage.getItem(SIDEBAR_KEY) !== "0";
|
|
3888
|
+
const sidebarOpen = ref(readSidebarOpen());
|
|
3889
|
+
const toggleSidebar = () => {
|
|
3890
|
+
sidebarOpen.value = !sidebarOpen.value;
|
|
3891
|
+
localStorage.setItem(SIDEBAR_KEY, sidebarOpen.value ? "1" : "0");
|
|
3892
|
+
};
|
|
3893
|
+
/** 预览宽度:预设 + 自定义,写 localStorage,刷新后保持 */
|
|
3894
|
+
const readViewport = () => {
|
|
3895
|
+
try {
|
|
3896
|
+
const raw = JSON.parse(localStorage.getItem(VIEWPORT_KEY) ?? "null");
|
|
3897
|
+
if (!raw || typeof raw.id !== "string") return { ...DEFAULT_VIEWPORT };
|
|
3898
|
+
return {
|
|
3899
|
+
id: raw.id,
|
|
3900
|
+
px: Number(raw.px) || 0
|
|
3901
|
+
};
|
|
3902
|
+
} catch {
|
|
3903
|
+
return { ...DEFAULT_VIEWPORT };
|
|
3904
|
+
}
|
|
3905
|
+
};
|
|
3906
|
+
const viewport = ref(readViewport());
|
|
3907
|
+
const setViewport = (next) => {
|
|
3908
|
+
viewport.value = next;
|
|
3909
|
+
localStorage.setItem(VIEWPORT_KEY, JSON.stringify(next));
|
|
3910
|
+
};
|
|
3911
|
+
const readStoredTheme = () => {
|
|
3912
|
+
const value = localStorage.getItem(STORAGE_KEY);
|
|
3913
|
+
return value === "light" || value === "dark" ? value : null;
|
|
3914
|
+
};
|
|
3915
|
+
const preference = ref(readStoredTheme() ?? config.theme?.ui ?? "auto");
|
|
3916
|
+
const systemDark = ref(false);
|
|
3917
|
+
const theme = computed(() => preference.value === "auto" ? systemDark.value ? "dark" : "light" : preference.value);
|
|
3918
|
+
/** 插件 UI 跟随外壳主题:给它们一个 computed,而不是让它们猜 DOM */
|
|
3919
|
+
providePlaygroundTheme(theme);
|
|
3920
|
+
watchEffect(() => {
|
|
3921
|
+
document.documentElement.dataset.pwTheme = theme.value;
|
|
3922
|
+
});
|
|
3923
|
+
const applyTheme = (next) => {
|
|
3924
|
+
preference.value = next;
|
|
3925
|
+
localStorage.setItem(STORAGE_KEY, next);
|
|
3926
|
+
};
|
|
3927
|
+
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
3928
|
+
/** 浅色/深色切换:从按钮位置揭开式过渡(View Transitions),降级时不打断交互 */
|
|
3929
|
+
const toggleTheme = (event) => {
|
|
3930
|
+
const next = theme.value === "dark" ? "light" : "dark";
|
|
3931
|
+
const doc = document;
|
|
3932
|
+
if (!event || event.detail === 0 || !doc.startViewTransition || reduceMotion.matches) {
|
|
3933
|
+
applyTheme(next);
|
|
3934
|
+
return;
|
|
3935
|
+
}
|
|
3936
|
+
const x = event.clientX;
|
|
3937
|
+
const y = event.clientY;
|
|
3938
|
+
const radius = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y));
|
|
3939
|
+
doc.startViewTransition(async () => {
|
|
3940
|
+
applyTheme(next);
|
|
3941
|
+
await nextTick();
|
|
3942
|
+
}).ready.then(() => {
|
|
3943
|
+
document.documentElement.animate({ clipPath: [`circle(0px at ${x}px ${y}px)`, `circle(${radius}px at ${x}px ${y}px)`] }, {
|
|
3944
|
+
duration: 420,
|
|
3945
|
+
easing: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
3946
|
+
pseudoElement: "::view-transition-new(root)"
|
|
3947
|
+
});
|
|
3948
|
+
}).catch(() => {});
|
|
3949
|
+
};
|
|
3950
|
+
const basePath = (() => {
|
|
3951
|
+
const raw = config.routeBase ?? config.base ?? "/";
|
|
3952
|
+
return raw.endsWith("/") ? raw : `${raw}/`;
|
|
3953
|
+
})();
|
|
3954
|
+
const pathSegments = () => {
|
|
3955
|
+
const pathname = decodeURIComponent(window.location.pathname);
|
|
3956
|
+
return (pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname.replace(/^\//, "")).replace(/\/+$/, "");
|
|
3957
|
+
};
|
|
3958
|
+
const hashName = () => decodeURIComponent(window.location.hash.replace(/^#/, ""));
|
|
3959
|
+
const readLocation = () => {
|
|
3960
|
+
const pageId = pathSegments();
|
|
3961
|
+
if (pageId && pages.some((page) => page.id === pageId)) activePageId.value = pageId;
|
|
3962
|
+
const name = hashName();
|
|
3963
|
+
if (name && activeScenes.value.some((scene) => scene.name === name)) activeSceneName.value = name;
|
|
3964
|
+
};
|
|
3965
|
+
const writeLocation = () => {
|
|
3966
|
+
const pageId = activePageId.value;
|
|
3967
|
+
if (!pageId) return;
|
|
3968
|
+
const target = `${basePath}${pageId}`;
|
|
3969
|
+
if (decodeURIComponent(window.location.pathname) !== target) window.history.pushState(null, "", target);
|
|
3970
|
+
};
|
|
3971
|
+
/** 切换页时重置到该页第一个场景(从搜索跳进来时落到指定场景) */
|
|
3972
|
+
watch(activePageId, (id) => {
|
|
3973
|
+
writeLocation();
|
|
3974
|
+
const list = scenesOfPage(id);
|
|
3975
|
+
const target = pendingScene.value && list.some((scene) => scene.name === pendingScene.value) ? pendingScene.value : "";
|
|
3976
|
+
pendingScene.value = "";
|
|
3977
|
+
activeSceneName.value = target || list[0]?.name || "";
|
|
3978
|
+
window.history.replaceState(null, "", `${basePath}${id}`);
|
|
3979
|
+
});
|
|
3980
|
+
const focusScene = (name) => {
|
|
3981
|
+
activeSceneName.value = name;
|
|
3982
|
+
window.history.replaceState(null, "", `${basePath}${activePageId.value}#${name}`);
|
|
3983
|
+
};
|
|
3984
|
+
const media = window.matchMedia("(prefers-color-scheme: dark)");
|
|
3985
|
+
const onMediaChange = (event) => {
|
|
3986
|
+
systemDark.value = event.matches;
|
|
3987
|
+
};
|
|
3988
|
+
onMounted(() => {
|
|
3989
|
+
document.title = title;
|
|
3990
|
+
systemDark.value = media.matches;
|
|
3991
|
+
media.addEventListener("change", onMediaChange);
|
|
3992
|
+
readLocation();
|
|
3993
|
+
if (activePageId.value) window.history.replaceState(null, "", `${basePath}${activePageId.value}${window.location.hash}`);
|
|
3994
|
+
window.addEventListener("popstate", readLocation);
|
|
3995
|
+
window.addEventListener("hashchange", readLocation);
|
|
3996
|
+
});
|
|
3997
|
+
onBeforeUnmount(() => {
|
|
3998
|
+
media.removeEventListener("change", onMediaChange);
|
|
3999
|
+
window.removeEventListener("popstate", readLocation);
|
|
4000
|
+
window.removeEventListener("hashchange", readLocation);
|
|
4001
|
+
});
|
|
4002
|
+
return (_ctx, _cache) => {
|
|
4003
|
+
return openBlock(), createElementBlock("div", { class: normalizeClass(["pw-app", { "is-sidebar-collapsed": !sidebarOpen.value }]) }, [
|
|
4004
|
+
withDirectives(createVNode(PlaygroundSidebar_default, {
|
|
4005
|
+
title: unref(title),
|
|
4006
|
+
groups: pageGroups.value,
|
|
4007
|
+
scenes: unref(scenes),
|
|
4008
|
+
"active-page-id": activePageId.value,
|
|
4009
|
+
"scene-count": visibleCount.value,
|
|
4010
|
+
onSelect: _cache[0] || (_cache[0] = ($event) => activePageId.value = $event),
|
|
4011
|
+
onSelectScene: jumpToScene
|
|
4012
|
+
}, null, 8, [
|
|
4013
|
+
"title",
|
|
4014
|
+
"groups",
|
|
4015
|
+
"scenes",
|
|
4016
|
+
"active-page-id",
|
|
4017
|
+
"scene-count"
|
|
4018
|
+
]), [[vShow, sidebarOpen.value]]),
|
|
4019
|
+
createElementVNode("main", _hoisted_1$1, [createElementVNode("div", _hoisted_2$1, [activePage.value ? (openBlock(), createBlock(PlaygroundPage_default, {
|
|
4020
|
+
key: activePage.value.id,
|
|
4021
|
+
ref_key: "pageRef",
|
|
4022
|
+
ref: pageRef,
|
|
4023
|
+
page: activePage.value,
|
|
4024
|
+
scenes: activeScenes.value,
|
|
4025
|
+
"initial-active": activeSceneName.value,
|
|
4026
|
+
viewport: viewport.value,
|
|
4027
|
+
theme: theme.value,
|
|
4028
|
+
"sidebar-open": sidebarOpen.value,
|
|
4029
|
+
"onUpdate:viewport": setViewport,
|
|
4030
|
+
onCollapseSidebar: toggleSidebar,
|
|
4031
|
+
"onUpdate:active": _cache[1] || (_cache[1] = ($event) => activeSceneName.value = $event),
|
|
4032
|
+
onToggleTheme: toggleTheme,
|
|
4033
|
+
onToggleSidebar: toggleSidebar,
|
|
4034
|
+
onFocus: focusScene
|
|
4035
|
+
}, null, 8, [
|
|
4036
|
+
"page",
|
|
4037
|
+
"scenes",
|
|
4038
|
+
"initial-active",
|
|
4039
|
+
"viewport",
|
|
4040
|
+
"theme",
|
|
4041
|
+
"sidebar-open"
|
|
4042
|
+
])) : (openBlock(), createElementBlock("div", _hoisted_3$1, [..._cache[2] || (_cache[2] = [createTextVNode(" 没有找到任何场景。目录约定:", -1), createElementVNode("code", null, "src/scenes/<分组>/<组件页>/<场景>.vue", -1)])]))])]),
|
|
4043
|
+
createVNode(PwToastHost_default),
|
|
4044
|
+
createVNode(PwTooltip_default),
|
|
4045
|
+
createVNode(PlaygroundPluginPanel_default)
|
|
4046
|
+
], 2);
|
|
4047
|
+
};
|
|
4048
|
+
}
|
|
4049
|
+
}), [["__scopeId", "data-v-395342d2"]]);
|
|
4050
|
+
// src/app/PlaygroundRoot.vue?vue&type=script&setup=true&lang.ts
|
|
4051
|
+
var _hoisted_1 = ["data-pw-plugin-state"];
|
|
4052
|
+
var _hoisted_2 = { class: "pw-boot__brand" };
|
|
4053
|
+
var _hoisted_3 = { class: "pw-boot__title" };
|
|
4054
|
+
var _hoisted_4 = {
|
|
4055
|
+
key: 0,
|
|
4056
|
+
class: "pw-boot__hint"
|
|
4057
|
+
};
|
|
4058
|
+
var _hoisted_5 = {
|
|
4059
|
+
key: 1,
|
|
4060
|
+
class: "pw-boot__error",
|
|
4061
|
+
role: "alert"
|
|
4062
|
+
};
|
|
4063
|
+
var _hoisted_6 = { class: "pw-boot__message" };
|
|
4064
|
+
var _hoisted_7 = {
|
|
4065
|
+
key: 0,
|
|
4066
|
+
class: "pw-boot__stack"
|
|
4067
|
+
};
|
|
4068
|
+
// src/app/PlaygroundRoot.vue
|
|
4069
|
+
var PlaygroundRoot_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
4070
|
+
__name: "PlaygroundRoot",
|
|
4071
|
+
setup(__props) {
|
|
4072
|
+
const title = config.title ?? "Playground";
|
|
4073
|
+
const state = ref("loading");
|
|
4074
|
+
const message = ref("");
|
|
4075
|
+
const stack = ref("");
|
|
4076
|
+
onBeforeMount(async () => {
|
|
4077
|
+
document.title = title;
|
|
4078
|
+
if (!pluginModules.length) {
|
|
4079
|
+
state.value = "ready";
|
|
4080
|
+
return;
|
|
4081
|
+
}
|
|
4082
|
+
try {
|
|
4083
|
+
const app = getCurrentInstance()?.appContext.app;
|
|
4084
|
+
if (!app) throw new Error("拿不到 App 实例");
|
|
4085
|
+
await runPlugins({
|
|
4086
|
+
app,
|
|
4087
|
+
config,
|
|
4088
|
+
scenes,
|
|
4089
|
+
modules: pluginModules
|
|
4090
|
+
});
|
|
4091
|
+
state.value = "ready";
|
|
4092
|
+
} catch (error) {
|
|
4093
|
+
message.value = error instanceof Error ? error.message : String(error);
|
|
4094
|
+
stack.value = error instanceof Error ? error.stack ?? "" : "";
|
|
4095
|
+
state.value = "error";
|
|
4096
|
+
}
|
|
4097
|
+
});
|
|
4098
|
+
return (_ctx, _cache) => {
|
|
4099
|
+
return state.value === "ready" ? (openBlock(), createBlock(App_default, { key: 0 })) : (openBlock(), createElementBlock("div", {
|
|
4100
|
+
key: 1,
|
|
4101
|
+
class: "pw-boot",
|
|
4102
|
+
"data-pw-plugin-state": state.value
|
|
4103
|
+
}, [createElementVNode("div", _hoisted_2, [_cache[0] || (_cache[0] = createElementVNode("span", {
|
|
4104
|
+
class: "pw-boot__logo",
|
|
4105
|
+
"aria-hidden": "true"
|
|
4106
|
+
}, null, -1)), createElementVNode("span", _hoisted_3, toDisplayString(unref(title)), 1)]), state.value === "loading" ? (openBlock(), createElementBlock("p", _hoisted_4, "正在初始化演示环境…")) : (openBlock(), createElementBlock("div", _hoisted_5, [
|
|
4107
|
+
_cache[2] || (_cache[2] = createElementVNode("p", { class: "pw-boot__head" }, "演示环境初始化失败", -1)),
|
|
4108
|
+
createElementVNode("p", _hoisted_6, toDisplayString(message.value), 1),
|
|
4109
|
+
stack.value ? (openBlock(), createElementBlock("details", _hoisted_7, [_cache[1] || (_cache[1] = createElementVNode("summary", null, "调用栈", -1)), createElementVNode("pre", null, toDisplayString(stack.value), 1)])) : createCommentVNode("", true)
|
|
4110
|
+
]))], 8, _hoisted_1));
|
|
4111
|
+
};
|
|
4112
|
+
}
|
|
4113
|
+
}), [["__scopeId", "data-v-44a22d03"]]);
|
|
4114
|
+
export { PlaygroundRoot_default as default };
|