imean-service-engine-htmx-plugin 2.4.0 → 2.6.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/dist/index.js CHANGED
@@ -80,7 +80,6 @@ function renderActionButton(action, index) {
80
80
  close,
81
81
  submit,
82
82
  formId,
83
- onClick,
84
83
  className = "",
85
84
  target
86
85
  } = action;
@@ -106,8 +105,7 @@ function renderActionButton(action, index) {
106
105
  index
107
106
  );
108
107
  }
109
- const finalOnClick = close ? CLOSE_DIALOG_SCRIPT : onClick;
110
- if (finalOnClick) {
108
+ if (close) {
111
109
  const variantStyles = {
112
110
  primary: "bg-blue-600 text-white hover:bg-blue-700",
113
111
  secondary: "bg-gray-200 text-gray-800 hover:bg-gray-300",
@@ -125,7 +123,8 @@ function renderActionButton(action, index) {
125
123
  "button",
126
124
  {
127
125
  type: "button",
128
- _: finalOnClick,
126
+ "x-data": CLOSE_DIALOG_ALPINE_DATA,
127
+ "x-on:click": "closeDialog()",
129
128
  className: `px-4 py-2 rounded transition-colors font-medium ${buttonStyle} ${className}`,
130
129
  "data-testid": testId2,
131
130
  ...confirm && { "data-confirm": confirm },
@@ -169,15 +168,25 @@ function renderActionButton(action, index) {
169
168
  function renderActionButtons(actions) {
170
169
  return actions.map((action, index) => renderActionButton(action, index));
171
170
  }
172
- var CLOSE_DIALOG_SCRIPT;
171
+ var CLOSE_DIALOG_ALPINE_DATA;
173
172
  var init_action_button_renderer = __esm({
174
173
  "src/utils/action-button-renderer.tsx"() {
175
174
  init_button();
176
- CLOSE_DIALOG_SCRIPT = `on click
177
- add .dialog-exit to .dialog-backdrop
178
- add .dialog-content-exit to .dialog-content
179
- wait 200ms
180
- set #dialog-container's innerHTML to '' end`;
175
+ CLOSE_DIALOG_ALPINE_DATA = `{
176
+ closeDialog() {
177
+ const backdrop = this.$el.closest('.dialog-backdrop');
178
+ if (backdrop) {
179
+ backdrop.classList.add('dialog-exit');
180
+ const content = backdrop.querySelector('.dialog-content');
181
+ if (content) {
182
+ content.classList.add('dialog-content-exit');
183
+ }
184
+ setTimeout(() => {
185
+ backdrop.remove();
186
+ }, 200);
187
+ }
188
+ }
189
+ }`;
181
190
  }
182
191
  });
183
192
 
@@ -307,11 +316,6 @@ var init_cdn_cache = __esm({
307
316
  url: "https://unpkg.com/htmx.org@latest",
308
317
  mimeType: "application/javascript"
309
318
  },
310
- {
311
- name: "hyperscript",
312
- url: "https://unpkg.com/hyperscript.org@latest",
313
- mimeType: "application/javascript"
314
- },
315
319
  {
316
320
  name: "tailwindcss",
317
321
  url: "https://cdn.tailwindcss.com",
@@ -321,16 +325,6 @@ var init_cdn_cache = __esm({
321
325
  name: "alpinejs",
322
326
  url: "https://unpkg.com/alpinejs@latest/dist/cdn.min.js",
323
327
  mimeType: "application/javascript"
324
- },
325
- {
326
- name: "idiomorph",
327
- url: "https://unpkg.com/idiomorph@0.7.4/dist/idiomorph-ext.min.js",
328
- mimeType: "application/javascript"
329
- },
330
- {
331
- name: "sortablejs",
332
- url: "https://unpkg.com/sortablejs@latest/Sortable.min.js",
333
- mimeType: "application/javascript"
334
328
  }
335
329
  ];
336
330
  cache = /* @__PURE__ */ new Map();
@@ -555,8 +549,8 @@ function renderFormField(field, initialData, formFieldRenderers) {
555
549
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: customRenderer({
556
550
  field,
557
551
  value: parsedValue,
558
- initialData,
559
- fieldName: field.name
552
+ item: initialData,
553
+ name: field.name
560
554
  }) }),
561
555
  field.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mt-1", children: field.description })
562
556
  ]
@@ -631,7 +625,7 @@ function renderFormField(field, initialData, formFieldRenderers) {
631
625
  name: field.name,
632
626
  required: field.required,
633
627
  placeholder: field.placeholder || (isJsonString(value) ? "JSON \u683C\u5F0F\u6570\u636E" : ""),
634
- rows: isJsonString(value) ? 10 : 4,
628
+ rows: isJsonString(value) ? Math.max(10, value.split("\n").length) : Math.max(5, Math.ceil(value.length / 100)),
635
629
  className: "w-full px-4 py-2.5 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 resize-y font-mono text-sm",
636
630
  "data-testid": `input-${field.name}`,
637
631
  children: isJsonString(value) ? formatJsonString(value) : value
@@ -2809,286 +2803,6 @@ var DefaultListFeature = class extends BaseFeature {
2809
2803
  }
2810
2804
  };
2811
2805
 
2812
- // src/component-system/store.ts
2813
- var STATE_EXPIRATION_TIME = 864e5;
2814
- var StateStore = class _StateStore {
2815
- static instance;
2816
- state;
2817
- constructor() {
2818
- this.state = /* @__PURE__ */ new Map();
2819
- setInterval(() => {
2820
- this.state.forEach((state) => {
2821
- if (state.lastUpdated < Date.now() - STATE_EXPIRATION_TIME) {
2822
- this.state.delete(state.instanceId);
2823
- }
2824
- });
2825
- }, 3e4);
2826
- }
2827
- static get() {
2828
- if (!_StateStore.instance) {
2829
- _StateStore.instance = new _StateStore();
2830
- }
2831
- return _StateStore.instance;
2832
- }
2833
- /** 获取实例状态 */
2834
- getState(instanceId) {
2835
- const state = this.state.get(instanceId);
2836
- if (state) {
2837
- return state;
2838
- }
2839
- const newState = {
2840
- instanceId,
2841
- data: {
2842
- props: {},
2843
- state: {}
2844
- },
2845
- lastUpdated: Date.now()
2846
- };
2847
- this.state.set(instanceId, newState);
2848
- return newState;
2849
- }
2850
- /** 设置实例状态 */
2851
- setState(instanceId, state) {
2852
- const instanceState = this.getState(instanceId);
2853
- instanceState.data = Object.assign(instanceState.data.state, state);
2854
- instanceState.lastUpdated = Date.now();
2855
- }
2856
- };
2857
-
2858
- // src/component-system/utils.ts
2859
- var globalIdCounter = 0;
2860
- function generateUniqueId() {
2861
- return `htmx-cid-${globalIdCounter++}`;
2862
- }
2863
- var HTMX_COMPONENT_PREFIX = "/_htmx_components";
2864
-
2865
- // src/component-system/context.tsx
2866
- var RenderContext = class {
2867
- constructor(prefix, instanceId, componentName) {
2868
- this.prefix = prefix;
2869
- this.instanceId = instanceId;
2870
- this.componentName = componentName;
2871
- }
2872
- // 生成唯一 ID(使用全局共享计数器,避免冲突)
2873
- $id() {
2874
- return generateUniqueId();
2875
- }
2876
- setState(state) {
2877
- StateStore.get().setState(this.instanceId, state);
2878
- }
2879
- get state() {
2880
- return StateStore.get().getState(this.instanceId).data.state;
2881
- }
2882
- get props() {
2883
- return StateStore.get().getState(this.instanceId).data.props;
2884
- }
2885
- // 生成方法 URL(使用当前 instanceId)
2886
- url(methodName, params) {
2887
- const baseUrl = `${this.prefix}/${HTMX_COMPONENT_PREFIX}/${this.componentName}/${this.instanceId}/${methodName}`;
2888
- if (params && Object.keys(params).length > 0) {
2889
- const queryString = new URLSearchParams(
2890
- Object.entries(params).reduce(
2891
- (acc, [key, value]) => {
2892
- acc[key] = String(value);
2893
- return acc;
2894
- },
2895
- {}
2896
- )
2897
- ).toString();
2898
- return `${baseUrl}?${queryString}`;
2899
- }
2900
- return baseUrl;
2901
- }
2902
- callMethod(methodName, params) {
2903
- const selectors = Object.entries(params).map(([name, expression]) => `${name}:${expression}`).join(",");
2904
- return {
2905
- "hx-post": this.url(methodName),
2906
- "hx-vals": `js:{_params_:{${selectors}}}`,
2907
- "hx-params": "_state_,_params_,_this_value_"
2908
- };
2909
- }
2910
- };
2911
- var ComponentContext = class extends RenderContext {
2912
- constructor(prefix, ctx, componentName) {
2913
- const routeParams = ctx.req.param();
2914
- const instanceId = String(routeParams.instanceId || "");
2915
- super(prefix, instanceId, componentName);
2916
- this.ctx = ctx;
2917
- }
2918
- // 获取所有参数(统一接口:聚合 query string 和 body)
2919
- async params() {
2920
- const params = {};
2921
- const routeParams = this.ctx.req.param();
2922
- Object.assign(params, routeParams);
2923
- const url = new URL(this.ctx.req.url);
2924
- for (const [key, value] of url.searchParams.entries()) {
2925
- params[key] = value;
2926
- }
2927
- const contentType = this.ctx.req.header("Content-Type") || "";
2928
- if (contentType.includes("application/json")) {
2929
- try {
2930
- const body = await this.ctx.req.json();
2931
- Object.assign(params, body);
2932
- } catch (e) {
2933
- console.warn("[ComponentContext] Failed to parse JSON body:", e);
2934
- }
2935
- } else if (this.ctx.req.method === "POST" || this.ctx.req.method === "PUT" || this.ctx.req.method === "PATCH" || this.ctx.req.method === "DELETE") {
2936
- try {
2937
- const formData = await this.ctx.req.formData();
2938
- for (const [key, value] of formData.entries()) {
2939
- params[key] = value instanceof File ? value : value.toString();
2940
- }
2941
- } catch (e) {
2942
- console.warn("[ComponentContext] Failed to parse form data:", e);
2943
- }
2944
- }
2945
- return params;
2946
- }
2947
- // 获取查询参数
2948
- query() {
2949
- const url = new URL(this.ctx.req.url);
2950
- return Object.fromEntries(url.searchParams.entries());
2951
- }
2952
- // 获取请求体
2953
- async body() {
2954
- const contentType = this.ctx.req.header("Content-Type") || "";
2955
- if (contentType.includes("application/json")) {
2956
- try {
2957
- return await this.ctx.req.json();
2958
- } catch (e) {
2959
- console.warn("[ComponentContext] Failed to parse JSON body:", e);
2960
- return {};
2961
- }
2962
- }
2963
- try {
2964
- const formData = await this.ctx.req.formData();
2965
- const body = {};
2966
- for (const [key, value] of formData.entries()) {
2967
- body[key] = value instanceof File ? value : value.toString();
2968
- }
2969
- return body;
2970
- } catch (e) {
2971
- console.warn("[ComponentContext] Failed to parse form data:", e);
2972
- return {};
2973
- }
2974
- }
2975
- };
2976
-
2977
- // src/component-system/component.tsx
2978
- var METHOD_METADATA_KEY = /* @__PURE__ */ Symbol("htmx:method");
2979
- function Method(config) {
2980
- return function(target, propertyKey, _descriptor) {
2981
- if (!target[METHOD_METADATA_KEY]) {
2982
- target[METHOD_METADATA_KEY] = /* @__PURE__ */ new Map();
2983
- }
2984
- target[METHOD_METADATA_KEY].set(propertyKey, {
2985
- method: config?.method || "get",
2986
- path: config?.path
2987
- });
2988
- };
2989
- }
2990
- var HtmxComponent = class {
2991
- constructor(name) {
2992
- this.name = name;
2993
- }
2994
- prefix;
2995
- // 组件函数
2996
- Component = (props) => {
2997
- const instanceId = generateUniqueId();
2998
- const state = StateStore.get().getState(instanceId).data;
2999
- state.props = props;
3000
- state.state = {};
3001
- const renderCtx = new RenderContext(
3002
- this.prefix,
3003
- instanceId,
3004
- this.name
3005
- );
3006
- return this.render(renderCtx, props);
3007
- };
3008
- // 返回 JSX script 元素
3009
- // 获取所有标记为 @Method() 的方法
3010
- // 注意:handler 不再绑定 this,因为方法会接收 ComponentContext 作为第一个参数
3011
- static getMethods(component) {
3012
- const methods = /* @__PURE__ */ new Map();
3013
- const metadata = component[METHOD_METADATA_KEY];
3014
- if (!metadata) return methods;
3015
- for (const [methodName, config] of metadata.entries()) {
3016
- const handler = component[methodName];
3017
- methods.set(methodName, {
3018
- method: config.method,
3019
- path: config.path,
3020
- handler
3021
- });
3022
- }
3023
- return methods;
3024
- }
3025
- };
3026
- var HtmxComponentHandler = class {
3027
- constructor(hono, prefix, components) {
3028
- this.hono = hono;
3029
- this.prefix = prefix;
3030
- this.components = components;
3031
- for (const component of this.components) {
3032
- component.prefix = this.prefix;
3033
- this.registerHandler(component);
3034
- }
3035
- }
3036
- registerHandler(component) {
3037
- const methods = HtmxComponent.getMethods(component);
3038
- for (const [methodName, methodConfig] of methods) {
3039
- const routePath = `${this.prefix}/${HTMX_COMPONENT_PREFIX}/${component.name}/:instanceId/${methodName}`;
3040
- imeanServiceEngine.logger.info(
3041
- `[HtmxComponent] Registering handler ${methodConfig.method} ${routePath}`
3042
- );
3043
- this.hono[methodConfig.method](routePath, async (ctx) => {
3044
- return this.handleComponentMethod(
3045
- ctx,
3046
- component,
3047
- methodConfig.handler.bind(component)
3048
- );
3049
- });
3050
- }
3051
- }
3052
- async handleComponentMethod(ctx, component, handler) {
3053
- const componentContext = new ComponentContext(
3054
- this.prefix,
3055
- ctx,
3056
- component.name
3057
- );
3058
- const result = await handler(componentContext);
3059
- if (result instanceof Object && ("target" in result || "swap" in result || "body" in result || "oobs" in result)) {
3060
- const { target, swap, body, oobs, trigger } = result;
3061
- const headers = {};
3062
- let bodyContent = body;
3063
- if (target) headers["HX-Retarget"] = target;
3064
- if (swap) headers["HX-Reswap"] = swap;
3065
- if (trigger) headers["HX-Trigger"] = trigger;
3066
- if (oobs) {
3067
- oobs.forEach((oob) => {
3068
- oob.props["hx-swap-oob"] = "true";
3069
- });
3070
- if (!body) {
3071
- headers["HX-Reswap"] = "delete";
3072
- }
3073
- bodyContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3074
- body,
3075
- oobs
3076
- ] });
3077
- }
3078
- return ctx.html(bodyContent, 200, headers);
3079
- }
3080
- if (result === null) {
3081
- return ctx.html(/* @__PURE__ */ jsxRuntime.jsx("div", {}), 200, {
3082
- "HX-Reswap": "none"
3083
- });
3084
- }
3085
- if (result instanceof Response) {
3086
- return result;
3087
- }
3088
- return ctx.html(result, 200);
3089
- }
3090
- };
3091
-
3092
2806
  // src/utils/path.ts
3093
2807
  function modelNameToPath(modelName) {
3094
2808
  return `/${modelName.replace(/([A-Z])/g, "-$1").toLowerCase()}`;
@@ -3100,8 +2814,9 @@ function ErrorAlert(props) {
3100
2814
  type = "error",
3101
2815
  showClose = true,
3102
2816
  className = "",
3103
- autoClose = 5e3
2817
+ autoClose = 5e3,
3104
2818
  // 默认 5 秒自动关闭
2819
+ "hx-swap-oob": hxSwapOob
3105
2820
  } = props;
3106
2821
  const typeClasses = {
3107
2822
  error: "bg-red-50 border-red-200 text-red-800",
@@ -3190,12 +2905,20 @@ function ErrorAlert(props) {
3190
2905
  const now = Date.now();
3191
2906
  this.elapsedTime = now - this.startTime;
3192
2907
  this.progress = Math.max(0, 100 - (this.elapsedTime / this.autoCloseDelay) * 100);
2908
+ // \u66F4\u65B0\u8FDB\u5EA6\u6761\u6837\u5F0F
2909
+ const progressBar = this.$el.querySelector('.progress-bar');
2910
+ if (progressBar) {
2911
+ progressBar.style.width = this.progress + '%';
2912
+ }
3193
2913
  if (this.progress > 0 && this.visible) {
3194
2914
  this.progressTimer = requestAnimationFrame(updateProgress);
3195
2915
  }
3196
2916
  };
3197
2917
  this.progressTimer = requestAnimationFrame(updateProgress);
3198
2918
  },
2919
+ getProgressStyle() {
2920
+ return { width: this.progress + '%' };
2921
+ },
3199
2922
  pauseAutoClose() {
3200
2923
  if (this.autoCloseTimer) {
3201
2924
  clearTimeout(this.autoCloseTimer);
@@ -3224,6 +2947,9 @@ function ErrorAlert(props) {
3224
2947
  cancelAnimationFrame(this.progressTimer);
3225
2948
  this.progressTimer = null;
3226
2949
  }
2950
+ // \u6DFB\u52A0\u6DE1\u51FA\u52A8\u753B
2951
+ this.$el.classList.add('error-alert-exit');
2952
+ // \u52A8\u753B\u7ED3\u675F\u540E\u5B8C\u5168\u79FB\u9664\u5143\u7D20\uFF0C\u4E0D\u7559\u7A7A div
3227
2953
  setTimeout(() => {
3228
2954
  if (this.$el && this.$el.parentNode) {
3229
2955
  this.$el.parentNode.removeChild(this.$el);
@@ -3234,6 +2960,9 @@ function ErrorAlert(props) {
3234
2960
  visible: true,
3235
2961
  close() {
3236
2962
  this.visible = false;
2963
+ // \u6DFB\u52A0\u6DE1\u51FA\u52A8\u753B
2964
+ this.$el.classList.add('error-alert-exit');
2965
+ // \u52A8\u753B\u7ED3\u675F\u540E\u5B8C\u5168\u79FB\u9664\u5143\u7D20\uFF0C\u4E0D\u7559\u7A7A div
3237
2966
  setTimeout(() => {
3238
2967
  if (this.$el && this.$el.parentNode) {
3239
2968
  this.$el.parentNode.removeChild(this.$el);
@@ -3255,18 +2984,26 @@ function ErrorAlert(props) {
3255
2984
  alpineProps["@mouseenter"] = "pauseAutoClose()";
3256
2985
  alpineProps["@mouseleave"] = "resumeAutoClose()";
3257
2986
  }
2987
+ const htmxProps = {};
2988
+ if (hxSwapOob) {
2989
+ htmxProps["hx-swap-oob"] = hxSwapOob;
2990
+ }
3258
2991
  return /* @__PURE__ */ jsxRuntime.jsxs(
3259
2992
  "div",
3260
2993
  {
3261
2994
  ...alpineProps,
3262
- className: `border rounded-lg p-4 shadow-lg mb-2 ${typeClasses[type]} ${className} relative overflow-hidden`,
2995
+ ...htmxProps,
2996
+ className: `w-full border rounded-lg p-4 shadow-lg ${typeClasses[type]} ${className} relative overflow-hidden pointer-events-auto`,
3263
2997
  role: "alert",
2998
+ style: {
2999
+ animation: "slideInRight 0.3s ease-out"
3000
+ },
3264
3001
  children: [
3265
3002
  autoClose > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-0 left-0 right-0 h-1 bg-gray-200", children: /* @__PURE__ */ jsxRuntime.jsx(
3266
3003
  "div",
3267
3004
  {
3268
- className: `h-full ${progressBarColors[type]}`,
3269
- ...{ ":style": "`width: ${progress}%`" }
3005
+ className: `h-full ${progressBarColors[type]} progress-bar`,
3006
+ ...{ "x-bind:style": "getProgressStyle()" }
3270
3007
  }
3271
3008
  ) }),
3272
3009
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start", children: [
@@ -3385,6 +3122,9 @@ async function createFeatureContext(ctx, page, feature, user, options) {
3385
3122
  };
3386
3123
  return featureContext;
3387
3124
  }
3125
+ function generateDialogId() {
3126
+ return `dialog-${Date.now()}}`;
3127
+ }
3388
3128
  function Dialog(props) {
3389
3129
  const {
3390
3130
  title,
@@ -3396,6 +3136,7 @@ function Dialog(props) {
3396
3136
  actions = [],
3397
3137
  fixedContentHeight = false
3398
3138
  } = props;
3139
+ const dialogId = generateDialogId();
3399
3140
  const sizeClasses = {
3400
3141
  sm: "max-w-md",
3401
3142
  md: "max-w-lg",
@@ -3403,19 +3144,33 @@ function Dialog(props) {
3403
3144
  xl: "max-w-4xl",
3404
3145
  full: "max-w-7xl"
3405
3146
  };
3406
- const backdropClickHandler = closeOnBackdropClick ? `on click if event.target is me
3407
- add .dialog-exit to me
3408
- add .dialog-content-exit to .dialog-content
3409
- wait 200ms
3410
- set #dialog-container's innerHTML to '' end` : "";
3147
+ const alpineData = `{
3148
+ closeDialog() {
3149
+ const backdrop = this.$el.closest('.dialog-backdrop');
3150
+ if (backdrop) {
3151
+ backdrop.classList.add('dialog-exit');
3152
+ const content = backdrop.querySelector('.dialog-content');
3153
+ if (content) {
3154
+ content.classList.add('dialog-content-exit');
3155
+ }
3156
+ setTimeout(() => {
3157
+ backdrop.remove();
3158
+ }, 200);
3159
+ }
3160
+ }
3161
+ }`;
3411
3162
  return /* @__PURE__ */ jsxRuntime.jsx(
3412
3163
  "div",
3413
3164
  {
3165
+ id: dialogId,
3414
3166
  className: "fixed inset-0 bg-black bg-opacity-50 z-[100] flex items-center justify-center p-4 dialog-backdrop",
3415
3167
  style: {
3416
3168
  animation: "fadeIn 0.2s ease-out"
3417
3169
  },
3418
- _: backdropClickHandler,
3170
+ "x-data": alpineData,
3171
+ ...closeOnBackdropClick && {
3172
+ "x-on:click": "if ($event.target === $el) closeDialog()"
3173
+ },
3419
3174
  children: /* @__PURE__ */ jsxRuntime.jsxs(
3420
3175
  "div",
3421
3176
  {
@@ -3423,7 +3178,7 @@ function Dialog(props) {
3423
3178
  style: {
3424
3179
  animation: "slideIn 0.3s ease-out"
3425
3180
  },
3426
- _: "on click call event.stopPropagation()",
3181
+ ...{ "x-on:click.stop": "" },
3427
3182
  children: [
3428
3183
  (title || showClose) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 border-b border-gray-200 bg-white flex items-center justify-between", children: [
3429
3184
  title && /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-900", children: title }),
@@ -3431,7 +3186,7 @@ function Dialog(props) {
3431
3186
  "button",
3432
3187
  {
3433
3188
  className: "text-gray-400 hover:text-gray-600 transition-colors",
3434
- _: "on click \r\n add .dialog-exit to .dialog-backdrop\r\n add .dialog-content-exit to .dialog-content\r\n wait 200ms\r\n set #dialog-container's innerHTML to '' end",
3189
+ "x-on:click": "closeDialog()",
3435
3190
  children: /* @__PURE__ */ jsxRuntime.jsx(
3436
3191
  "svg",
3437
3192
  {
@@ -3528,11 +3283,26 @@ function PermissionDeniedContent(props) {
3528
3283
  Button,
3529
3284
  {
3530
3285
  variant: "secondary",
3531
- _: "on click set #dialog-container's innerHTML to ''",
3286
+ "x-data": `{
3287
+ closeDialog() {
3288
+ const backdrop = this.$el.closest('.dialog-backdrop');
3289
+ if (backdrop) {
3290
+ backdrop.classList.add('dialog-exit');
3291
+ const content = backdrop.querySelector('.dialog-content');
3292
+ if (content) {
3293
+ content.classList.add('dialog-content-exit');
3294
+ }
3295
+ setTimeout(() => {
3296
+ backdrop.remove();
3297
+ }, 200);
3298
+ }
3299
+ }
3300
+ }`,
3301
+ "x-on:click": "closeDialog()",
3532
3302
  children: "\u5173\u95ED"
3533
3303
  }
3534
3304
  ),
3535
- !isDialog && userInfo && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "secondary", _: "on click window.history.back()", children: "\u8FD4\u56DE" })
3305
+ !isDialog && userInfo && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "secondary", onclick: "window.history.back()", children: "\u8FD4\u56DE" })
3536
3306
  ] })
3537
3307
  ] });
3538
3308
  }
@@ -3553,13 +3323,8 @@ var getResourceUrl = (prefix, name) => {
3553
3323
  function globalScripts(prefix) {
3554
3324
  return html.html`
3555
3325
  <script src=${getResourceUrl(prefix, "htmx")}></script>
3556
- <script src=${getResourceUrl(prefix, "htmx-ext-form-json")}></script>
3557
- <script src=${getResourceUrl(prefix, "hyperscript")}></script>
3558
3326
  <script src=${getResourceUrl(prefix, "tailwindcss")}></script>
3559
3327
  <script src=${getResourceUrl(prefix, "alpinejs")} defer></script>
3560
- <script src=${getResourceUrl(prefix, "sortablejs")}></script>
3561
- <script src=${getResourceUrl(prefix, "idiomorph")}></script>
3562
- <script type="module" src=${getResourceUrl(prefix, "datastar")}></script>
3563
3328
  `;
3564
3329
  }
3565
3330
  function globalStyles() {
@@ -3689,36 +3454,6 @@ function globalStyles() {
3689
3454
  }
3690
3455
  </style>`;
3691
3456
  }
3692
- function sortableScript() {
3693
- html.html`<script>
3694
- if (typeof htmx !== "undefined" && typeof Sortable !== "undefined") {
3695
- htmx.onLoad(function (content) {
3696
- var sortables = content.querySelectorAll(".sortable");
3697
- for (var i = 0; i < sortables.length; i++) {
3698
- var sortable = sortables[i];
3699
- // 检查是否已经初始化
3700
- if (sortable.sortableInstance) {
3701
- continue;
3702
- }
3703
- var sortableInstance = new Sortable(sortable, {
3704
- animation: 150,
3705
- ghostClass: "sortable-ghost",
3706
- filter: ".htmx-indicator",
3707
- onMove: function (evt) {
3708
- return evt.related.className.indexOf("htmx-indicator") === -1;
3709
- },
3710
- });
3711
- sortable.sortableInstance = sortableInstance;
3712
- sortable.addEventListener("htmx:afterSwap", function () {
3713
- if (sortable.sortableInstance) {
3714
- sortable.sortableInstance.option("disabled", false);
3715
- }
3716
- });
3717
- }
3718
- });
3719
- }
3720
- </script>`;
3721
- }
3722
3457
  function Breadcrumb(props) {
3723
3458
  const { items } = props;
3724
3459
  if (items.length === 0) {
@@ -4074,7 +3809,7 @@ function SortableList(props) {
4074
3809
  function StringArrayEditor(props) {
4075
3810
  const {
4076
3811
  value,
4077
- fieldName,
3812
+ name,
4078
3813
  placeholder = "\u8BF7\u8F93\u5165\u5185\u5BB9",
4079
3814
  allowEmpty = false,
4080
3815
  rows = 1
@@ -4082,185 +3817,174 @@ function StringArrayEditor(props) {
4082
3817
  const initialItems = value || [];
4083
3818
  const initialDataJson = JSON.stringify({
4084
3819
  items: initialItems.map((item) => item || ""),
4085
- fieldName,
3820
+ fieldName: name,
4086
3821
  placeholder,
4087
3822
  allowEmpty,
4088
3823
  rows
4089
3824
  });
4090
- return /* @__PURE__ */ jsxRuntime.jsxs(
4091
- "div",
4092
- {
4093
- className: "space-y-3",
4094
- "x-data": initialDataJson,
4095
- children: [
4096
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
4097
- "button",
4098
- {
4099
- type: "button",
4100
- ...{
4101
- "x-on:click": `
3825
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", "x-data": initialDataJson, children: [
3826
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
3827
+ "button",
3828
+ {
3829
+ type: "button",
3830
+ ...{
3831
+ "x-on:click": `
4102
3832
  items.push('');
4103
3833
  `
4104
- },
4105
- className: "px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm font-medium flex items-center gap-2",
4106
- "data-testid": `${fieldName}-add-button`,
4107
- children: [
4108
- /* @__PURE__ */ jsxRuntime.jsx(
4109
- "svg",
3834
+ },
3835
+ className: "px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm font-medium flex items-center gap-2",
3836
+ "data-testid": `${name}-add-button`,
3837
+ children: [
3838
+ /* @__PURE__ */ jsxRuntime.jsx(
3839
+ "svg",
3840
+ {
3841
+ className: "w-4 h-4",
3842
+ fill: "none",
3843
+ stroke: "currentColor",
3844
+ viewBox: "0 0 24 24",
3845
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3846
+ "path",
4110
3847
  {
4111
- className: "w-4 h-4",
4112
- fill: "none",
4113
- stroke: "currentColor",
4114
- viewBox: "0 0 24 24",
4115
- children: /* @__PURE__ */ jsxRuntime.jsx(
4116
- "path",
4117
- {
4118
- strokeLinecap: "round",
4119
- strokeLinejoin: "round",
4120
- strokeWidth: "2",
4121
- d: "M12 4v16m8-8H4"
4122
- }
4123
- )
3848
+ strokeLinecap: "round",
3849
+ strokeLinejoin: "round",
3850
+ strokeWidth: "2",
3851
+ d: "M12 4v16m8-8H4"
4124
3852
  }
4125
- ),
4126
- "\u6DFB\u52A0\u9879"
4127
- ]
4128
- }
4129
- ) }),
4130
- /* @__PURE__ */ jsxRuntime.jsx(
4131
- "div",
4132
- {
4133
- "x-show": "items.length > 0",
4134
- "data-testid": `${fieldName}-list-container`,
4135
- ...{
4136
- "@sortable:change.stop": `
3853
+ )
3854
+ }
3855
+ ),
3856
+ "\u6DFB\u52A0\u9879"
3857
+ ]
3858
+ }
3859
+ ) }),
3860
+ /* @__PURE__ */ jsxRuntime.jsx(
3861
+ "div",
3862
+ {
3863
+ "x-show": "items.length > 0",
3864
+ "data-testid": `${name}-list-container`,
3865
+ ...{
3866
+ "@sortable:change.stop": `
4137
3867
  (function() {
4138
3868
  const { oldIndex, newIndex } = $event.detail;
4139
3869
  [items[oldIndex], items[newIndex]] = [items[newIndex], items[oldIndex]];
4140
3870
  })();
4141
3871
  `
4142
- },
4143
- children: /* @__PURE__ */ jsxRuntime.jsx(SortableList, { className: "space-y-2", handle: "[data-drag-handle]", children: /* @__PURE__ */ jsxRuntime.jsx("template", { "x-for": "(item, index) in items", "x-bind:key": "index", children: /* @__PURE__ */ jsxRuntime.jsx(ArrayItem, { fieldName, rows }) }) })
4144
- }
4145
- ),
4146
- /* @__PURE__ */ jsxRuntime.jsx(
4147
- "div",
4148
- {
4149
- className: "empty-state text-center py-8 text-gray-400 text-sm border border-dashed border-gray-300 rounded-lg",
4150
- "x-show": "items.length === 0",
4151
- "data-testid": `${fieldName}-empty-state`,
4152
- children: '\u6682\u65E0\u9879\uFF0C\u70B9\u51FB"\u6DFB\u52A0\u9879"\u6309\u94AE\u6DFB\u52A0'
4153
- }
4154
- )
4155
- ]
4156
- }
4157
- );
3872
+ },
3873
+ children: /* @__PURE__ */ jsxRuntime.jsx(SortableList, { className: "space-y-2", handle: "[data-drag-handle]", children: /* @__PURE__ */ jsxRuntime.jsx("template", { "x-for": "(item, index) in items", "x-bind:key": "index", children: /* @__PURE__ */ jsxRuntime.jsx(ArrayItem, { fieldName: name, rows }) }) })
3874
+ }
3875
+ ),
3876
+ /* @__PURE__ */ jsxRuntime.jsx(
3877
+ "div",
3878
+ {
3879
+ className: "empty-state text-center py-8 text-gray-400 text-sm border border-dashed border-gray-300 rounded-lg",
3880
+ "x-show": "items.length === 0",
3881
+ "data-testid": `${name}-empty-state`,
3882
+ children: '\u6682\u65E0\u9879\uFF0C\u70B9\u51FB"\u6DFB\u52A0\u9879"\u6309\u94AE\u6DFB\u52A0'
3883
+ }
3884
+ )
3885
+ ] });
4158
3886
  }
4159
- function ArrayItem({ fieldName, rows = 1 }) {
4160
- return /* @__PURE__ */ jsxRuntime.jsxs(
4161
- "div",
4162
- {
4163
- "data-array-item": true,
4164
- className: "flex items-center gap-2 group",
4165
- children: [
4166
- /* @__PURE__ */ jsxRuntime.jsx(
4167
- "div",
3887
+ function ArrayItem({
3888
+ fieldName,
3889
+ rows = 1
3890
+ }) {
3891
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-array-item": true, className: "flex items-center gap-2 group", children: [
3892
+ /* @__PURE__ */ jsxRuntime.jsx(
3893
+ "div",
3894
+ {
3895
+ className: "flex-shrink-0 cursor-move text-gray-400 hover:text-gray-600 transition-colors p-1",
3896
+ "data-drag-handle": true,
3897
+ "data-testid": `${fieldName}-drag-handle`,
3898
+ title: "\u62D6\u62FD\u6392\u5E8F",
3899
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3900
+ "svg",
4168
3901
  {
4169
- className: "flex-shrink-0 cursor-move text-gray-400 hover:text-gray-600 transition-colors p-1",
4170
- "data-drag-handle": true,
4171
- "data-testid": `${fieldName}-drag-handle`,
4172
- title: "\u62D6\u62FD\u6392\u5E8F",
3902
+ className: "w-5 h-5",
3903
+ fill: "none",
3904
+ stroke: "currentColor",
3905
+ viewBox: "0 0 24 24",
4173
3906
  children: /* @__PURE__ */ jsxRuntime.jsx(
4174
- "svg",
3907
+ "path",
4175
3908
  {
4176
- className: "w-5 h-5",
4177
- fill: "none",
4178
- stroke: "currentColor",
4179
- viewBox: "0 0 24 24",
4180
- children: /* @__PURE__ */ jsxRuntime.jsx(
4181
- "path",
4182
- {
4183
- strokeLinecap: "round",
4184
- strokeLinejoin: "round",
4185
- strokeWidth: "2",
4186
- d: "M4 8h16M4 16h16"
4187
- }
4188
- )
3909
+ strokeLinecap: "round",
3910
+ strokeLinejoin: "round",
3911
+ strokeWidth: "2",
3912
+ d: "M4 8h16M4 16h16"
4189
3913
  }
4190
3914
  )
4191
3915
  }
4192
- ),
4193
- rows === 1 ? /* @__PURE__ */ jsxRuntime.jsx(
4194
- "input",
4195
- {
4196
- type: "text",
4197
- "x-model": "items[index]",
4198
- "x-bind:placeholder": "placeholder + ' ' + (index + 1)",
4199
- className: "flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
4200
- "data-testid": `${fieldName}-input`,
4201
- "x-bind:required": "!allowEmpty"
4202
- }
4203
- ) : /* @__PURE__ */ jsxRuntime.jsx(
4204
- "textarea",
4205
- {
4206
- "x-model": "items[index]",
4207
- "x-bind:placeholder": "placeholder + ' ' + (index + 1)",
4208
- rows,
4209
- className: "flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-y",
4210
- "data-testid": `${fieldName}-input`,
4211
- "x-bind:required": "!allowEmpty"
4212
- }
4213
- ),
4214
- /* @__PURE__ */ jsxRuntime.jsx(
4215
- "input",
4216
- {
4217
- type: "hidden",
4218
- "x-bind:name": "fieldName + '[' + index + ']'",
4219
- "x-bind:value": "item"
4220
- }
4221
- ),
4222
- /* @__PURE__ */ jsxRuntime.jsx(
4223
- "button",
4224
- {
4225
- type: "button",
4226
- ...{
4227
- "x-on:click": `
3916
+ )
3917
+ }
3918
+ ),
3919
+ rows === 1 ? /* @__PURE__ */ jsxRuntime.jsx(
3920
+ "input",
3921
+ {
3922
+ type: "text",
3923
+ "x-model": "items[index]",
3924
+ "x-bind:placeholder": "placeholder + ' ' + (index + 1)",
3925
+ className: "flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
3926
+ "data-testid": `${fieldName}-input`,
3927
+ "x-bind:required": "!allowEmpty"
3928
+ }
3929
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
3930
+ "textarea",
3931
+ {
3932
+ "x-model": "items[index]",
3933
+ "x-bind:placeholder": "placeholder + ' ' + (index + 1)",
3934
+ rows,
3935
+ className: "flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-y",
3936
+ "data-testid": `${fieldName}-input`,
3937
+ "x-bind:required": "!allowEmpty"
3938
+ }
3939
+ ),
3940
+ /* @__PURE__ */ jsxRuntime.jsx(
3941
+ "input",
3942
+ {
3943
+ type: "hidden",
3944
+ "x-bind:name": "fieldName + '[' + index + ']'",
3945
+ "x-bind:value": "item"
3946
+ }
3947
+ ),
3948
+ /* @__PURE__ */ jsxRuntime.jsx(
3949
+ "button",
3950
+ {
3951
+ type: "button",
3952
+ ...{
3953
+ "x-on:click": `
4228
3954
  items.splice(index, 1);
4229
3955
  `
4230
- },
4231
- className: "flex-shrink-0 px-3 py-2 text-sm text-red-600 hover:bg-red-50 rounded-lg transition-colors",
4232
- "data-testid": `${fieldName}-remove-button`,
4233
- title: "\u5220\u9664\u6B64\u9879",
3956
+ },
3957
+ className: "flex-shrink-0 px-3 py-2 text-sm text-red-600 hover:bg-red-50 rounded-lg transition-colors",
3958
+ "data-testid": `${fieldName}-remove-button`,
3959
+ title: "\u5220\u9664\u6B64\u9879",
3960
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3961
+ "svg",
3962
+ {
3963
+ className: "w-5 h-5",
3964
+ fill: "none",
3965
+ stroke: "currentColor",
3966
+ viewBox: "0 0 24 24",
4234
3967
  children: /* @__PURE__ */ jsxRuntime.jsx(
4235
- "svg",
3968
+ "path",
4236
3969
  {
4237
- className: "w-5 h-5",
4238
- fill: "none",
4239
- stroke: "currentColor",
4240
- viewBox: "0 0 24 24",
4241
- children: /* @__PURE__ */ jsxRuntime.jsx(
4242
- "path",
4243
- {
4244
- strokeLinecap: "round",
4245
- strokeLinejoin: "round",
4246
- strokeWidth: "2",
4247
- d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
4248
- }
4249
- )
3970
+ strokeLinecap: "round",
3971
+ strokeLinejoin: "round",
3972
+ strokeWidth: "2",
3973
+ d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
4250
3974
  }
4251
3975
  )
4252
3976
  }
4253
3977
  )
4254
- ]
4255
- }
4256
- );
3978
+ }
3979
+ )
3980
+ ] });
4257
3981
  }
4258
3982
  function TagsEditor(props) {
4259
- const { value, fieldName, placeholder = "\u8F93\u5165\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u6DFB\u52A0" } = props;
3983
+ const { value, name, placeholder = "\u8F93\u5165\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u6DFB\u52A0" } = props;
4260
3984
  const initialTags = value || [];
4261
3985
  const initialDataJson = JSON.stringify({
4262
3986
  tags: initialTags.map((tag) => tag || ""),
4263
- fieldName,
3987
+ fieldName: name,
4264
3988
  newTag: "",
4265
3989
  editingIndex: null,
4266
3990
  editingValue: "",
@@ -4287,7 +4011,7 @@ function TagsEditor(props) {
4287
4011
  placeholder,
4288
4012
  autocomplete: "off",
4289
4013
  className: "w-full px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
4290
- "data-testid": `${fieldName}-input`
4014
+ "data-testid": `${name}-input`
4291
4015
  }
4292
4016
  ),
4293
4017
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4296,7 +4020,7 @@ function TagsEditor(props) {
4296
4020
  "x-show": "error && editingIndex === null",
4297
4021
  "x-text": "error",
4298
4022
  className: "text-red-600 text-sm p-2",
4299
- id: `${fieldName}-error`
4023
+ id: `${name}-error`
4300
4024
  }
4301
4025
  )
4302
4026
  ] }),
@@ -4304,7 +4028,7 @@ function TagsEditor(props) {
4304
4028
  "div",
4305
4029
  {
4306
4030
  "x-show": "tags.length > 0",
4307
- "data-testid": `${fieldName}-tags-container`,
4031
+ "data-testid": `${name}-tags-container`,
4308
4032
  ...{
4309
4033
  "@sortable:change.stop": `
4310
4034
  (function() {
@@ -4327,8 +4051,8 @@ function TagsEditor(props) {
4327
4051
  "x-bind:value": "editingIndex === index ? editingValue : tag"
4328
4052
  }
4329
4053
  ),
4330
- /* @__PURE__ */ jsxRuntime.jsx(TagItem, { fieldName }),
4331
- /* @__PURE__ */ jsxRuntime.jsx(TagItemEdit, { fieldName })
4054
+ /* @__PURE__ */ jsxRuntime.jsx(TagItem, { fieldName: name }),
4055
+ /* @__PURE__ */ jsxRuntime.jsx(TagItemEdit, { fieldName: name })
4332
4056
  ] }) })
4333
4057
  }
4334
4058
  )
@@ -4339,7 +4063,7 @@ function TagsEditor(props) {
4339
4063
  {
4340
4064
  className: "empty-state text-center py-4 text-gray-400 text-sm border border-dashed border-gray-300 rounded-md",
4341
4065
  "x-show": "tags.length === 0",
4342
- "data-testid": `${fieldName}-empty-state`,
4066
+ "data-testid": `${name}-empty-state`,
4343
4067
  children: "\u6682\u65E0\u6807\u7B7E\uFF0C\u5728\u4E0A\u65B9\u8F93\u5165\u6846\u4E2D\u8F93\u5165\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u6DFB\u52A0"
4344
4068
  }
4345
4069
  )
@@ -4520,7 +4244,7 @@ function TagItemEdit({ fieldName }) {
4520
4244
  );
4521
4245
  }
4522
4246
  function ObjectEditor(props) {
4523
- const { value, fieldName, objectSchema } = props;
4247
+ const { value, name, objectSchema } = props;
4524
4248
  if (!objectSchema) {
4525
4249
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 border border-yellow-300 rounded-lg bg-yellow-50 text-yellow-800 text-sm", children: "\u8BF7\u63D0\u4F9B objectSchema \u53C2\u6570\u4EE5\u4F7F\u7528\u5BF9\u8C61\u7F16\u8F91\u5668" });
4526
4250
  }
@@ -4540,7 +4264,7 @@ function ObjectEditor(props) {
4540
4264
  const initialValueJson = JSON.stringify(initialObject);
4541
4265
  JSON.stringify(fields.map((f) => f.name));
4542
4266
  const generateField = (field) => {
4543
- const fieldId = `${fieldName}-${field.name}`;
4267
+ const fieldId = `${name}-${field.name}`;
4544
4268
  const fieldValue = initialObject[field.name];
4545
4269
  const fieldValueStr = fieldValue === void 0 || fieldValue === null ? "" : typeof fieldValue === "object" ? JSON.stringify(fieldValue) : String(fieldValue);
4546
4270
  const requiredAttr = field.required ? "required" : "";
@@ -4550,11 +4274,11 @@ function ObjectEditor(props) {
4550
4274
  <input
4551
4275
  type="text"
4552
4276
  id="${fieldId}"
4553
- name="${fieldName}.${field.name}"
4277
+ name="${name}.${field.name}"
4554
4278
  value="${fieldValueStr}"
4555
4279
  class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
4556
- data-testid="${fieldName}-input-${field.name}"
4557
- oninput="updateObjectField('${fieldName}', '${field.name}', this.value, 'text', ${field.required})"
4280
+ data-testid="${name}-input-${field.name}"
4281
+ oninput="updateObjectField('${name}', '${field.name}', this.value, 'text', ${field.required})"
4558
4282
  ${requiredAttr}
4559
4283
  />
4560
4284
  `;
@@ -4562,11 +4286,11 @@ function ObjectEditor(props) {
4562
4286
  inputElement = html.html`
4563
4287
  <textarea
4564
4288
  id="${fieldId}"
4565
- name="${fieldName}.${field.name}"
4289
+ name="${name}.${field.name}"
4566
4290
  class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-y"
4567
4291
  rows="4"
4568
- data-testid="${fieldName}-input-${field.name}"
4569
- oninput="updateObjectField('${fieldName}', '${field.name}', this.value, 'text', ${field.required})"
4292
+ data-testid="${name}-input-${field.name}"
4293
+ oninput="updateObjectField('${name}', '${field.name}', this.value, 'text', ${field.required})"
4570
4294
  ${requiredAttr}
4571
4295
  >${fieldValueStr}</textarea>
4572
4296
  `;
@@ -4576,12 +4300,12 @@ function ObjectEditor(props) {
4576
4300
  <input
4577
4301
  type="number"
4578
4302
  id="${fieldId}"
4579
- name="${fieldName}.${field.name}"
4303
+ name="${name}.${field.name}"
4580
4304
  value="${fieldValueStr}"
4581
4305
  step="${step}"
4582
4306
  class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
4583
- data-testid="${fieldName}-input-${field.name}"
4584
- oninput="updateObjectField('${fieldName}', '${field.name}', this.value, 'number', ${field.required})"
4307
+ data-testid="${name}-input-${field.name}"
4308
+ oninput="updateObjectField('${name}', '${field.name}', this.value, 'number', ${field.required})"
4585
4309
  ${requiredAttr}
4586
4310
  />
4587
4311
  `;
@@ -4590,11 +4314,11 @@ function ObjectEditor(props) {
4590
4314
  <input
4591
4315
  type="date"
4592
4316
  id="${fieldId}"
4593
- name="${fieldName}.${field.name}"
4317
+ name="${name}.${field.name}"
4594
4318
  value="${fieldValueStr}"
4595
4319
  class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
4596
- data-testid="${fieldName}-input-${field.name}"
4597
- oninput="updateObjectField('${fieldName}', '${field.name}', this.value, 'date', ${field.required})"
4320
+ data-testid="${name}-input-${field.name}"
4321
+ oninput="updateObjectField('${name}', '${field.name}', this.value, 'date', ${field.required})"
4598
4322
  ${requiredAttr}
4599
4323
  />
4600
4324
  `;
@@ -4603,11 +4327,11 @@ function ObjectEditor(props) {
4603
4327
  <input
4604
4328
  type="email"
4605
4329
  id="${fieldId}"
4606
- name="${fieldName}.${field.name}"
4330
+ name="${name}.${field.name}"
4607
4331
  value="${fieldValueStr}"
4608
4332
  class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
4609
- data-testid="${fieldName}-input-${field.name}"
4610
- oninput="updateObjectField('${fieldName}', '${field.name}', this.value, 'text', ${field.required})"
4333
+ data-testid="${name}-input-${field.name}"
4334
+ oninput="updateObjectField('${name}', '${field.name}', this.value, 'text', ${field.required})"
4611
4335
  ${requiredAttr}
4612
4336
  />
4613
4337
  `;
@@ -4615,10 +4339,10 @@ function ObjectEditor(props) {
4615
4339
  inputElement = html.html`
4616
4340
  <select
4617
4341
  id="${fieldId}"
4618
- name="${fieldName}.${field.name}"
4342
+ name="${name}.${field.name}"
4619
4343
  class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white"
4620
- data-testid="${fieldName}-select-${field.name}"
4621
- onchange="updateObjectField('${fieldName}', '${field.name}', this.value, 'text', ${field.required})"
4344
+ data-testid="${name}-select-${field.name}"
4345
+ onchange="updateObjectField('${name}', '${field.name}', this.value, 'text', ${field.required})"
4622
4346
  ${requiredAttr}
4623
4347
  >
4624
4348
  ${!field.required ? html.html`<option value="">请选择</option>` : ""}
@@ -4641,11 +4365,11 @@ function ObjectEditor(props) {
4641
4365
  <input
4642
4366
  type="checkbox"
4643
4367
  id="${fieldId}"
4644
- name="${fieldName}.${field.name}"
4368
+ name="${name}.${field.name}"
4645
4369
  ${checked ? "checked" : ""}
4646
4370
  class="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
4647
- data-testid="${fieldName}-checkbox-${field.name}"
4648
- onchange="updateObjectField('${fieldName}', '${field.name}', this.checked, 'checkbox', ${field.required}')"
4371
+ data-testid="${name}-checkbox-${field.name}"
4372
+ onchange="updateObjectField('${name}', '${field.name}', this.checked, 'checkbox', ${field.required}')"
4649
4373
  />
4650
4374
  <label for="${fieldId}" class="ml-2 text-sm text-gray-700">
4651
4375
  ${field.label}
@@ -4654,12 +4378,12 @@ function ObjectEditor(props) {
4654
4378
  `;
4655
4379
  }
4656
4380
  return html.html`
4657
- <div class="space-y-2" data-testid="${fieldName}-field-${field.name}">
4381
+ <div class="space-y-2" data-testid="${name}-field-${field.name}">
4658
4382
  ${field.type !== "checkbox" ? html.html`
4659
4383
  <label
4660
4384
  for="${fieldId}"
4661
4385
  class="block text-sm font-semibold text-gray-700"
4662
- data-testid="${fieldName}-label-${field.name}"
4386
+ data-testid="${name}-label-${field.name}"
4663
4387
  >
4664
4388
  ${field.label}
4665
4389
  ${field.required ? html.html`<span class="text-red-500 ml-1">*</span>` : ""}
@@ -4671,15 +4395,15 @@ function ObjectEditor(props) {
4671
4395
  };
4672
4396
  return html.html`
4673
4397
  <div
4674
- id="object-editor-${fieldName}"
4398
+ id="object-editor-${name}"
4675
4399
  class="space-y-4"
4676
4400
  data-initial-value="${initialValueJson}"
4677
4401
  >
4678
4402
  <input
4679
4403
  type="hidden"
4680
- name="${fieldName}"
4404
+ name="${name}"
4681
4405
  value="${initialValueJson}"
4682
- data-testid="hidden-${fieldName}"
4406
+ data-testid="hidden-${name}"
4683
4407
  />
4684
4408
  <div class="space-y-4">
4685
4409
  ${fields.map((field) => generateField(field))}
@@ -4741,14 +4465,13 @@ function BaseLayout(props) {
4741
4465
  <meta name="description" content="${props.description || ""}" />
4742
4466
  ${globalScripts(props.prefix)} ${globalStyles()}
4743
4467
  </head>
4744
- <body hx-ext="morph" className="bg-gray-50" hx-indicator="#loading-bar">
4468
+ <body className="bg-gray-50" hx-indicator="#loading-bar">
4745
4469
  ${LoadingBar()} ${props.children}
4746
4470
  <div
4747
- id="error-container"
4748
- className="fixed top-4 right-4 z-[200] w-full max-w-2xl px-4"
4471
+ id="notification-container"
4472
+ style="position: fixed; top: 1rem; right: 1rem; z-index: 200; display: flex; flex-direction: column; gap: 0.5rem; pointer-events: none; width: 100%; max-width: 28rem; padding-left: 1rem; padding-right: 1rem;"
4749
4473
  ></div>
4750
4474
  <div id="dialog-container"></div>
4751
- ${sortableScript()}
4752
4475
  </body>
4753
4476
  </html>
4754
4477
  `;
@@ -5000,7 +4723,7 @@ async function handlePermissionDenied(ctx, result, options) {
5000
4723
  if (isHtmxRequest) {
5001
4724
  const headers = {
5002
4725
  "HX-Retarget": "#dialog-container",
5003
- "HX-Reswap": "innerHTML",
4726
+ "HX-Reswap": "beforeend",
5004
4727
  "X-Permission-Denied": "true"
5005
4728
  };
5006
4729
  return ctx.html(
@@ -5045,22 +4768,15 @@ function buildNotificationFragments(notifications) {
5045
4768
  if (notifications.length === 0) {
5046
4769
  return null;
5047
4770
  }
5048
- return notifications.map((notification, index) => /* @__PURE__ */ jsxRuntime.jsx(
5049
- "div",
4771
+ return notifications.map((notification, index) => /* @__PURE__ */ jsxRuntime.jsx("div", { id: "notification-container", "hx-swap-oob": "beforeend", children: /* @__PURE__ */ jsxRuntime.jsx(
4772
+ ErrorAlert,
5050
4773
  {
5051
- id: "error-container",
5052
- "hx-swap-oob": "beforeend",
5053
- children: /* @__PURE__ */ jsxRuntime.jsx(
5054
- ErrorAlert,
5055
- {
5056
- type: notification.type,
5057
- title: notification.title,
5058
- message: notification.message
5059
- }
5060
- )
4774
+ type: notification.type,
4775
+ title: notification.title,
4776
+ message: notification.message
5061
4777
  },
5062
4778
  `notification-${index}`
5063
- ));
4779
+ ) }));
5064
4780
  }
5065
4781
  function isEmptyContent(result) {
5066
4782
  if (result === null || result === void 0) return true;
@@ -5154,7 +4870,6 @@ async function renderResult(ctx, context, result, renderOptions) {
5154
4870
  if (context.isDialog) {
5155
4871
  return ctx.html(
5156
4872
  /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5157
- /* @__PURE__ */ jsxRuntime.jsx("div", { id: "dialog-container", "hx-swap-oob": "innerHTML" }),
5158
4873
  notificationFragments,
5159
4874
  /* @__PURE__ */ jsxRuntime.jsx("title", { children: metadata.title })
5160
4875
  ] }),
@@ -5210,7 +4925,7 @@ async function renderResult(ctx, context, result, renderOptions) {
5210
4925
  );
5211
4926
  }
5212
4927
  const target = context.isDialog ? "#dialog-container" : "#main-content";
5213
- const swap = context.isDialog ? "innerHTML" : "outerHTML";
4928
+ const swap = context.isDialog ? "beforeend" : "outerHTML";
5214
4929
  const headers = {
5215
4930
  "HX-Retarget": target,
5216
4931
  "HX-Reswap": swap
@@ -5273,7 +4988,6 @@ async function renderResult(ctx, context, result, renderOptions) {
5273
4988
  useAdminLayout,
5274
4989
  currentPath,
5275
4990
  userInfo: user,
5276
- componentRegistry: renderOptions.componentRegistry,
5277
4991
  breadcrumbs,
5278
4992
  actions,
5279
4993
  children: result
@@ -5309,7 +5023,6 @@ async function renderResult(ctx, context, result, renderOptions) {
5309
5023
  prefix: options.prefix,
5310
5024
  title: dynamicMetadata.title,
5311
5025
  description: dynamicMetadata.description,
5312
- componentRegistry: renderOptions.componentRegistry,
5313
5026
  children: /* @__PURE__ */ jsxRuntime.jsx(
5314
5027
  AdminLayout,
5315
5028
  {
@@ -5321,7 +5034,6 @@ async function renderResult(ctx, context, result, renderOptions) {
5321
5034
  userInfo: user,
5322
5035
  breadcrumbs,
5323
5036
  actions,
5324
- componentRegistry: renderOptions.componentRegistry,
5325
5037
  children: result
5326
5038
  }
5327
5039
  )
@@ -5336,7 +5048,6 @@ async function renderResult(ctx, context, result, renderOptions) {
5336
5048
  prefix: options.prefix,
5337
5049
  title: dynamicMetadata.title,
5338
5050
  description: dynamicMetadata.description,
5339
- componentRegistry: renderOptions.componentRegistry,
5340
5051
  children: /* @__PURE__ */ jsxRuntime.jsx(NoLayout, { children: result })
5341
5052
  }
5342
5053
  )
@@ -5425,14 +5136,15 @@ async function handleRequest(ctx, page, feature, handlerOptions) {
5425
5136
  if (isHtmxRequest) {
5426
5137
  return ctx.html(
5427
5138
  /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5428
- /* @__PURE__ */ jsxRuntime.jsx("div", { id: "error-container", "hx-swap-oob": "beforeend", children: /* @__PURE__ */ jsxRuntime.jsx(
5139
+ /* @__PURE__ */ jsxRuntime.jsx(
5429
5140
  ErrorAlert,
5430
5141
  {
5142
+ "hx-swap-oob": "beforeend:#notification-container",
5431
5143
  type: "error",
5432
5144
  title: "\u8BF7\u6C42\u5931\u8D25",
5433
5145
  message: "Feature has no handler or render method"
5434
5146
  }
5435
- ) }),
5147
+ ),
5436
5148
  /* @__PURE__ */ jsxRuntime.jsx("title", { children: "\u9519\u8BEF" })
5437
5149
  ] }),
5438
5150
  500,
@@ -5449,10 +5161,15 @@ async function handleRequest(ctx, page, feature, handlerOptions) {
5449
5161
  if (isHtmxRequest) {
5450
5162
  const errorMessage = error instanceof Error ? error.message : "Internal server error";
5451
5163
  return ctx.html(
5452
- /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5453
- /* @__PURE__ */ jsxRuntime.jsx("div", { id: "error-container", "hx-swap-oob": "beforeend", children: /* @__PURE__ */ jsxRuntime.jsx(ErrorAlert, { type: "error", title: "\u8BF7\u6C42\u5931\u8D25", message: errorMessage }) }),
5454
- /* @__PURE__ */ jsxRuntime.jsx("title", { children: "\u9519\u8BEF" })
5455
- ] }),
5164
+ /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(
5165
+ ErrorAlert,
5166
+ {
5167
+ "hx-swap-oob": "beforeend:#notification-container",
5168
+ type: "error",
5169
+ title: "\u8BF7\u6C42\u5931\u8D25",
5170
+ message: errorMessage
5171
+ }
5172
+ ) }),
5456
5173
  500,
5457
5174
  {
5458
5175
  "HX-Reswap": "none"
@@ -5555,7 +5272,6 @@ var HtmxAdminPlugin = class {
5555
5272
  options;
5556
5273
  serviceName = "";
5557
5274
  pages = /* @__PURE__ */ new Map();
5558
- componentHandler;
5559
5275
  constructor(options) {
5560
5276
  this.options = {
5561
5277
  title: options?.title || "\u7BA1\u7406\u540E\u53F0",
@@ -5564,8 +5280,7 @@ var HtmxAdminPlugin = class {
5564
5280
  homePath: options?.homePath || "",
5565
5281
  navigation: options?.navigation ?? [],
5566
5282
  authProvider: options?.authProvider,
5567
- pages: options?.pages ?? [],
5568
- components: options?.components ?? []
5283
+ pages: options?.pages ?? []
5569
5284
  };
5570
5285
  this.initPages();
5571
5286
  }
@@ -5597,11 +5312,6 @@ var HtmxAdminPlugin = class {
5597
5312
  imeanServiceEngine.logger.info(
5598
5313
  `HtmxAdminPlugin initialized${this.serviceName ? ` (service: ${this.serviceName})` : ""}`
5599
5314
  );
5600
- this.componentHandler = new HtmxComponentHandler(
5601
- this.hono,
5602
- this.options.prefix,
5603
- this.options.components
5604
- );
5605
5315
  initializeCdnCache().catch((error) => {
5606
5316
  imeanServiceEngine.logger.error("[HtmxAdminPlugin] CDN \u7F13\u5B58\u521D\u59CB\u5316\u5931\u8D25", error);
5607
5317
  });
@@ -5624,7 +5334,6 @@ var HtmxAdminPlugin = class {
5624
5334
  };
5625
5335
 
5626
5336
  exports.BaseFeature = BaseFeature;
5627
- exports.ComponentContext = ComponentContext;
5628
5337
  exports.CustomFeature = CustomFeature;
5629
5338
  exports.DefaultCreateFeature = DefaultCreateFeature;
5630
5339
  exports.DefaultDeleteFeature = DefaultDeleteFeature;
@@ -5634,12 +5343,10 @@ exports.DefaultListFeature = DefaultListFeature;
5634
5343
  exports.Dialog = Dialog;
5635
5344
  exports.ErrorAlert = ErrorAlert;
5636
5345
  exports.HtmxAdminPlugin = HtmxAdminPlugin;
5637
- exports.HtmxComponent = HtmxComponent;
5638
5346
  exports.LoadingBar = LoadingBar;
5639
- exports.Method = Method;
5640
5347
  exports.ObjectEditor = ObjectEditor;
5641
5348
  exports.PageModel = PageModel;
5642
- exports.RenderContext = RenderContext;
5349
+ exports.SortableList = SortableList;
5643
5350
  exports.StringArrayEditor = StringArrayEditor;
5644
5351
  exports.TagsEditor = TagsEditor;
5645
5352
  exports.checkUserPermission = checkUserPermission;