@rinova/playground 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -89,6 +89,8 @@ const size = ref<'sm' | 'md' | 'lg'>('lg')
89
89
  | `port` | Vite 默认(5173) | dev server 端口 |
90
90
  | `components` | — | 契约清单要扫的组件 glob;不配则不生成 `.playground/manifest.json` |
91
91
  | `tsconfig` | 项目根 `tsconfig.json` | 组件契约提取用的 tsconfig |
92
+ | `groups` | — | 分组(`scenes` 下一级目录名)→ 侧栏显示名,**声明顺序即侧栏顺序**:`{ 'tao-common': 'Tao 通用组件' }` |
93
+ | `groupIndex` | `false` | 为每个分组自动合成一个「概览」落地页,点分组标题即可进入 |
92
94
 
93
95
  ## 场景内 API
94
96
 
@@ -131,6 +133,21 @@ useSceneOutput(() => ({ size: args.size, clicked: count.value }))
131
133
 
132
134
  面板以 400ms 轮询这个 getter,把返回值显示在出参区。
133
135
 
136
+ ### 一个场景多个 demo —— 参数槽位
137
+
138
+ 同一场景里放多个 demo 时,各自调用一次并在选项里写 `slot`,面板会按槽位名分块,
139
+ 互不干扰(改一块的入参只影响那一个 demo):
140
+
141
+ ```ts
142
+ const primary = useSceneArgs({ label: '主按钮', size: 'md' }, { slot: '主按钮' })
143
+ const ghost = useSceneArgs({ label: '次按钮', size: 'sm' }, { slot: '次按钮' })
144
+
145
+ useSceneOutput(() => ({ clicked: primaryCount.value }), '主按钮')
146
+ useSceneOutput(() => ({ clicked: ghostCount.value }), '次按钮')
147
+ ```
148
+
149
+ 槽位按代码里声明的顺序排列;只有一块时不显示槽位标题。
150
+
134
151
  ### toast
135
152
 
