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