cabloy 5.1.141 → 5.1.142

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.
Files changed (92) hide show
  1. package/.cabloy-version +1 -1
  2. package/.github/workflows/playwright-e2e.yml +2 -2
  3. package/CHANGELOG.md +21 -0
  4. package/cabloy-docs/frontend/form-guide.md +47 -0
  5. package/cabloy-docs/frontend/markdown-guide.md +50 -17
  6. package/cabloy-docs/frontend/route-alias-guide.md +20 -0
  7. package/cabloy-docs/frontend/scripts.md +9 -11
  8. package/cabloy-docs/frontend/zova-form-under-the-hood.md +25 -0
  9. package/cabloy-docs/fullstack/parallel-worktree-environment.md +1 -1
  10. package/cabloy-docs/fullstack/quickstart.md +5 -4
  11. package/cabloy-docs/reference/repo-scripts.md +29 -42
  12. package/e2e/config/playwright.config.ts +34 -0
  13. package/e2e/scripts/e2e.ts +2 -27
  14. package/e2e/scripts/runE2e.ts +62 -0
  15. package/e2e/scripts/runE2eArgs.test.ts +75 -0
  16. package/e2e/scripts/runE2eArgs.ts +158 -0
  17. package/e2e/specs/{a-commerce/commerce.spec.ts → a-commerce.spec.ts} +361 -30
  18. package/e2e/specs/{cabloy-basic/account.spec.ts → account.spec.ts} +1 -2
  19. package/oxlint.config.ts +3 -0
  20. package/package.json +3 -9
  21. package/scripts/upgrade.ts +63 -68
  22. package/vona/packages-utils/lint/package.json +1 -1
  23. package/vona/packages-utils/lint/src/oxc/lint.ts +1 -1
  24. package/vona/packages-utils/lint/src/oxc/lintVue.ts +1 -1
  25. package/vona/pnpm-lock.yaml +41 -41
  26. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/refundRecoveryView.tsx +7 -2
  27. package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +31 -15
  28. package/vona/src/suite/a-home/modules/home-user/package.json +1 -0
  29. package/vona/src/suite/a-home/modules/home-user/src/service/account.ts +15 -2
  30. package/vona/src/suite/a-home/modules/home-user/test/accountActivation.test.ts +34 -4
  31. package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/package.json +2 -1
  32. package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/src/.metadata/index.ts +26 -0
  33. package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/src/bean/imageScene.markdown.ts +13 -0
  34. package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
  35. package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  36. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/providerOperation.ts +23 -0
  37. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/refundOperation.ts +7 -0
  38. package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
  39. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +1 -1
  40. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +1 -1
  41. package/vona/src/suite-vendor/a-pay/package.json +5 -5
  42. package/zova/packages-utils/zova-jsx/package.json +2 -2
  43. package/zova/packages-zova/zova/package.json +3 -3
  44. package/zova/packages-zova/zova-core/package.json +1 -1
  45. package/zova/packages-zova/zova-core/src/bean/resource/locale/type.ts +1 -0
  46. package/zova/pnpm-lock.yaml +63 -18
  47. package/zova/src/front/config/config/config.cabloyBasicWeb.ts +3 -1
  48. package/zova/src/front/config/config/config.cabloyCommerce.ts +4 -2
  49. package/zova/src/suite/a-commerce/modules/commerce-member/src/page/address/controller.tsx +64 -63
  50. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +6 -1
  51. package/zova/src/suite/a-commerce/modules/commerce-trade/src/component/tableCellActionRefund/controller.tsx +413 -134
  52. package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/en-us.ts +76 -0
  53. package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/zh-cn.ts +67 -0
  54. package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +1 -0
  55. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/cart/controller.tsx +1 -1
  56. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +7 -7
  57. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/order/controller.tsx +46 -9
  58. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +50 -30
  59. package/zova/src/suite/cabloy-basic/modules/basic-captcha/src/component/formFieldCaptcha/controller.tsx +6 -2
  60. package/zova/src/suite/cabloy-basic/modules/basic-image/src/component/formFieldImage/controller.tsx +9 -15
  61. package/zova/src/suite/cabloy-basic/modules/basic-markdown/package.json +10 -2
  62. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/.metadata/index.ts +11 -2
  63. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/.metadata/locales.ts +7 -0
  64. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/formFieldMarkdown/controller.tsx +723 -5
  65. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/formFieldMarkdown/render.tsx +461 -2
  66. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/config/locale/en-us.ts +59 -0
  67. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/config/locale/zh-cn.ts +59 -0
  68. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/index.ts +1 -0
  69. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/lib/codeBlockLanguages.ts +42 -0
  70. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/lib/richTextContentStyle.ts +20 -0
  71. package/zova/src/suite/cabloy-basic/modules/basic-markdown/test/lib/codeBlockLanguages.test.ts +25 -0
  72. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
  73. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +1 -0
  74. package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
  75. package/zova/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  76. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/component/paymentNextAction/controller.tsx +26 -12
  77. package/zova/src/suite-vendor/a-pay/modules/pay-mock/cli/openapi.config.ts +1 -1
  78. package/zova/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
  79. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/payMockMockPayment.ts +26 -0
  80. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/apiSchema/payMockMockPayment.ts +8 -1
  81. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/model/payMockPayment.ts +13 -1
  82. package/zova/src/suite-vendor/a-pay/package.json +3 -3
  83. package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
  84. package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/formField/controller.tsx +2 -0
  85. package/zova/src/suite-vendor/a-zova/modules/a-form/src/types/formField.ts +7 -6
  86. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
  87. package/zova/src/suite-vendor/a-zova/package.json +3 -3
  88. package/e2e/config/playwright.basic.config.ts +0 -3
  89. package/e2e/config/playwright.commerce.config.ts +0 -3
  90. package/e2e/config/playwright.shared.config.ts +0 -38
  91. package/e2e/scripts/testE2eClean.ts +0 -56
  92. /package/e2e/specs/{cabloy-basic/basic.spec.ts → cabloy-basic.spec.ts} +0 -0