136
153
  ```ts
@@ -142,6 +159,7 @@ toast('保存失败', 'error') // 'info' | 'success' | 'warning' | 'error'
142
159
 
143
160
  ## 样式约定
144
161
 
162
+ - 只需引入一次 `@rinova/playground/style.css`(外壳样式)。**编辑器界面样式由包内按需加载**:打开代码面板时才会去拉 `@rinova/playground/editor.css`(≈125 KB gzip),不用手动引入;配 `codeViewer: 'shiki'` 的项目则永远不加载它。
145
163
  - 场景写 `<style scoped lang="less">`(使用方需要装 `less`)。**v0.1 没有 DOM 隔离**,非 scoped 样式会污染外壳。
146
164
  - 不要硬编码颜色,用外壳暴露的 12 个变量,明暗主题自动切换:
147
165
 
@@ -1,4 +1,4 @@
1
- import { a as toast, o as toastState, r as currentArgs, t as currentOutput } from "./output-Dx3ubog9.js";
1
+ import { a as currentArgsSlots, c as toastState, i as currentArgs, n as currentOutputSlots, s as toast, t as currentOutput } from "./output-CGawAeuu.js";
2
2
  import { Fragment, Teleport, TransitionGroup, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, h, inject, normalizeClass, onBeforeUnmount, onMounted, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, unref, watch, watchEffect, withCtx } from "vue";
3
3
  import config from "virtual:playground/config";
4
4
  import { scenes } from "virtual:playground/scenes";
@@ -2379,7 +2379,7 @@ var Registry$1 = class {
2379
2379
  };
2380
2380
  var INITIAL = StateStackImpl.NULL;
2381
2381
  // src/components/PwToastHost.vue?vue&type=script&setup=true&lang.ts
2382
- var _hoisted_1$10 = {
2382
+ var _hoisted_1$11 = {
2383
2383
  class: "pw-toast-host",
2384
2384
  role: "status",
2385
2385
  "aria-live": "polite"
@@ -2388,7 +2388,7 @@ var PwToastHost_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
2388
2388
  __name: "PwToastHost",
2389
2389
  setup(__props) {
2390
2390
  return (_ctx, _cache) => {
2391
- return openBlock(), createBlock(Teleport, { to: "body" }, [createElementVNode("div", _hoisted_1$10, [createVNode(TransitionGroup, { name: "pw-toast" }, {
2391
+ return openBlock(), createBlock(Teleport, { to: "body" }, [createElementVNode("div", _hoisted_1$11, [createVNode(TransitionGroup, { name: "pw-toast" }, {
2392
2392
  default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(toastState), (item) => {
2393
2393
  return openBlock(), createElementBlock("div", {
2394
2394
  key: item.id,
@@ -2884,13 +2884,13 @@ var X$2 = createLucideIcon({
2884
2884
  }]]
2885
2885
  });
2886
2886
  // src/components/PwButton.vue?vue&type=script&setup=true&lang.ts
2887
- var _hoisted_1$9 = ["disabled", "title"];
2888
- var _hoisted_2$8 = {
2887
+ var _hoisted_1$10 = ["disabled", "title"];
2888
+ var _hoisted_2$9 = {
2889
2889
  key: 0,
2890
2890
  class: "pw-btn__spinner",
2891
2891
  "aria-hidden": "true"
2892
2892
  };
2893
- var _hoisted_3$7 = {
2893
+ var _hoisted_3$8 = {
2894
2894
  key: 2,
2895
2895
  class: "pw-btn__label"
2896
2896
  };
@@ -2921,13 +2921,13 @@ var PwButton_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
2921
2921
  disabled: __props.disabled || __props.loading,
2922
2922
  title: __props.title,
2923
2923
  type: "button"
2924
- }, [__props.loading ? (openBlock(), createElementBlock("span", _hoisted_2$8)) : renderSlot(_ctx.$slots, "icon", {}, void 0, true, 1), _ctx.$slots.default ? (openBlock(), createElementBlock("span", _hoisted_3$7, [renderSlot(_ctx.$slots, "default", {}, void 0, true)])) : createCommentVNode("", true)], 10, _hoisted_1$9);
2924
+ }, [__props.loading ? (openBlock(), createElementBlock("span", _hoisted_2$9)) : renderSlot(_ctx.$slots, "icon", {}, void 0, true, 1), _ctx.$slots.default ? (openBlock(), createElementBlock("span", _hoisted_3$8, [renderSlot(_ctx.$slots, "default", {}, void 0, true)])) : createCommentVNode("", true)], 10, _hoisted_1$10);
2925
2925
  };
2926
2926
  }
2927
2927
  }), [["__scopeId", "data-v-8c5978c8"]]);
2928
2928
  // src/components/PwInput.vue?vue&type=script&setup=true&lang.ts
2929
- var _hoisted_1$8 = ["data-empty"];
2930
- var _hoisted_2$7 = [
2929
+ var _hoisted_1$9 = ["data-empty"];
2930
+ var _hoisted_2$8 = [
2931
2931
  "type",
2932
2932
  "value",
2933
2933
  "placeholder",
@@ -2986,7 +2986,7 @@ var PwInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
2986
2986
  onKeydown: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("keydown", $event)),
2987
2987
  onFocus: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("focus", $event)),
2988
2988
  onBlur: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("blur", $event))
2989
- }, null, 40, _hoisted_2$7),
2989
+ }, null, 40, _hoisted_2$8),
2990
2990
  __props.clearable && __props.modelValue ? (openBlock(), createElementBlock("button", {
2991
2991
  key: 0,
2992
2992
  class: "pw-input__clear",
@@ -2995,12 +2995,12 @@ var PwInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
2995
2995
  onClick: clear
2996
2996
  }, " × ")) : createCommentVNode("", true),
2997
2997
  renderSlot(_ctx.$slots, "suffix", {}, void 0, true)
2998
- ], 10, _hoisted_1$8);
2998
+ ], 10, _hoisted_1$9);
2999
2999
  };
3000
3000
  }
3001
3001
  }), [["__scopeId", "data-v-e249cd4b"]]);
3002
3002
  // src/components/PwSwitch.vue?vue&type=script&setup=true&lang.ts
3003
- var _hoisted_1$7 = ["aria-checked", "disabled"];
3003
+ var _hoisted_1$8 = ["aria-checked", "disabled"];
3004
3004
  // src/components/PwSwitch.vue
3005
3005
  var PwSwitch_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
3006
3006
  __name: "PwSwitch",
@@ -3019,7 +3019,7 @@ var PwSwitch_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
3019
3019
  "aria-checked": __props.modelValue,
3020
3020
  disabled: __props.disabled,
3021
3021
  onClick: _cache[0] || (_cache[0] = ($event) => emit("update:modelValue", !__props.modelValue))
3022
- }, [..._cache[1] || (_cache[1] = [createElementVNode("span", { class: "pw-switch__thumb" }, null, -1)])], 10, _hoisted_1$7);
3022
+ }, [..._cache[1] || (_cache[1] = [createElementVNode("span", { class: "pw-switch__thumb" }, null, -1)])], 10, _hoisted_1$8);
3023
3023
  };
3024
3024
  }
3025
3025
  }), [["__scopeId", "data-v-64fffbcb"]]);
@@ -11093,16 +11093,16 @@ var getHighlighter = () => {
11093
11093
  return highlighterPromise;
11094
11094
  };
11095
11095
  // src/code/CodeBlock.vue?vue&type=script&setup=true&lang.ts
11096
- var _hoisted_1$6 = { class: "pw-code" };
11097
- var _hoisted_2$6 = {
11096
+ var _hoisted_1$7 = { class: "pw-code" };
11097
+ var _hoisted_2$7 = {
11098
11098
  key: 0,
11099
11099
  class: "pw-code__hint"
11100
11100
  };
11101
- var _hoisted_3$6 = {
11101
+ var _hoisted_3$7 = {
11102
11102
  key: 1,
11103
11103
  class: "pw-code__error"
11104
11104
  };
11105
- var _hoisted_4$6 = ["innerHTML"];
11105
+ var _hoisted_4$7 = ["innerHTML"];
11106
11106
  // src/code/CodeBlock.vue
11107
11107
  var CodeBlock_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
11108
11108
  __name: "CodeBlock",
@@ -11134,34 +11134,53 @@ var CodeBlock_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
11134
11134
  }
11135
11135
  }, { immediate: true });
11136
11136
  return (_ctx, _cache) => {
11137
- return openBlock(), createElementBlock("div", _hoisted_1$6, [state.value === "loading" ? (openBlock(), createElementBlock("div", _hoisted_2$6, "高亮中…")) : state.value === "error" ? (openBlock(), createElementBlock("pre", _hoisted_3$6, toDisplayString(error.value), 1)) : (openBlock(), createElementBlock("div", {
11137
+ return openBlock(), createElementBlock("div", _hoisted_1$7, [state.value === "loading" ? (openBlock(), createElementBlock("div", _hoisted_2$7, "高亮中…")) : state.value === "error" ? (openBlock(), createElementBlock("pre", _hoisted_3$7, toDisplayString(error.value), 1)) : (openBlock(), createElementBlock("div", {
11138
11138
  key: 2,
11139
11139
  class: "pw-code__body",
11140
11140
  innerHTML: html.value
11141
- }, null, 8, _hoisted_4$6))]);
11141
+ }, null, 8, _hoisted_4$7))]);
11142
11142
  };
11143
11143
  }
11144
11144
  }), [["__scopeId", "data-v-feddef62"]]);
11145
11145
  // src/app/PlaygroundParamsPanel.vue?vue&type=script&setup=true&lang.ts
11146
- var _hoisted_1$5 = { class: "pw-params" };
11147
- var _hoisted_2$5 = { class: "pw-params__head" };
11148
- var _hoisted_3$5 = ["disabled"];
11149
- var _hoisted_4$5 = ["disabled"];
11150
- var _hoisted_5$5 = { class: "pw-params__body" };
11151
- var _hoisted_6$3 = {
11146
+ var _hoisted_1$6 = { class: "pw-params" };
11147
+ var _hoisted_2$6 = { class: "pw-params__head" };
11148
+ var _hoisted_3$6 = ["disabled"];
11149
+ var _hoisted_4$6 = ["disabled"];
11150
+ var _hoisted_5$6 = { class: "pw-params__body" };
11151
+ var _hoisted_6$4 = {
11152
+ key: 0,
11153
+ class: "pw-params__slots"
11154
+ };
11155
+ var _hoisted_7$4 = {
11152
11156
  key: 0,
11153
- class: "pw-form"
11157
+ class: "pw-slot__head"
11154
11158
  };
11155
- var _hoisted_7$3 = ["title"];
11156
- var _hoisted_8$1 = ["value", "onChange"];
11157
- var _hoisted_9$1 = ["value"];
11158
- var _hoisted_10 = {
11159
+ var _hoisted_8$2 = { class: "pw-slot__name" };
11160
+ var _hoisted_9$1 = { class: "pw-form" };
11161
+ var _hoisted_10$1 = ["title"];
11162
+ var _hoisted_11 = ["value", "onChange"];
11163
+ var _hoisted_12 = ["value"];
11164
+ var _hoisted_13 = {
11159
11165
  key: 0,
11160
11166
  class: "pw-params__hint"
11161
11167
  };
11162
- var _hoisted_11 = {
11168
+ var _hoisted_14 = {
11169
+ key: 0,
11170
+ class: "pw-params__hint pw-params__hint--empty"
11171
+ };
11172
+ var _hoisted_15 = {
11163
11173
  key: 1,
11164
- class: "pw-params__output"
11174
+ class: "pw-params__slots"
11175
+ };
11176
+ var _hoisted_16 = {
11177
+ key: 0,
11178
+ class: "pw-slot__head"
11179
+ };
11180
+ var _hoisted_17 = { class: "pw-slot__name" };
11181
+ var _hoisted_18 = {
11182
+ key: 0,
11183
+ class: "pw-params__hint pw-params__hint--empty"
11165
11184
  };
11166
11185
  // src/app/PlaygroundParamsPanel.vue
11167
11186
  var PlaygroundParamsPanel_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
@@ -11169,67 +11188,56 @@ var PlaygroundParamsPanel_default = /*#__PURE__*/ _plugin_vue_export_helper_defa
11169
11188
  props: { theme: {} },
11170
11189
  setup(__props) {
11171
11190
  const tab = ref("args");
11172
- const snapshot = ref(null);
11173
- const error = ref("");
11191
+ const snapshots = ref([]);
11174
11192
  let timer = null;
11175
- const hasArgs = computed(() => currentArgs.value !== null);
11176
- const hasOutput = computed(() => typeof currentOutput.value === "function");
11193
+ const argsSlots = computed(() => currentArgsSlots.value);
11194
+ const outputSlots = computed(() => currentOutputSlots.value);
11195
+ const hasArgs = computed(() => argsSlots.value.length > 0);
11196
+ const hasOutput = computed(() => outputSlots.value.length > 0);
11197
+ /** 多槽位(一个场景多个 demo)时,每块都加小标题 */
11198
+ const multiSlot = computed(() => argsSlots.value.length > 1 || outputSlots.value.length > 1);
11177
11199
  const activeTab = computed(() => {
11178
11200
  if (tab.value === "args" && hasArgs.value) return "args";
11179
11201
  if (tab.value === "output" && hasOutput.value) return "output";
11180
11202
  return hasArgs.value ? "args" : "output";
11181
11203
  });
11182
- /** 表单字段:按值的类型决定控件 */
11183
- const fields = computed(() => {
11184
- const binding = currentArgs.value;
11185
- if (!binding) return [];
11186
- return Object.entries(binding.values).map(([key, value]) => ({
11187
- key,
11188
- value,
11189
- label: binding.options.labels?.[key] ?? key,
11190
- kind: typeof value === "boolean" ? "boolean" : typeof value === "number" ? "number" : "string",
11191
- options: binding.options.options?.[key]
11192
- }));
11193
- });
11194
- const setValue = (key, value) => {
11195
- const binding = currentArgs.value;
11196
- if (!binding) return;
11204
+ const fieldsOf = (binding) => Object.entries(binding.values).map(([key, value]) => ({
11205
+ key,
11206
+ value,
11207
+ label: binding.options.labels?.[key] ?? key,
11208
+ kind: typeof value === "boolean" ? "boolean" : typeof value === "number" ? "number" : "string",
11209
+ options: binding.options.options?.[key]
11210
+ }));
11211
+ const setValue = (binding, key, value) => {
11197
11212
  binding.values[key] = value;
11198
11213
  };
11199
- const setString = (key, raw) => setValue(key, raw);
11200
- const setNumber = (key, raw) => {
11214
+ const setString = (binding, key, raw) => setValue(binding, key, raw);
11215
+ const setNumber = (binding, key, raw) => {
11201
11216
  const parsed = Number(raw);
11202
- setValue(key, Number.isFinite(parsed) ? parsed : 0);
11217
+ setValue(binding, key, Number.isFinite(parsed) ? parsed : 0);
11203
11218
  };
11204
- const resetArgs = () => {
11205
- const binding = currentArgs.value;
11206
- if (!binding) return;
11219
+ const resetSlot = (binding) => {
11207
11220
  for (const [key, value] of Object.entries(binding.defaults)) binding.values[key] = value;
11208
11221
  };
11222
+ const resetAll = () => {
11223
+ argsSlots.value.forEach(resetSlot);
11224
+ };
11209
11225
  const readOutput = () => {
11210
- const getter = currentOutput.value;
11211
- if (!getter) {
11212
- snapshot.value = null;
11213
- error.value = "";
11214
- return;
11215
- }
11216
- try {
11217
- snapshot.value = getter();
11218
- error.value = "";
11219
- } catch (err) {
11220
- error.value = err instanceof Error ? err.message : String(err);
11221
- snapshot.value = null;
11222
- }
11226
+ snapshots.value = outputSlots.value.map((binding) => {
11227
+ try {
11228
+ const value = binding.getter();
11229
+ return {
11230
+ label: binding.label || binding.key,
11231
+ text: JSON.stringify(value, null, 2) ?? String(value)
11232
+ };
11233
+ } catch (err) {
11234
+ return {
11235
+ label: binding.label || binding.key,
11236
+ text: err instanceof Error ? err.message : String(err)
11237
+ };
11238
+ }
11239
+ });
11223
11240
  };
11224
- const outputText = computed(() => {
11225
- if (error.value) return error.value;
11226
- if (!snapshot.value) return "(无数据)";
11227
- try {
11228
- return JSON.stringify(snapshot.value, null, 2);
11229
- } catch {
11230
- return String(snapshot.value);
11231
- }
11232
- });
11233
11241
  const startPolling = () => {
11234
11242
  readOutput();
11235
11243
  timer = window.setInterval(readOutput, 400);
@@ -11242,69 +11250,130 @@ var PlaygroundParamsPanel_default = /*#__PURE__*/ _plugin_vue_export_helper_defa
11242
11250
  if (timer !== null) window.clearInterval(timer);
11243
11251
  });
11244
11252
  return (_ctx, _cache) => {
11245
- return openBlock(), createElementBlock("aside", _hoisted_1$5, [createElementVNode("header", _hoisted_2$5, [
11253
+ return openBlock(), createElementBlock("aside", _hoisted_1$6, [createElementVNode("header", _hoisted_2$6, [
11246
11254
  createElementVNode("button", {
11247
11255
  type: "button",
11248
11256
  class: normalizeClass(["pw-params__tab", { "is-active": activeTab.value === "args" }]),
11249
11257
  disabled: !hasArgs.value,
11250
11258
  onClick: _cache[0] || (_cache[0] = ($event) => tab.value = "args")
11251
- }, " 入参 ", 10, _hoisted_3$5),
11259
+ }, " 入参 ", 10, _hoisted_3$6),
11252
11260
  createElementVNode("button", {
11253
11261
  type: "button",
11254
11262
  class: normalizeClass(["pw-params__tab", { "is-active": activeTab.value === "output" }]),
11255
11263
  disabled: !hasOutput.value,
11256
11264
  onClick: _cache[1] || (_cache[1] = ($event) => tab.value = "output")
11257
- }, " 出参 ", 10, _hoisted_4$5),
11265
+ }, " 出参 ", 10, _hoisted_4$6),
11258
11266
  _cache[2] || (_cache[2] = createElementVNode("span", { class: "pw-params__spacer" }, null, -1)),
11259
11267
  activeTab.value === "args" && hasArgs.value ? (openBlock(), createBlock(PwButton_default, {
11260
11268
  key: 0,
11261
11269
  size: "sm",
11262
11270
  variant: "text",
11263
11271
  title: "恢复默认值",
11264
- onClick: resetArgs
11272
+ onClick: resetAll
11265
11273
  }, {
11266
11274
  icon: withCtx(() => [createVNode(unref(RotateCcw), { size: 13 })]),
11267
11275
  _: 1
11268
11276
  })) : createCommentVNode("", true)
11269
- ]), createElementVNode("div", _hoisted_5$5, [activeTab.value === "args" ? (openBlock(), createElementBlock("div", _hoisted_6$3, [(openBlock(true), createElementBlock(Fragment, null, renderList(fields.value, (field) => {
11270
- return openBlock(), createElementBlock("div", {
11271
- key: field.key,
11272
- class: "pw-form__row"
11273
- }, [createElementVNode("label", {
11274
- class: "pw-form__label",
11275
- title: field.key
11276
- }, toDisplayString(field.label), 9, _hoisted_7$3), field.kind === "boolean" ? (openBlock(), createBlock(PwSwitch_default, {
11277
- key: 0,
11278
- "model-value": field.value === true,
11279
- "onUpdate:modelValue": ($event) => setValue(field.key, $event)
11280
- }, null, 8, ["model-value", "onUpdate:modelValue"])) : field.options ? (openBlock(), createElementBlock("select", {
11281
- key: 1,
11282
- class: "pw-form__select",
11283
- value: String(field.value),
11284
- onChange: ($event) => setString(field.key, $event.target.value)
11285
- }, [(openBlock(true), createElementBlock(Fragment, null, renderList(field.options, (option) => {
11286
- return openBlock(), createElementBlock("option", {
11287
- key: String(option),
11288
- value: String(option)
11289
- }, toDisplayString(option), 9, _hoisted_9$1);
11290
- }), 128))], 40, _hoisted_8$1)) : (openBlock(), createBlock(PwInput_default, {
11291
- key: 2,
11277
+ ]), createElementVNode("div", _hoisted_5$6, [activeTab.value === "args" ? (openBlock(), createElementBlock("div", _hoisted_6$4, [(openBlock(true), createElementBlock(Fragment, null, renderList(argsSlots.value, (slot) => {
11278
+ return openBlock(), createElementBlock("section", {
11279
+ key: `${slot.key}:${slot.label}`,
11280
+ class: "pw-slot"
11281
+ }, [multiSlot.value ? (openBlock(), createElementBlock("header", _hoisted_7$4, [createElementVNode("span", _hoisted_8$2, toDisplayString(slot.label || slot.key), 1), createVNode(PwButton_default, {
11292
11282
  size: "sm",
11293
- type: field.kind === "number" ? "number" : "text",
11294
- "model-value": String(field.value ?? ""),
11295
- "onUpdate:modelValue": ($event) => field.kind === "number" ? setNumber(field.key, $event) : setString(field.key, $event)
11296
- }, null, 8, [
11297
- "type",
11298
- "model-value",
11299
- "onUpdate:modelValue"
11300
- ]))]);
11301
- }), 128)), !fields.value.length ? (openBlock(), createElementBlock("p", _hoisted_10, "该场景没有声明入参")) : createCommentVNode("", true)])) : (openBlock(), createElementBlock("div", _hoisted_11, [createVNode(CodeBlock_default, {
11302
- code: outputText.value,
11303
- lang: "json"
11304
- }, null, 8, ["code"])]))])]);
11283
+ variant: "text",
11284
+ title: "只恢复这一块",
11285
+ onClick: ($event) => resetSlot(slot)
11286
+ }, {
11287
+ icon: withCtx(() => [createVNode(unref(RotateCcw), { size: 12 })]),
11288
+ _: 1
11289
+ }, 8, ["onClick"])])) : createCommentVNode("", true), createElementVNode("div", _hoisted_9$1, [(openBlock(true), createElementBlock(Fragment, null, renderList(fieldsOf(slot), (field) => {
11290
+ return openBlock(), createElementBlock("div", {
11291
+ key: field.key,
11292
+ class: "pw-form__row"
11293
+ }, [createElementVNode("label", {
11294
+ class: "pw-form__label",
11295
+ title: field.key
11296
+ }, toDisplayString(field.label), 9, _hoisted_10$1), field.kind === "boolean" ? (openBlock(), createBlock(PwSwitch_default, {
11297
+ key: 0,
11298
+ "model-value": field.value === true,
11299
+ "onUpdate:modelValue": ($event) => setValue(slot, field.key, $event)
11300
+ }, null, 8, ["model-value", "onUpdate:modelValue"])) : field.options ? (openBlock(), createElementBlock("select", {
11301
+ key: 1,
11302
+ class: "pw-form__select",
11303
+ value: String(field.value),
11304
+ onChange: ($event) => setString(slot, field.key, $event.target.value)
11305
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(field.options, (option) => {
11306
+ return openBlock(), createElementBlock("option", {
11307
+ key: String(option),
11308
+ value: String(option)
11309
+ }, toDisplayString(option), 9, _hoisted_12);
11310
+ }), 128))], 40, _hoisted_11)) : (openBlock(), createBlock(PwInput_default, {
11311
+ key: 2,
11312
+ size: "sm",
11313
+ type: field.kind === "number" ? "number" : "text",
11314
+ "model-value": String(field.value ?? ""),
11315
+ "onUpdate:modelValue": ($event) => field.kind === "number" ? setNumber(slot, field.key, $event) : setString(slot, field.key, $event)
11316
+ }, null, 8, [
11317
+ "type",
11318
+ "model-value",
11319
+ "onUpdate:modelValue"
11320
+ ]))]);
11321
+ }), 128)), !fieldsOf(slot).length ? (openBlock(), createElementBlock("p", _hoisted_13, "这一块没有声明入参")) : createCommentVNode("", true)])]);
11322
+ }), 128)), !argsSlots.value.length ? (openBlock(), createElementBlock("p", _hoisted_14, " 该场景没有声明入参 ")) : createCommentVNode("", true)])) : (openBlock(), createElementBlock("div", _hoisted_15, [(openBlock(true), createElementBlock(Fragment, null, renderList(snapshots.value, (item) => {
11323
+ return openBlock(), createElementBlock("section", {
11324
+ key: item.label,
11325
+ class: "pw-slot pw-slot--output"
11326
+ }, [multiSlot.value ? (openBlock(), createElementBlock("header", _hoisted_16, [createElementVNode("span", _hoisted_17, toDisplayString(item.label), 1)])) : createCommentVNode("", true), createVNode(CodeBlock_default, {
11327
+ code: item.text,
11328
+ lang: "json"
11329
+ }, null, 8, ["code"])]);
11330
+ }), 128)), !snapshots.value.length ? (openBlock(), createElementBlock("p", _hoisted_18, "(无数据)")) : createCommentVNode("", true)]))])]);
11331
+ };
11332
+ }
11333
+ }), [["__scopeId", "data-v-582d049e"]]);
11334
+ // src/app/PlaygroundGroupIndex.vue?vue&type=script&setup=true&lang.ts
11335
+ var _hoisted_1$5 = { class: "pw-group-index" };
11336
+ var _hoisted_2$5 = { class: "pw-group-index__head" };
11337
+ var _hoisted_3$5 = { class: "pw-group-index__title" };
11338
+ var _hoisted_4$5 = { class: "pw-group-index__count" };
11339
+ var _hoisted_5$5 = { class: "pw-group-index__list" };
11340
+ var _hoisted_6$3 = ["onClick"];
11341
+ var _hoisted_7$3 = {
11342
+ key: 0,
11343
+ class: "pw-group-index__desc"
11344
+ };
11345
+ var _hoisted_8$1 = { class: "pw-group-index__id" };
11346
+ // src/app/PlaygroundGroupIndex.vue
11347
+ var PlaygroundGroupIndex_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
11348
+ __name: "PlaygroundGroupIndex",
11349
+ props: {
11350
+ group: {},
11351
+ items: {}
11352
+ },
11353
+ emits: ["select"],
11354
+ setup(__props) {
11355
+ /**
11356
+ * 分组落地页:由 `groupIndex: true` 时框架合成的条目使用。
11357
+ * 没有源文件,只是把该组其它场景列出来。
11358
+ */
11359
+ return (_ctx, _cache) => {
11360
+ return openBlock(), createElementBlock("section", _hoisted_1$5, [createElementVNode("header", _hoisted_2$5, [createElementVNode("h2", _hoisted_3$5, toDisplayString(__props.group), 1), createElementVNode("span", _hoisted_4$5, toDisplayString(__props.items.length) + " 个场景", 1)]), createElementVNode("ul", _hoisted_5$5, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item) => {
11361
+ return openBlock(), createElementBlock("li", {
11362
+ key: item.id,
11363
+ class: "pw-group-index__item"
11364
+ }, [
11365
+ createElementVNode("button", {
11366
+ type: "button",
11367
+ class: "pw-group-index__link",
11368
+ onClick: ($event) => _ctx.$emit("select", item.id)
11369
+ }, toDisplayString(item.title), 9, _hoisted_6$3),
11370
+ item.description ? (openBlock(), createElementBlock("p", _hoisted_7$3, toDisplayString(item.description), 1)) : createCommentVNode("", true),
11371
+ createElementVNode("code", _hoisted_8$1, toDisplayString(item.id), 1)
11372
+ ]);
11373
+ }), 128))])]);
11305
11374
  };
11306
11375
  }
11307
- }), [["__scopeId", "data-v-7e91c8d4"]]);
11376
+ }), [["__scopeId", "data-v-97e3a4f9"]]);
11308
11377
  // src/app/PlaygroundSidebar.vue?vue&type=script&setup=true&lang.ts
11309
11378
  var _hoisted_1$4 = { class: "pw-sidebar" };
11310
11379
  var _hoisted_2$4 = { class: "pw-sidebar__brand" };
@@ -11313,11 +11382,12 @@ var _hoisted_4$4 = { class: "pw-sidebar__search" };
11313
11382
  var _hoisted_5$4 = { class: "pw-sidebar__nav" };
11314
11383
  var _hoisted_6$2 = { class: "pw-nav-group__title" };
11315
11384
  var _hoisted_7$2 = ["onClick"];
11316
- var _hoisted_8 = {
11385
+ var _hoisted_8 = ["onClick"];
11386
+ var _hoisted_9 = {
11317
11387
  key: 0,
11318
11388
  class: "pw-sidebar__empty"
11319
11389
  };
11320
- var _hoisted_9 = { class: "pw-sidebar__footer" };
11390
+ var _hoisted_10 = { class: "pw-sidebar__footer" };
11321
11391
  // src/app/PlaygroundSidebar.vue
11322
11392
  var PlaygroundSidebar_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
11323
11393
  __name: "PlaygroundSidebar",
@@ -11332,6 +11402,8 @@ var PlaygroundSidebar_default = /*#__PURE__*/ _plugin_vue_export_helper_default(
11332
11402
  const props = __props;
11333
11403
  const query = ref("");
11334
11404
  const inputRef = ref(null);
11405
+ /** 分组头点击聚焦该组的合成落地页;没有落地页时返回 null */
11406
+ const groupIndexId = (group) => group.items.find((item) => item.kind === "group-index")?.id ?? null;
11335
11407
  const focusSearch = () => {
11336
11408
  inputRef.value?.focus();
11337
11409
  };
@@ -11369,20 +11441,25 @@ var PlaygroundSidebar_default = /*#__PURE__*/ _plugin_vue_export_helper_default(
11369
11441
  return openBlock(), createElementBlock("section", {
11370
11442
  key: group.name,
11371
11443
  class: "pw-nav-group"
11372
- }, [createElementVNode("div", _hoisted_6$2, toDisplayString(group.name), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(group.items, (item) => {
11444
+ }, [createElementVNode("div", _hoisted_6$2, [groupIndexId(group) ? (openBlock(), createElementBlock("button", {
11445
+ key: 0,
11446
+ type: "button",
11447
+ class: normalizeClass(["pw-nav-group__title-btn", { "is-active": groupIndexId(group) === __props.activeId }]),
11448
+ onClick: ($event) => _ctx.$emit("select", groupIndexId(group))
11449
+ }, toDisplayString(group.name), 11, _hoisted_7$2)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(group.name), 1)], 64))]), (openBlock(true), createElementBlock(Fragment, null, renderList(group.items, (item) => {
11373
11450
  return openBlock(), createElementBlock("button", {
11374
11451
  key: item.id,
11375
11452
  type: "button",
11376
11453
  class: normalizeClass(["pw-nav-item", { "is-active": item.id === __props.activeId }]),
11377
11454
  onClick: ($event) => _ctx.$emit("select", item.id)
11378
- }, toDisplayString(item.title), 11, _hoisted_7$2);
11455
+ }, toDisplayString(item.title), 11, _hoisted_8);
11379
11456
  }), 128))]);
11380
- }), 128)), !filteredGroups.value.length ? (openBlock(), createElementBlock("p", _hoisted_8, "没有匹配的场景")) : createCommentVNode("", true)]),
11381
- createElementVNode("footer", _hoisted_9, toDisplayString(__props.sceneCount) + " 个场景", 1)
11457
+ }), 128)), !filteredGroups.value.length ? (openBlock(), createElementBlock("p", _hoisted_9, "没有匹配的场景")) : createCommentVNode("", true)]),
11458
+ createElementVNode("footer", _hoisted_10, toDisplayString(__props.sceneCount) + " 个场景", 1)
11382
11459
  ]);
11383
11460
  };
11384
11461
  }
11385
- }), [["__scopeId", "data-v-826f64d4"]]);
11462
+ }), [["__scopeId", "data-v-cda96938"]]);
11386
11463
  // src/code/monaco.ts
11387
11464
  /**
11388
11465
  * Monaco 单例:worker 环境只能设置一次,highlighter 也只注入一次。
@@ -11410,9 +11487,10 @@ var loadMonaco = () => {
11410
11487
  const [editorWorker, tsWorker, monaco, _contribution, shikiModule] = await Promise.all([
11411
11488
  import("./editor.worker-VDFPHiZj.js"),
11412
11489
  import("./ts.worker-CpQm_d0w.js"),
11413
- import("./editor.api-BtqJ5Bxw.js").then((n) => n.s),
11414
- import("./monaco.contribution-GLS4-bVF.js").then((n) => n.t),
11415
- import("./dist-g9u4q-9e.js")
11490
+ import("./editor.api-YVDhnHI-.js").then((n) => n.s),
11491
+ import("./monaco.contribution-CaO2YABh.js").then((n) => n.t),
11492
+ import("./dist-B0hittbo.js"),
11493
+ import("@rinova/playground/editor.css")
11416
11494
  ]);
11417
11495
  const EditorWorker = editorWorker.default;
11418
11496
  const TsWorker = tsWorker.default;
@@ -11818,7 +11896,7 @@ var _hoisted_1 = { class: "pw-app" };
11818
11896
  var _hoisted_2 = { class: "pw-main" };
11819
11897
  var _hoisted_3 = { class: "pw-body" };
11820
11898
  var _hoisted_4 = {
11821
- key: 1,
11899
+ key: 2,
11822
11900
  class: "pw-empty"
11823
11901
  };
11824
11902
  var _hoisted_5 = {
@@ -11831,6 +11909,12 @@ var App_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ *
11831
11909
  __name: "App",
11832
11910
  setup(__props) {
11833
11911
  const title = config.title ?? "Playground";
11912
+ /** config.groups 的声明顺序即侧栏分组顺序;未声明的组排到后面 */
11913
+ const groupLabelOrder = Object.values(config.groups ?? {});
11914
+ const groupRank = (name) => {
11915
+ const index = groupLabelOrder.indexOf(name);
11916
+ return index === -1 ? Number.MAX_SAFE_INTEGER : index;
11917
+ };
11834
11918
  const groups = computed(() => {
11835
11919
  const map = /* @__PURE__ */ new Map();
11836
11920
  for (const scene of scenes) {
@@ -11842,12 +11926,19 @@ var App_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ *
11842
11926
  return [...map.entries()].map(([name, items]) => ({
11843
11927
  name,
11844
11928
  items,
11845
- weight: items.reduce((min, item) => Math.min(min, item.order), Number.MAX_SAFE_INTEGER)
11846
- })).sort((a, b) => a.weight - b.weight || a.name.localeCompare(b.name));
11929
+ weight: items.filter((item) => item.kind !== "group-index").reduce((min, item) => Math.min(min, item.order), Number.MAX_SAFE_INTEGER)
11930
+ })).sort((a, b) => groupRank(a.name) - groupRank(b.name) || a.weight - b.weight || a.name.localeCompare(b.name));
11847
11931
  });
11848
- const visibleCount = computed(() => scenes.filter((scene) => !scene.hidden).length);
11932
+ const visibleCount = computed(() => scenes.filter((scene) => !scene.hidden && scene.kind !== "group-index").length);
11849
11933
  const activeId = ref(scenes.find((scene) => !scene.hidden)?.id ?? scenes[0]?.id ?? "");
11850
11934
  const activeScene = computed(() => scenes.find((scene) => scene.id === activeId.value) ?? scenes[0]);
11935
+ /** 当前是分组落地页时,列出该组的普通场景(不含其它合成页) */
11936
+ const groupSceneItems = computed(() => {
11937
+ const scene = activeScene.value;
11938
+ if (!scene || scene.kind !== "group-index") return [];
11939
+ return scenes.filter((item) => item.group === scene.group && item.kind !== "group-index" && !item.hidden);
11940
+ });
11941
+ const isGroupIndex = computed(() => activeScene.value?.kind === "group-index");
11851
11942
  const showCode = ref(false);
11852
11943
  const showPayload = ref(false);
11853
11944
  const hasPayload = computed(() => currentArgs.value !== null || currentOutput.value !== null);
@@ -11871,7 +11962,7 @@ var App_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ *
11871
11962
  localStorage.setItem(STORAGE_KEY, preference.value);
11872
11963
  };
11873
11964
  const basePath = (() => {
11874
- const raw = config.base ?? "/";
11965
+ const raw = config.routeBase ?? config.base ?? "/";
11875
11966
  return raw.endsWith("/") ? raw : `${raw}/`;
11876
11967
  })();
11877
11968
  /** 从 location.pathname 解析出场景 id(去掉 base 与尾部斜杠) */
@@ -11958,23 +12049,28 @@ var App_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ *
11958
12049
  "width",
11959
12050
  "theme"
11960
12051
  ])) : createCommentVNode("", true),
11961
- createElementVNode("div", _hoisted_3, [activeScene.value ? (openBlock(), createBlock(PlaygroundStage_default, {
12052
+ createElementVNode("div", _hoisted_3, [isGroupIndex.value && activeScene.value ? (openBlock(), createBlock(PlaygroundGroupIndex_default, {
11962
12053
  key: 0,
12054
+ group: activeScene.value.group,
12055
+ items: groupSceneItems.value,
12056
+ onSelect: _cache[4] || (_cache[4] = ($event) => activeId.value = $event)
12057
+ }, null, 8, ["group", "items"])) : activeScene.value ? (openBlock(), createBlock(PlaygroundStage_default, {
12058
+ key: 1,
11963
12059
  scene: activeScene.value,
11964
12060
  width: width.value
11965
12061
  }, null, 8, ["scene", "width"])) : (openBlock(), createElementBlock("div", _hoisted_4, "没有找到任何场景,请在 scenes 目录下新建 .vue 文件。")), showPayload.value && hasPayload.value ? (openBlock(), createBlock(PlaygroundParamsPanel_default, {
11966
- key: 2,
12062
+ key: 3,
11967
12063
  theme: theme.value
11968
12064
  }, null, 8, ["theme"])) : createCommentVNode("", true)]),
11969
- showCode.value && activeScene.value ? (openBlock(), createElementBlock("div", _hoisted_5, [createVNode(PlaygroundSourcePanel_default, {
12065
+ showCode.value && activeScene.value && !isGroupIndex.value ? (openBlock(), createElementBlock("div", _hoisted_5, [createVNode(PlaygroundSourcePanel_default, {
11970
12066
  scene: activeScene.value,
11971
12067
  theme: theme.value,
11972
- onClose: _cache[4] || (_cache[4] = ($event) => showCode.value = false)
12068
+ onClose: _cache[5] || (_cache[5] = ($event) => showCode.value = false)
11973
12069
  }, null, 8, ["scene", "theme"])])) : createCommentVNode("", true)
11974
12070
  ]),
11975
12071
  createVNode(PwToastHost_default)
11976
12072
  ]);
11977
12073
  };
11978
12074
  }
11979
- }), [["__scopeId", "data-v-cba81ecf"]]);
12075
+ }), [["__scopeId", "data-v-5c38b5f6"]]);
11980
12076
  export { App_default as default, __exportAll as i, FontStyle as n, INITIAL as r, EncodedTokenMetadata as t };