@@ -1,10 +1,445 @@
1
+ import type { IIconRecord } from 'zova-module-a-icon';
2
+ import type { IImageUploaderRenderState } from 'zova-module-basic-image';
3
+
1
4
  import { EditorContent } from '@tiptap/vue-3';
2
5
  import { BeanRenderBase, ClientOnly } from 'zova';
3
6
  import { Render } from 'zova-module-a-bean';
4
7
  import { ZFormField } from 'zova-module-a-form';
8
+ import { ZIcon } from 'zova-module-a-icon';
9
+ import { ZImageUploader } from 'zova-module-basic-image';
10
+
11
+ import { codeBlockLanguages, getCodeBlockLanguage } from '../../lib/codeBlockLanguages.js';
5
12
 
6
13
  @Render()
7
14
  export class RenderFormFieldMarkdown extends BeanRenderBase {
15
+ private _toolbarButton(
16
+ name: keyof IIconRecord,
17
+ label: string,
18
+ action: () => void,
19
+ options: { active?: boolean; disabled?: boolean } = {},
20
+ ) {
21
+ return (
22
+ <button
23
+ type="button"
24
+ class={['btn btn-ghost btn-sm btn-square', options.active && 'btn-active text-primary']}
25
+ aria-label={label}
26
+ title={label}
27
+ aria-pressed={options.active}
28
+ disabled={options.disabled}
29
+ onMousedown={event => {
30
+ event.preventDefault();
31
+ }}
32
+ onClick={event => {
33
+ event.stopPropagation();
34
+ action();
35
+ }}
36
+ >
37
+ <ZIcon name={name} width={18} height={18}></ZIcon>
38
+ </button>
39
+ );
40
+ }
41
+
42
+ private _renderTablePicker() {
43
+ if (!this.tablePickerOpen) return null;
44
+ const { rows, cols } = this.tablePickerPreview;
45
+ const maxSize = this.tablePickerMaxSize;
46
+ return (
47
+ <div
48
+ id="markdown-table-size-picker"
49
+ ref={ref => {
50
+ if (this.ctx.disposed) return;
51
+ this.setTablePickerRoot?.(ref as HTMLDivElement | null);
52
+ }}
53
+ class="absolute right-0 top-full z-20 mt-1 rounded-box border border-base-300 bg-base-100 p-3 shadow-lg"
54
+ role="dialog"
55
+ aria-label={this.scope.locale.TableSizePicker()}
56
+ >
57
+ <div
58
+ class="mb-2 text-center text-sm text-base-content/70"
59
+ aria-live="polite"
60
+ aria-atomic="true"
61
+ >
62
+ {this.scope.locale.TableSizeStatus()}: {rows} × {cols}
63
+ </div>
64
+ <div
65
+ role="grid"
66
+ aria-label={this.scope.locale.TableSizePicker()}
67
+ aria-rowcount={maxSize}
68
+ aria-colcount={maxSize}
69
+ class="grid gap-1"
70
+ style={{ gridTemplateColumns: `repeat(${maxSize}, minmax(1.25rem, 1fr))` }}
71
+ >
72
+ {Array.from({ length: maxSize }, (_, rowIndex) =>
73
+ Array.from({ length: maxSize }, (_, colIndex) => {
74
+ const row = rowIndex + 1;
75
+ const col = colIndex + 1;
76
+ const selected = row <= rows && col <= cols;
77
+ const active =
78
+ row === this.tablePickerActive.rows && col === this.tablePickerActive.cols;
79
+ const label = `${row} × ${col} ${this.scope.locale.TableSizeCell()}`;
80
+ return (
81
+ <button
82
+ key={`${row}-${col}`}
83
+ type="button"
84
+ role="gridcell"
85
+ data-table-picker-cell={`${row}-${col}`}
86
+ aria-label={label}
87
+ aria-rowindex={row}
88
+ aria-colindex={col}
89
+ aria-selected={selected}
90
+ tabindex={active ? 0 : -1}
91
+ class={[
92
+ 'h-5 w-5 rounded-sm border border-base-300 p-0 transition-colors',
93
+ selected && 'border-primary bg-primary',
94
+ !selected && 'bg-base-200 hover:border-primary hover:bg-primary/30',
95
+ active && 'outline outline-2 outline-primary outline-offset-1',
96
+ ]}
97
+ onMousedown={event => {
98
+ event.preventDefault();
99
+ }}
100
+ onPointerenter={() => {
101
+ this.setTablePickerPreview(row, col);
102
+ }}
103
+ onFocus={() => {
104
+ this.setTablePickerPreview(row, col);
105
+ }}
106
+ onClick={event => {
107
+ event.stopPropagation();
108
+ this.selectTablePickerSize(row, col);
109
+ }}
110
+ ></button>
111
+ );
112
+ }),
113
+ )}
114
+ </div>
115
+ </div>
116
+ );
117
+ }
118
+
119
+ private _renderTablePickerTrigger() {
120
+ return (
121
+ <div class="relative">
122
+ <button
123
+ type="button"
124
+ aria-label={this.scope.locale.InsertTable()}
125
+ title={this.scope.locale.InsertTable()}
126
+ aria-haspopup="grid"
127
+ aria-expanded={this.tablePickerOpen}
128
+ aria-controls="markdown-table-size-picker"
129
+ disabled={!this.toolbarState.canTable}
130
+ class="btn btn-ghost btn-sm btn-square"
131
+ onMousedown={event => {
132
+ event.preventDefault();
133
+ }}
134
+ onClick={event => {
135
+ event.stopPropagation();
136
+ this.toggleTablePicker();
137
+ }}
138
+ ref={ref => {
139
+ if (this.ctx.disposed) return;
140
+ this.setTablePickerTrigger?.(ref as HTMLButtonElement | null);
141
+ }}
142
+ >
143
+ <ZIcon name=":editor:grid-on" width={18} height={18}></ZIcon>
144
+ </button>
145
+ {this._renderTablePicker()}
146
+ </div>
147
+ );
148
+ }
149
+
150
+ private _renderCodeBlockLanguageSelect() {
151
+ const state = this.toolbarState;
152
+ return (
153
+ <select
154
+ class="select select-sm select-bordered w-auto min-w-28"
155
+ aria-label={this.scope.locale.CodeBlockLanguage()}
156
+ value={getCodeBlockLanguage(state.codeBlockLanguage) ?? ''}
157
+ onMousedown={event => {
158
+ event.stopPropagation();
159
+ this.setCodeBlockToolbarInteracting(true);
160
+ }}
161
+ onClick={event => {
162
+ event.stopPropagation();
163
+ this.setCodeBlockToolbarInteracting(true);
164
+ }}
165
+ onFocus={() => {
166
+ this.setCodeBlockToolbarInteracting(true);
167
+ }}
168
+ onChange={event => {
169
+ event.stopPropagation();
170
+ const value = (event.target as HTMLSelectElement).value;
171
+ this.setCodeBlockLanguage(value || undefined);
172
+ }}
173
+ >
174
+ <option value="">{this.scope.locale.PlainText()}</option>
175
+ {codeBlockLanguages.map(language => (
176
+ <option key={language} value={language}>
177
+ {this.scope.locale[language]()}
178
+ </option>
179
+ ))}
180
+ </select>
181
+ );
182
+ }
183
+
184
+ private _renderToolbar(imageUploader?: IImageUploaderRenderState) {
185
+ const state = this.toolbarState;
186
+ return (
187
+ <div
188
+ class="flex flex-wrap items-center gap-1 border-b border-base-300 p-2"
189
+ role="toolbar"
190
+ aria-label={this.scope.locale.MarkdownToolbar()}
191
+ >
192
+ {this._toolbarButton(':editor:undo', this.scope.locale.Undo(), () => this.undo(), {
193
+ disabled: !state.canUndo,
194
+ })}
195
+ {this._toolbarButton(':editor:redo', this.scope.locale.Redo(), () => this.redo(), {
196
+ disabled: !state.canRedo,
197
+ })}
198
+ <select
199
+ class="select select-sm select-bordered w-auto min-w-28"
200
+ aria-label={this.scope.locale.BlockStyle()}
201
+ value={state.headingLevel ? `heading-${state.headingLevel}` : 'paragraph'}
202
+ onMousedown={event => {
203
+ event.stopPropagation();
204
+ }}
205
+ onChange={event => {
206
+ event.stopPropagation();
207
+ const value = (event.target as HTMLSelectElement).value;
208
+ if (value === 'paragraph') {
209
+ this.setParagraph();
210
+ } else if (value.startsWith('heading-')) {
211
+ this.setHeading(Number(value.slice('heading-'.length)) as 1 | 2 | 3 | 4 | 5 | 6);
212
+ }
213
+ }}
214
+ >
215
+ <option value="paragraph">{this.scope.locale.Paragraph()}</option>
216
+ <option value="heading-1">{this.scope.locale.Heading1()}</option>
217
+ <option value="heading-2">{this.scope.locale.Heading2()}</option>
218
+ <option value="heading-3">{this.scope.locale.Heading3()}</option>
219
+ <option value="heading-4">{this.scope.locale.Heading4()}</option>
220
+ <option value="heading-5">{this.scope.locale.Heading5()}</option>
221
+ <option value="heading-6">{this.scope.locale.Heading6()}</option>
222
+ </select>
223
+ {this._toolbarButton(
224
+ ':editor:format-bold',
225
+ this.scope.locale.Bold(),
226
+ () => this.toggleBold(),
227
+ { active: state.bold, disabled: !state.canBold },
228
+ )}
229
+ {this._toolbarButton(
230
+ ':editor:format-italic',
231
+ this.scope.locale.Italic(),
232
+ () => this.toggleItalic(),
233
+ { active: state.italic, disabled: !state.canItalic },
234
+ )}
235
+ {this._toolbarButton(
236
+ ':editor:format-strikethrough',
237
+ this.scope.locale.Strikethrough(),
238
+ () => this.toggleStrike(),
239
+ { active: state.strike, disabled: !state.canStrike },
240
+ )}
241
+ {this._toolbarButton(
242
+ ':editor:code',
243
+ this.scope.locale.InlineCode(),
244
+ () => this.toggleCode(),
245
+ { active: state.code, disabled: !state.canCode },
246
+ )}
247
+ {this._toolbarButton(
248
+ ':editor:insert-link-outline',
249
+ this.scope.locale.Link(),
250
+ () => {
251
+ void this.editLink();
252
+ },
253
+ { active: state.link, disabled: !state.canLink },
254
+ )}
255
+ {this._toolbarButton(
256
+ ':editor:bookmark',
257
+ this.scope.locale.Highlight(),
258
+ () => this.toggleHighlight(),
259
+ { active: state.highlight, disabled: !state.canHighlight },
260
+ )}
261
+ {this._toolbarButton(
262
+ ':editor:format-list-bulleted',
263
+ this.scope.locale.BulletList(),
264
+ () => this.toggleBulletList(),
265
+ { active: state.bulletList, disabled: !state.canBulletList },
266
+ )}
267
+ {this._toolbarButton(
268
+ ':editor:format-list-numbered',
269
+ this.scope.locale.OrderedList(),
270
+ () => this.toggleOrderedList(),
271
+ { active: state.orderedList, disabled: !state.canOrderedList },
272
+ )}
273
+ {this._toolbarButton(
274
+ ':editor:task-alt',
275
+ this.scope.locale.TaskList(),
276
+ () => this.toggleTaskList(),
277
+ { active: state.taskList, disabled: !state.canTaskList },
278
+ )}
279
+ {this._toolbarButton(
280
+ ':editor:format-quote',
281
+ this.scope.locale.Blockquote(),
282
+ () => this.toggleBlockquote(),
283
+ { active: state.blockquote, disabled: !state.canBlockquote },
284
+ )}
285
+ {this._toolbarButton(
286
+ ':editor:code-block',
287
+ this.scope.locale.CodeBlock(),
288
+ () => this.toggleCodeBlock(),
289
+ { active: state.codeBlock, disabled: !state.canCodeBlock },
290
+ )}
291
+ {this._toolbarButton(
292
+ ':editor:horizontal-rule',
293
+ this.scope.locale.HorizontalRule(),
294
+ () => this.setHorizontalRule(),
295
+ { disabled: !state.canHorizontalRule },
296
+ )}
297
+ {imageUploader &&
298
+ this._toolbarButton(
299
+ ':editor:image-outline',
300
+ this.scope.locale.InsertImage(),
301
+ () => {
302
+ this.beginImageUpload(imageUploader.chooseFiles);
303
+ },
304
+ {
305
+ disabled: imageUploader.isUploading || imageUploader.policy.pending,
306
+ },
307
+ )}
308
+ {this._renderTablePickerTrigger()}
309
+ </div>
310
+ );
311
+ }
312
+
313
+ private _renderCodeBlockToolbar() {
314
+ const position = this.codeBlockToolbarPosition;
315
+ return (
316
+ <div
317
+ class="pointer-events-none absolute inset-0 z-10"
318
+ ref={ref => {
319
+ if (this.ctx.disposed) return;
320
+ this.setCodeBlockToolbarHost?.(ref as HTMLDivElement | null);
321
+ }}
322
+ >
323
+ {position && (
324
+ <div
325
+ class="pointer-events-auto absolute flex items-center rounded-box border border-base-300 bg-base-100 p-1 shadow"
326
+ onPointerenter={() => {
327
+ this.setCodeBlockToolbarHovered(true);
328
+ }}
329
+ onPointerleave={() => {
330
+ this.setCodeBlockToolbarHovered(false);
331
+ }}
332
+ style={{
333
+ left: `${position.left}px`,
334
+ top: `${position.top}px`,
335
+ transform: 'translate(-50%, -100%)',
336
+ }}
337
+ role="toolbar"
338
+ aria-label={this.scope.locale.CodeBlockLanguage()}
339
+ onFocus={() => {
340
+ this.setCodeBlockToolbarFocused(true);
341
+ }}
342
+ onBlur={event => {
343
+ const nextTarget = event.relatedTarget;
344
+ const currentTarget = event.currentTarget as HTMLElement;
345
+ if (!(nextTarget instanceof Node) || !currentTarget.contains(nextTarget)) {
346
+ this.setCodeBlockToolbarFocused(false);
347
+ }
348
+ }}
349
+ >
350
+ {this._renderCodeBlockLanguageSelect()}
351
+ </div>
352
+ )}
353
+ </div>
354
+ );
355
+ }
356
+
357
+ private _renderTableToolbar() {
358
+ const position = this.tableToolbarPosition;
359
+ const state = this.toolbarState;
360
+ return (
361
+ <div
362
+ class="pointer-events-none absolute inset-0 z-10"
363
+ ref={ref => {
364
+ if (this.ctx.disposed) return;
365
+ this.setTableToolbarHost?.(ref as HTMLDivElement | null);
366
+ }}
367
+ >
368
+ {position && (
369
+ <div
370
+ class="pointer-events-auto absolute flex flex-wrap items-center gap-1 rounded-box border border-base-300 bg-base-100 p-1 shadow"
371
+ style={{
372
+ left: `${position.left}px`,
373
+ top: `${position.top}px`,
374
+ transform: 'translate(-50%, -100%)',
375
+ }}
376
+ role="toolbar"
377
+ aria-label={this.scope.locale.TableToolbar()}
378
+ onFocus={() => {
379
+ this.setTableToolbarFocused(true);
380
+ }}
381
+ onBlur={event => {
382
+ const nextTarget = event.relatedTarget;
383
+ const currentTarget = event.currentTarget as HTMLElement;
384
+ if (!(nextTarget instanceof Node) || !currentTarget.contains(nextTarget)) {
385
+ this.setTableToolbarFocused(false);
386
+ }
387
+ }}
388
+ >
389
+ {this._toolbarButton(
390
+ ':editor:add-box',
391
+ this.scope.locale.AddRowBefore(),
392
+ () => this.addTableRowBefore(),
393
+ { disabled: !state.canAddTableRowBefore },
394
+ )}
395
+ {this._toolbarButton(
396
+ ':editor:add-box-outline',
397
+ this.scope.locale.AddRowAfter(),
398
+ () => this.addTableRowAfter(),
399
+ { disabled: !state.canAddTableRowAfter },
400
+ )}
401
+ {this._toolbarButton(
402
+ ':editor:horizontal-rule',
403
+ this.scope.locale.DeleteRow(),
404
+ () => this.deleteTableRow(),
405
+ {
406
+ disabled: !state.canDeleteTableRow,
407
+ },
408
+ )}
409
+ {this._toolbarButton(
410
+ ':editor:add-box',
411
+ this.scope.locale.AddColumnBefore(),
412
+ () => this.addTableColumnBefore(),
413
+ { disabled: !state.canAddTableColumnBefore },
414
+ )}
415
+ {this._toolbarButton(
416
+ ':editor:add-box-outline',
417
+ this.scope.locale.AddColumnAfter(),
418
+ () => this.addTableColumnAfter(),
419
+ { disabled: !state.canAddTableColumnAfter },
420
+ )}
421
+ {this._toolbarButton(
422
+ '::delete',
423
+ this.scope.locale.DeleteColumn(),
424
+ () => this.deleteTableColumn(),
425
+ {
426
+ disabled: !state.canDeleteTableColumn,
427
+ },
428
+ )}
429
+ {this._toolbarButton(
430
+ '::delete-forever',
431
+ this.scope.locale.DeleteTable(),
432
+ () => this.deleteTable(),
433
+ {
434
+ disabled: !state.canDeleteTable,
435
+ },
436
+ )}
437
+ </div>
438
+ )}
439
+ </div>
440
+ );
441
+ }
442
+
8
443
  public render() {
9
444
  return (
10
445
  <ZFormField
@@ -15,7 +450,7 @@ export class RenderFormFieldMarkdown extends BeanRenderBase {
15
450
  <div class={props.class}>
16
451
  <div
17
452
  class={[
18
- 'rounded-box border border-base-300 bg-base-100',
453
+ 'relative rounded-box border border-base-300 bg-base-100',
19
454
  !$$formField.field.state.meta.isValid && 'border-error',
20
455
  ]}
21
456
  onClick={() => {
@@ -26,7 +461,31 @@ export class RenderFormFieldMarkdown extends BeanRenderBase {
26
461
  >
27
462
  <ClientOnly
28
463
  v-slots={{
29
- default: () => <EditorContent editor={this.editor} class={this.cMarkdown} />,
464
+ default: () => (
465
+ <>
466
+ {!this.readonly && (
467
+ <ZImageUploader
468
+ imageScene={this.imageScene}
469
+ multiple={false}
470
+ onUploaded={result => {
471
+ this.handleImageUploaded(result);
472
+ }}
473
+ onError={error => {
474
+ this.handleImageUploadError(error);
475
+ }}
476
+ slotDefault={state => this._renderToolbar(state)}
477
+ ></ZImageUploader>
478
+ )}
479
+ {!this.readonly && this.editor && this._renderTableToolbar()}
480
+ {!this.readonly && this.editor && this._renderCodeBlockToolbar()}
481
+ {this.imageUploadError && (
482
+ <p class="px-4 pt-3 text-sm text-error" role="alert" aria-live="polite">
483
+ {this.imageUploadError}
484
+ </p>
485
+ )}
486
+ <EditorContent editor={this.editor} class={this.cMarkdown} />
487
+ </>
488
+ ),
30
489
  placeholder: () => <div class="min-h-96 p-4" aria-hidden="true"></div>,
31
490
  }}
32
491
  ></ClientOnly>
@@ -0,0 +1,59 @@
1
+ export default {
2
+ BlockStyle: 'Block style',
3
+ Blockquote: 'Blockquote',
4
+ Bold: 'Bold',
5
+ BulletList: 'Bullet list',
6
+ CodeBlock: 'Code block',
7
+ CodeBlockLanguage: 'Code block language',
8
+ Heading1: 'Heading 1',
9
+ Heading2: 'Heading 2',
10
+ Heading3: 'Heading 3',
11
+ Heading4: 'Heading 4',
12
+ Heading5: 'Heading 5',
13
+ Heading6: 'Heading 6',
14
+ Highlight: 'Highlight',
15
+ HorizontalRule: 'Horizontal rule',
16
+ InlineCode: 'Inline code',
17
+ ImageUploadFailed: 'Image upload failed',
18
+ ImageUploadInvalidUrl: 'The uploaded image does not have a usable URL',
19
+ InsertImage: 'Upload image',
20
+ InsertTable: 'Insert table',
21
+ Italic: 'Italic',
22
+ Link: 'Link',
23
+ LinkUrl: 'Link URL',
24
+ MarkdownToolbar: 'Markdown toolbar',
25
+ OrderedList: 'Ordered list',
26
+ Paragraph: 'Paragraph',
27
+ PlainText: 'Plain text',
28
+ bash: 'Bash',
29
+ css: 'CSS',
30
+ cpp: 'C++',
31
+ csharp: 'C#',
32
+ go: 'Go',
33
+ html: 'HTML',
34
+ java: 'Java',
35
+ javascript: 'JavaScript',
36
+ json: 'JSON',
37
+ markdown: 'Markdown',
38
+ php: 'PHP',
39
+ python: 'Python',
40
+ rust: 'Rust',
41
+ shell: 'Shell',
42
+ sql: 'SQL',
43
+ typescript: 'TypeScript',
44
+ Redo: 'Redo',
45
+ Strikethrough: 'Strikethrough',
46
+ TaskList: 'Task list',
47
+ TableToolbar: 'Table toolbar',
48
+ TableSizePicker: 'Table size',
49
+ TableSizeStatus: 'Selected table size',
50
+ TableSizeCell: 'table',
51
+ AddRowBefore: 'Add row before',
52
+ AddRowAfter: 'Add row after',
53
+ DeleteRow: 'Delete row',
54
+ AddColumnBefore: 'Add column before',
55
+ AddColumnAfter: 'Add column after',
56
+ DeleteColumn: 'Delete column',
57
+ DeleteTable: 'Delete table',
58
+ Undo: 'Undo',
59
+ };
@@ -0,0 +1,59 @@
1
+ export default {
2
+ BlockStyle: '段落样式',
3
+ Blockquote: '引用',
4
+ Bold: '加粗',
5
+ BulletList: '无序列表',
6
+ CodeBlock: '代码块',
7
+ CodeBlockLanguage: '代码块语言',
8
+ Heading1: '一级标题',
9
+ Heading2: '二级标题',
10
+ Heading3: '三级标题',
11
+ Heading4: '四级标题',
12
+ Heading5: '五级标题',
13
+ Heading6: '六级标题',
14
+ Highlight: '高亮',
15
+ HorizontalRule: '分隔线',
16
+ InlineCode: '行内代码',
17
+ ImageUploadFailed: '图片上传失败',
18
+ ImageUploadInvalidUrl: '上传的图片没有可用的地址',
19
+ InsertImage: '上传图片',
20
+ InsertTable: '插入表格',
21
+ Italic: '斜体',
22
+ Link: '链接',
23
+ LinkUrl: '链接地址',
24
+ MarkdownToolbar: 'Markdown 工具栏',
25
+ OrderedList: '有序列表',
26
+ Paragraph: '段落',
27
+ PlainText: '纯文本',
28
+ bash: 'Bash',
29
+ css: 'CSS',
30
+ cpp: 'C++',
31
+ csharp: 'C#',
32
+ go: 'Go',
33
+ html: 'HTML',
34
+ java: 'Java',
35
+ javascript: 'JavaScript',
36
+ json: 'JSON',
37
+ markdown: 'Markdown',
38
+ php: 'PHP',
39
+ python: 'Python',
40
+ rust: 'Rust',
41
+ shell: 'Shell',
42
+ sql: 'SQL',
43
+ typescript: 'TypeScript',
44
+ Redo: '重做',
45
+ Strikethrough: '删除线',
46
+ TaskList: '任务列表',
47
+ TableToolbar: '表格工具栏',
48
+ TableSizePicker: '表格大小',
49
+ TableSizeStatus: '当前表格大小',
50
+ TableSizeCell: '表格',
51
+ AddRowBefore: '在上方插入行',
52
+ AddRowAfter: '在下方插入行',
53
+ DeleteRow: '删除行',
54
+ AddColumnBefore: '在左侧插入列',
55
+ AddColumnAfter: '在右侧插入列',
56
+ DeleteColumn: '删除列',
57
+ DeleteTable: '删除表格',
58
+ Undo: '撤销',
59
+ };
@@ -1 +1,2 @@
1
+ export * from './.metadata/locales.js';
1
2
  export * from './.metadata/index.js';
@@ -0,0 +1,42 @@
1
+ export const codeBlockLanguages = [
2
+ 'bash',
3
+ 'css',
4
+ 'cpp',
5
+ 'csharp',
6
+ 'go',
7
+ 'html',
8
+ 'java',
9
+ 'javascript',
10
+ 'json',
11
+ 'markdown',
12
+ 'php',
13
+ 'python',
14
+ 'rust',
15
+ 'shell',
16
+ 'sql',
17
+ 'typescript',
18
+ ] as const;
19
+
20
+ export type CodeBlockLanguage = (typeof codeBlockLanguages)[number];
21
+
22
+ const codeBlockLanguageAliases: Record<string, CodeBlockLanguage> = {
23
+ js: 'javascript',
24
+ sh: 'shell',
25
+ ts: 'typescript',
26
+ };
27
+
28
+ export function getCodeBlockLanguage(
29
+ language: string | null | undefined,
30
+ ): CodeBlockLanguage | undefined {
31
+ if (!language) return undefined;
32
+ if ((codeBlockLanguages as readonly string[]).includes(language)) {
33
+ return language as CodeBlockLanguage;
34
+ }
35
+ return codeBlockLanguageAliases[language];
36
+ }
37
+
38
+ export function isCodeBlockLanguage(
39
+ language: string | null | undefined,
40
+ ): language is CodeBlockLanguage {
41
+ return !!getCodeBlockLanguage(language);
42
+ }
@@ -95,9 +95,29 @@ export function richTextContentStyle(): NestedCSSProperties {
95
95
  '& pre code': {
96
96
  background: 'transparent',
97
97
  borderRadius: 0,
98
+ display: 'block',
98
99
  fontSize: '0.875rem',
99
100
  padding: 0,
100
101
  },
102
+ '& pre code .hljs-comment, & pre code .hljs-quote': {
103
+ color: 'color-mix(in srgb, var(--color-base-content) 55%, transparent)',
104
+ fontStyle: 'italic',
105
+ },
106
+ '& pre code .hljs-keyword, & pre code .hljs-selector-tag, & pre code .hljs-type': {
107
+ color: 'var(--color-secondary)',
108
+ },
109
+ '& pre code .hljs-string, & pre code .hljs-attr, & pre code .hljs-template-variable': {
110
+ color: 'var(--color-success)',
111
+ },
112
+ '& pre code .hljs-number, & pre code .hljs-literal, & pre code .hljs-built_in': {
113
+ color: 'var(--color-info)',
114
+ },
115
+ '& pre code .hljs-title, & pre code .hljs-function, & pre code .hljs-variable': {
116
+ color: 'var(--color-primary)',
117
+ },
118
+ '& pre code .hljs-meta, & pre code .hljs-symbol, & pre code .hljs-params': {
119
+ color: 'var(--color-warning)',
120
+ },
101
121
  '& mark': {
102
122
  background: 'color-mix(in srgb, var(--color-primary) 30%, transparent)',
103
123
  borderRadius: '0.125rem',