kviewer 0.2.0 → 0.2.2

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 (114) hide show
  1. package/README.md +47 -0
  2. package/dist/module.d.mts +8 -1
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +25 -3
  5. package/dist/runtime/annotation/engine/canvas-resolution.d.ts +7 -0
  6. package/dist/runtime/annotation/engine/canvas-resolution.js +21 -0
  7. package/dist/runtime/annotation/engine/editor/editor.d.ts +5 -0
  8. package/dist/runtime/annotation/engine/editor/editor.js +14 -0
  9. package/dist/runtime/annotation/engine/editor/selector.d.ts +13 -1
  10. package/dist/runtime/annotation/engine/editor/selector.js +60 -8
  11. package/dist/runtime/annotation/engine/painter.d.ts +31 -0
  12. package/dist/runtime/annotation/engine/painter.js +88 -2
  13. package/dist/runtime/annotation/engine/store.js +3 -0
  14. package/dist/runtime/annotation/engine/tools/arrow.js +0 -1
  15. package/dist/runtime/annotation/engine/tools/circle.js +0 -2
  16. package/dist/runtime/annotation/engine/tools/free-highlight.js +0 -2
  17. package/dist/runtime/annotation/engine/tools/freehand.js +0 -2
  18. package/dist/runtime/annotation/engine/tools/rectangle.js +0 -2
  19. package/dist/runtime/annotation/engine/types.d.ts +5 -0
  20. package/dist/runtime/annotation/font-style.d.ts +15 -0
  21. package/dist/runtime/annotation/font-style.js +19 -0
  22. package/dist/runtime/annotation/parsers/extractCheckboxStyles.d.ts +8 -0
  23. package/dist/runtime/annotation/parsers/extractCheckboxStyles.js +24 -1
  24. package/dist/runtime/annotation/pdf-export/annotation-flags.d.ts +8 -0
  25. package/dist/runtime/annotation/pdf-export/annotation-flags.js +5 -0
  26. package/dist/runtime/annotation/pdf-export/export-form-fields.js +3 -1
  27. package/dist/runtime/annotation/pdf-export/export.js +13 -1
  28. package/dist/runtime/annotation/pdf-export/parse_circle.js +3 -0
  29. package/dist/runtime/annotation/pdf-export/parse_freetext.js +31 -19
  30. package/dist/runtime/annotation/pdf-export/parse_highlight.js +3 -0
  31. package/dist/runtime/annotation/pdf-export/parse_ink.js +5 -1
  32. package/dist/runtime/annotation/pdf-export/parse_line.js +5 -1
  33. package/dist/runtime/annotation/pdf-export/parse_polyline.js +5 -1
  34. package/dist/runtime/annotation/pdf-export/parse_square.js +3 -0
  35. package/dist/runtime/annotation/pdf-export/parse_stamp.js +4 -2
  36. package/dist/runtime/annotation/pdf-export/parse_strikeout.js +3 -0
  37. package/dist/runtime/annotation/pdf-export/parse_text.js +3 -0
  38. package/dist/runtime/annotation/pdf-export/parse_underline.js +3 -0
  39. package/dist/runtime/annotation/pdf-export/unicode-font.d.ts +5 -0
  40. package/dist/runtime/annotation/pdf-export/unicode-font.js +31 -0
  41. package/dist/runtime/annotation/pdf-export/winansi.d.ts +17 -0
  42. package/dist/runtime/annotation/pdf-export/winansi.js +77 -0
  43. package/dist/runtime/annotation/pdf-import/decode_circle.js +0 -1
  44. package/dist/runtime/annotation/pdf-import/decode_ink.js +0 -1
  45. package/dist/runtime/annotation/pdf-import/decode_line.js +0 -1
  46. package/dist/runtime/annotation/pdf-import/decode_square.js +0 -1
  47. package/dist/runtime/annotation/pdf-import/extract_stamp_appearance.d.ts +12 -0
  48. package/dist/runtime/annotation/pdf-import/extract_stamp_appearance.js +17 -4
  49. package/dist/runtime/annotation/pdf-import/types.d.ts +2 -0
  50. package/dist/runtime/assets/fonts/LICENSE-LiberationSans +102 -0
  51. package/dist/runtime/assets/fonts/LiberationSans-Regular.ttf +0 -0
  52. package/dist/runtime/assets/kviewer.css +1 -1
  53. package/dist/runtime/components/AnnotationToolbar.vue +6 -4
  54. package/dist/runtime/components/PdfPage.d.vue.ts +16 -0
  55. package/dist/runtime/components/PdfPage.vue +170 -9
  56. package/dist/runtime/components/PdfPage.vue.d.ts +16 -0
  57. package/dist/runtime/components/PerfHud.d.vue.ts +3 -0
  58. package/dist/runtime/components/PerfHud.vue +97 -0
  59. package/dist/runtime/components/PerfHud.vue.d.ts +3 -0
  60. package/dist/runtime/components/ToolButton.vue +4 -1
  61. package/dist/runtime/components/Viewer.d.vue.ts +37 -2
  62. package/dist/runtime/components/Viewer.vue +208 -13
  63. package/dist/runtime/components/Viewer.vue.d.ts +37 -2
  64. package/dist/runtime/components/ViewerBar.d.vue.ts +21 -1
  65. package/dist/runtime/components/ViewerBar.vue +183 -101
  66. package/dist/runtime/components/ViewerBar.vue.d.ts +21 -1
  67. package/dist/runtime/components/ViewerTabs.d.vue.ts +10 -2
  68. package/dist/runtime/components/ViewerTabs.vue +13 -2
  69. package/dist/runtime/components/ViewerTabs.vue.d.ts +10 -2
  70. package/dist/runtime/components/form-fields/FormDropdown.vue +50 -19
  71. package/dist/runtime/components/form-fields/FormSignatureField.vue +4 -2
  72. package/dist/runtime/components/form-fields/FormTextField.vue +7 -18
  73. package/dist/runtime/components/modals/FreeTextModal.vue +6 -4
  74. package/dist/runtime/components/modals/SignatureDrawModal.vue +6 -4
  75. package/dist/runtime/components/panels/PlacedFieldSidebar.vue +67 -50
  76. package/dist/runtime/components/panels/SignaturePicker.vue +6 -4
  77. package/dist/runtime/components/panels/StampPicker.vue +3 -1
  78. package/dist/runtime/components/tools/AnnotationToolButton.d.vue.ts +10 -0
  79. package/dist/runtime/components/tools/AnnotationToolButton.vue +33 -0
  80. package/dist/runtime/components/tools/AnnotationToolButton.vue.d.ts +10 -0
  81. package/dist/runtime/components/tools/MarqueeTool.vue +3 -1
  82. package/dist/runtime/components/tools/PageSettings.vue +13 -11
  83. package/dist/runtime/components/tools/SearchTool.vue +12 -10
  84. package/dist/runtime/components/tools/ToolProperties.vue +7 -5
  85. package/dist/runtime/components/tools/ToolbarMenu.d.vue.ts +9 -0
  86. package/dist/runtime/components/tools/ToolbarMenu.vue +79 -0
  87. package/dist/runtime/components/tools/ToolbarMenu.vue.d.ts +9 -0
  88. package/dist/runtime/components/tools/ZoomControls.vue +17 -9
  89. package/dist/runtime/composables/canvas-limits.d.ts +22 -0
  90. package/dist/runtime/composables/canvas-limits.js +10 -0
  91. package/dist/runtime/composables/useFormFields.d.ts +6 -0
  92. package/dist/runtime/composables/useFormFields.js +28 -0
  93. package/dist/runtime/composables/useInertiaPanzoom.d.ts +10 -0
  94. package/dist/runtime/composables/useInertiaPanzoom.js +57 -10
  95. package/dist/runtime/composables/useKviewerI18n.d.ts +18 -0
  96. package/dist/runtime/composables/useKviewerI18n.js +29 -0
  97. package/dist/runtime/composables/usePageVirtualization.d.ts +11 -0
  98. package/dist/runtime/composables/usePageVirtualization.js +40 -0
  99. package/dist/runtime/composables/useScriptingManager.d.ts +8 -4
  100. package/dist/runtime/composables/useScriptingManager.js +2 -0
  101. package/dist/runtime/composables/useViewerState.d.ts +4 -0
  102. package/dist/runtime/composables/useViewerState.js +8 -0
  103. package/dist/runtime/embed/bridge-client.d.ts +9 -1
  104. package/dist/runtime/embed/bridge-client.js +13 -0
  105. package/dist/runtime/embed/bridge-host.d.ts +3 -0
  106. package/dist/runtime/embed/bridge-host.js +30 -8
  107. package/dist/runtime/embed/protocol.d.ts +21 -0
  108. package/dist/runtime/i18n/messages.d.ts +252 -0
  109. package/dist/runtime/i18n/messages.js +249 -0
  110. package/dist/runtime/public-types.d.ts +2 -0
  111. package/dist/runtime/toolbar-tools.d.ts +54 -0
  112. package/dist/runtime/toolbar-tools.js +79 -0
  113. package/dist/types.d.mts +1 -1
  114. package/package.json +14 -31
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <UModal
3
3
  v-model:open="isOpen"
4
- :title="props.initialText !== void 0 ? 'Edit Text' : 'Free Text'"
4
+ :title="props.initialText !== void 0 ? t('editText') : t('freeText')"
5
5
  :ui="{ width: 'sm:max-w-md' }"
6
6
  >
7
7
  <template #body>
@@ -9,7 +9,7 @@
9
9
  <UTextarea
10
10
  ref="textareaRef"
11
11
  v-model="inputValue"
12
- placeholder="Start typing..."
12
+ :placeholder="t('startTyping')"
13
13
  :rows="3"
14
14
  />
15
15
  <div class="flex items-center justify-between">
@@ -38,13 +38,13 @@
38
38
  <template #footer>
39
39
  <div class="flex justify-end gap-2 w-full">
40
40
  <UButton variant="ghost" color="neutral" @click="cancel">
41
- Cancel
41
+ {{ t("cancel") }}
42
42
  </UButton>
43
43
  <UButton
44
44
  :disabled="!inputValue.trim()"
45
45
  @click="submit"
46
46
  >
47
- OK
47
+ {{ t("ok") }}
48
48
  </UButton>
49
49
  </div>
50
50
  </template>
@@ -54,6 +54,8 @@
54
54
  <script setup>
55
55
  import { ref, watch, nextTick } from "vue";
56
56
  import { defaultOptions } from "../../annotation/engine/config";
57
+ import { useKviewerI18n } from "../../composables/useKviewerI18n";
58
+ const { t } = useKviewerI18n();
57
59
  const props = defineProps({
58
60
  open: { type: Boolean, required: true },
59
61
  defaultColor: { type: String, required: true },
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <UModal v-model:open="isOpen" title="Draw Signature" :ui="{ content: 'sm:max-w-2xl' }">
2
+ <UModal v-model:open="isOpen" :title="t('drawSignature')" :ui="{ content: 'sm:max-w-2xl' }">
3
3
  <template #body>
4
4
  <div class="flex flex-col gap-3">
5
5
  <div
@@ -30,7 +30,7 @@
30
30
  />
31
31
  </div>
32
32
  <UButton variant="ghost" color="neutral" size="xs" @click="clearCanvas">
33
- Clear
33
+ {{ t("clear") }}
34
34
  </UButton>
35
35
  </div>
36
36
  </div>
@@ -38,10 +38,10 @@
38
38
  <template #footer>
39
39
  <div class="flex justify-end gap-2">
40
40
  <UButton variant="ghost" color="neutral" @click="cancel">
41
- Cancel
41
+ {{ t("cancel") }}
42
42
  </UButton>
43
43
  <UButton :disabled="!hasDrawn" @click="submit">
44
- Save
44
+ {{ t("save") }}
45
45
  </UButton>
46
46
  </div>
47
47
  </template>
@@ -51,6 +51,8 @@
51
51
  <script setup>
52
52
  import { ref, watch, nextTick, onBeforeUnmount } from "vue";
53
53
  import { defaultOptions } from "../../annotation/engine/config";
54
+ import { useKviewerI18n } from "../../composables/useKviewerI18n";
55
+ const { t } = useKviewerI18n();
54
56
  const props = defineProps({
55
57
  open: { type: Boolean, required: true }
56
58
  });
@@ -20,7 +20,7 @@
20
20
  <div class="flex-1 min-h-0 overflow-y-auto">
21
21
  <div v-if="field" class="flex flex-col gap-4 p-4">
22
22
  <div class="flex flex-col gap-1">
23
- <label class="text-xs font-medium text-muted">Field name</label>
23
+ <label class="text-xs font-medium text-muted">{{ t("fieldName") }}</label>
24
24
  <UInput
25
25
  v-model="nameDraft"
26
26
  size="sm"
@@ -29,19 +29,19 @@
29
29
  @keydown.enter.prevent="commitName"
30
30
  />
31
31
  <p class="text-xs text-muted">
32
- Shared names group radio buttons and bind multiple widgets to the same value.
32
+ {{ t("fieldNameHint") }}
33
33
  </p>
34
34
  </div>
35
35
 
36
36
  <div class="flex flex-col gap-2">
37
37
  <UCheckbox
38
38
  :model-value="field.required"
39
- label="Required"
39
+ :label="t('required')"
40
40
  @update:model-value="onRequired"
41
41
  />
42
42
  <UCheckbox
43
43
  :model-value="field.readOnly"
44
- label="Read-only"
44
+ :label="t('readOnly')"
45
45
  @update:model-value="onReadOnly"
46
46
  />
47
47
  </div>
@@ -50,15 +50,15 @@
50
50
  <div class="flex flex-col gap-2">
51
51
  <UCheckbox
52
52
  :model-value="!!field.multiLine"
53
- label="Multi-line"
53
+ :label="t('multiLine')"
54
54
  @update:model-value="onMultiLine"
55
55
  />
56
56
  <div class="flex flex-col gap-1">
57
- <label class="text-xs font-medium text-muted">Max length</label>
57
+ <label class="text-xs font-medium text-muted">{{ t("maxLength") }}</label>
58
58
  <UInput
59
59
  type="number"
60
60
  :model-value="field.maxLen != null ? String(field.maxLen) : ''"
61
- placeholder="No limit"
61
+ :placeholder="t('noLimit')"
62
62
  size="sm"
63
63
  min="0"
64
64
  @update:model-value="onMaxLen"
@@ -69,7 +69,7 @@
69
69
 
70
70
  <template v-if="field.fieldType === 'checkbox'">
71
71
  <div class="flex flex-col gap-1">
72
- <label class="text-xs font-medium text-muted">Style</label>
72
+ <label class="text-xs font-medium text-muted">{{ t("style") }}</label>
73
73
  <USelect
74
74
  :model-value="field.checkboxStyle ?? 'check'"
75
75
  :items="checkboxStyleItems"
@@ -77,14 +77,14 @@
77
77
  @update:model-value="onCheckboxStyle"
78
78
  />
79
79
  <p class="text-xs text-muted">
80
- Glyph drawn when checked. Saved as <code>/MK /CA</code> on export.
80
+ {{ t("checkboxStyleHint") }}
81
81
  </p>
82
82
  </div>
83
83
  </template>
84
84
 
85
85
  <template v-if="field.fieldType === 'dropdown'">
86
86
  <div class="flex flex-col gap-1">
87
- <label class="text-xs font-medium text-muted">Options</label>
87
+ <label class="text-xs font-medium text-muted">{{ t("options") }}</label>
88
88
  <div class="flex flex-col gap-1">
89
89
  <div
90
90
  v-for="(opt, i) in field.options ?? []"
@@ -95,7 +95,7 @@
95
95
  :model-value="opt.displayValue"
96
96
  size="sm"
97
97
  spellcheck="false"
98
- placeholder="Label"
98
+ :placeholder="t('optionLabelPlaceholder')"
99
99
  class="flex-1"
100
100
  @update:model-value="(v) => updateOption(i, v)"
101
101
  />
@@ -111,7 +111,7 @@
111
111
  </div>
112
112
  <UButton
113
113
  icon="i-lucide-plus"
114
- label="Add option"
114
+ :label="t('addOption')"
115
115
  variant="soft"
116
116
  color="neutral"
117
117
  size="xs"
@@ -120,18 +120,18 @@
120
120
  />
121
121
  <UCheckbox
122
122
  :model-value="field.combo !== false"
123
- label="Combo box (collapsible)"
123
+ :label="t('comboBox')"
124
124
  class="mt-2"
125
125
  @update:model-value="onCombo"
126
126
  />
127
127
  <UCheckbox
128
128
  :model-value="!!field.editable"
129
- label="Allow custom text"
129
+ :label="t('allowCustomText')"
130
130
  @update:model-value="onEditable"
131
131
  />
132
132
  <UCheckbox
133
133
  :model-value="!!field.multiSelect"
134
- label="Multi-select"
134
+ :label="t('multiSelect')"
135
135
  @update:model-value="onMultiSelect"
136
136
  />
137
137
  </div>
@@ -139,23 +139,23 @@
139
139
 
140
140
  <template v-if="field.fieldType === 'signature'">
141
141
  <div class="flex flex-col gap-1">
142
- <label class="text-xs font-medium text-muted">Prompt text</label>
142
+ <label class="text-xs font-medium text-muted">{{ t("promptText") }}</label>
143
143
  <UInput
144
144
  v-model="promptDraft"
145
145
  size="sm"
146
146
  spellcheck="false"
147
- placeholder="Click to sign"
147
+ :placeholder="t('clickToSign')"
148
148
  @blur="commitPrompt"
149
149
  @keydown.enter.prevent="commitPrompt"
150
150
  />
151
151
  <p class="text-xs text-muted">
152
- Shown on the unsigned widget. Defaults to "Click to sign".
152
+ {{ t("promptTextHint", { default: t("clickToSign") }) }}
153
153
  </p>
154
154
  </div>
155
155
 
156
156
  <div class="flex flex-col gap-2">
157
157
  <div class="flex flex-col gap-1">
158
- <label class="text-xs font-medium text-muted">Lock fields after signing</label>
158
+ <label class="text-xs font-medium text-muted">{{ t("lockFieldsAfterSigning") }}</label>
159
159
  <USelect
160
160
  :model-value="field.lockAction ?? 'none'"
161
161
  :items="lockActionItems"
@@ -163,8 +163,7 @@
163
163
  @update:model-value="onLockAction"
164
164
  />
165
165
  <p class="text-xs text-muted">
166
- Once this signature is filled, listed fields become read-only
167
- inside the viewer. Not preserved on PDF export.
166
+ {{ t("lockFieldsHint") }}
168
167
  </p>
169
168
  </div>
170
169
 
@@ -173,13 +172,13 @@
173
172
  class="flex flex-col gap-1"
174
173
  >
175
174
  <label class="text-xs font-medium text-muted">
176
- {{ field.lockAction === "include" ? "Lock these fields" : "Lock all fields except" }}
175
+ {{ field.lockAction === "include" ? t("lockTheseFields") : t("lockAllFieldsExcept") }}
177
176
  </label>
178
177
  <div
179
178
  v-if="otherFieldNames.length === 0"
180
179
  class="text-xs text-muted italic"
181
180
  >
182
- No other named fields in the document.
181
+ {{ t("noOtherNamedFields") }}
183
182
  </div>
184
183
  <div v-else class="flex flex-col gap-1 max-h-48 overflow-y-auto">
185
184
  <UCheckbox
@@ -196,7 +195,7 @@
196
195
 
197
196
  <template v-if="field.fieldType === 'radio'">
198
197
  <div class="flex flex-col gap-1">
199
- <label class="text-xs font-medium text-muted">Group</label>
198
+ <label class="text-xs font-medium text-muted">{{ t("group") }}</label>
200
199
  <USelect
201
200
  :model-value="field.fieldName"
202
201
  :items="radioGroupItems"
@@ -204,12 +203,12 @@
204
203
  @update:model-value="onRadioGroup"
205
204
  />
206
205
  <p class="text-xs text-muted">
207
- Radios sharing a group act as one — picking one clears the others.
206
+ {{ t("radioGroupHint") }}
208
207
  </p>
209
208
  </div>
210
209
 
211
210
  <div class="flex flex-col gap-1">
212
- <label class="text-xs font-medium text-muted">Option value</label>
211
+ <label class="text-xs font-medium text-muted">{{ t("optionValue") }}</label>
213
212
  <UInput
214
213
  v-model="buttonValueDraft"
215
214
  size="sm"
@@ -218,14 +217,14 @@
218
217
  @keydown.enter.prevent="commitButtonValue"
219
218
  />
220
219
  <p class="text-xs text-muted">
221
- Value stored when this option is selected. Must be unique inside the group.
220
+ {{ t("optionValueHint") }}
222
221
  </p>
223
222
  </div>
224
223
  </template>
225
224
 
226
225
  <div class="flex flex-col gap-1 text-xs text-muted">
227
- <div>Type: <span class="font-medium">{{ field.fieldType }}</span></div>
228
- <div>Page: <span class="font-medium">{{ field.pageNumber }}</span></div>
226
+ <div>{{ t("typeLabel") }}: <span class="font-medium">{{ field.fieldType }}</span></div>
227
+ <div>{{ t("pageLabel") }}: <span class="font-medium">{{ field.pageNumber }}</span></div>
229
228
  </div>
230
229
 
231
230
  <UButton
@@ -235,11 +234,11 @@
235
234
  block
236
235
  @click="onDelete"
237
236
  >
238
- Delete field
237
+ {{ t("deleteField") }}
239
238
  </UButton>
240
239
  </div>
241
240
  <div v-else class="text-sm text-muted p-4">
242
- Select a placed field to edit its properties.
241
+ {{ t("selectFieldHint") }}
243
242
  </div>
244
243
  </div>
245
244
  </aside>
@@ -250,7 +249,22 @@ import { computed, onBeforeUnmount, ref, watch } from "vue";
250
249
  import { CHECKBOX_STYLE_OPTIONS } from "../../annotation/checkbox-styles";
251
250
  import { useFormFields } from "../../composables/useFormFields";
252
251
  import { useViewerState } from "../../composables/useViewerState";
253
- const checkboxStyleItems = CHECKBOX_STYLE_OPTIONS;
252
+ import { useKviewerI18n } from "../../composables/useKviewerI18n";
253
+ const { t } = useKviewerI18n();
254
+ const checkboxStyleLabelKeys = {
255
+ check: "styleCheck",
256
+ cross: "styleCross",
257
+ diamond: "styleDiamond",
258
+ circle: "styleCircle",
259
+ star: "styleStar",
260
+ square: "styleSquare"
261
+ };
262
+ const checkboxStyleItems = computed(
263
+ () => CHECKBOX_STYLE_OPTIONS.map(({ value }) => ({
264
+ label: t(checkboxStyleLabelKeys[value]),
265
+ value
266
+ }))
267
+ );
254
268
  const formFields = useFormFields();
255
269
  const state = useViewerState();
256
270
  const field = computed(() => {
@@ -259,29 +273,32 @@ const field = computed(() => {
259
273
  return formFields.getFieldById(id) ?? null;
260
274
  });
261
275
  const title = computed(() => {
262
- if (!field.value) return "Field properties";
276
+ if (!field.value) return t("fieldProperties");
263
277
  return field.value.fieldName;
264
278
  });
265
279
  const description = computed(() => {
266
280
  if (!field.value) return void 0;
267
- return `${labelForType(field.value.fieldType)} on page ${field.value.pageNumber}`;
281
+ return t("fieldOnPage", {
282
+ type: labelForType(field.value.fieldType),
283
+ page: field.value.pageNumber
284
+ });
268
285
  });
269
- function labelForType(t) {
270
- switch (t) {
286
+ function labelForType(fieldType) {
287
+ switch (fieldType) {
271
288
  case "text":
272
- return "Text field";
289
+ return t("fieldTypeText");
273
290
  case "checkbox":
274
- return "Checkbox";
291
+ return t("fieldTypeCheckbox");
275
292
  case "radio":
276
- return "Radio button";
293
+ return t("fieldTypeRadio");
277
294
  case "signature":
278
- return "Signature field";
295
+ return t("fieldTypeSignature");
279
296
  case "dropdown":
280
- return "Dropdown";
297
+ return t("fieldTypeDropdown");
281
298
  case "button":
282
- return "Button";
299
+ return t("fieldTypeButton");
283
300
  default:
284
- return "Field";
301
+ return t("fieldTypeGeneric");
285
302
  }
286
303
  }
287
304
  const nameDraft = ref("");
@@ -292,12 +309,12 @@ watch(field, (f) => {
292
309
  buttonValueDraft.value = f?.buttonValue ?? "";
293
310
  promptDraft.value = f?.promptText ?? "";
294
311
  }, { immediate: true });
295
- const lockActionItems = [
296
- { label: "None", value: "none" },
297
- { label: "All other fields", value: "all" },
298
- { label: "Selected fields only", value: "include" },
299
- { label: "All except selected", value: "exclude" }
300
- ];
312
+ const lockActionItems = computed(() => [
313
+ { label: t("lockNone"), value: "none" },
314
+ { label: t("lockAllOther"), value: "all" },
315
+ { label: t("lockSelectedOnly"), value: "include" },
316
+ { label: t("lockAllExceptSelected"), value: "exclude" }
317
+ ]);
301
318
  const NEW_GROUP_SENTINEL = "__new_radio_group__";
302
319
  const radioGroupItems = computed(() => {
303
320
  const names = /* @__PURE__ */ new Set();
@@ -309,7 +326,7 @@ const radioGroupItems = computed(() => {
309
326
  const sorted = Array.from(names).sort();
310
327
  return [
311
328
  ...sorted.map((n) => ({ label: n, value: n })),
312
- { label: "+ New group", value: NEW_GROUP_SENTINEL }
329
+ { label: t("newGroup"), value: NEW_GROUP_SENTINEL }
313
330
  ];
314
331
  });
315
332
  const otherFieldNames = computed(() => {
@@ -3,7 +3,7 @@
3
3
  <button
4
4
  class="relative flex items-center justify-center w-8 h-8 rounded-md transition-colors cursor-pointer"
5
5
  :class="isActive ? 'bg-default text-highlighted' : 'text-muted hover:bg-default/50 hover:text-highlighted'"
6
- title="Signature"
6
+ :title="t('signature')"
7
7
  >
8
8
  <UIcon name="i-lucide-pen-tool" class="text-lg" />
9
9
  </button>
@@ -20,7 +20,7 @@
20
20
  >
21
21
  <img
22
22
  :src="sig.imageUrl"
23
- :alt="sig.name || 'Signature'"
23
+ :alt="sig.name || t('signature')"
24
24
  class="h-8 max-w-36 object-contain bg-white rounded border border-default px-2"
25
25
  >
26
26
  <UButton
@@ -34,7 +34,7 @@
34
34
  </div>
35
35
  </div>
36
36
  <p v-else class="text-xs text-muted mb-2">
37
- No saved signatures
37
+ {{ t("noSavedSignatures") }}
38
38
  </p>
39
39
 
40
40
  <UButton
@@ -45,7 +45,7 @@
45
45
  block
46
46
  @click="drawModalOpen = true"
47
47
  >
48
- Add signature
48
+ {{ t("addSignature") }}
49
49
  </UButton>
50
50
  </div>
51
51
  </template>
@@ -63,9 +63,11 @@
63
63
  <script setup>
64
64
  import { ref, computed } from "vue";
65
65
  import { useViewerState } from "../../composables/useViewerState";
66
+ import { useKviewerI18n } from "../../composables/useKviewerI18n";
66
67
  import { AnnotationType } from "../../annotation/engine/types";
67
68
  import SignatureDrawModal from "../modals/SignatureDrawModal.vue";
68
69
  const state = useViewerState();
70
+ const { t } = useKviewerI18n();
69
71
  const drawModalOpen = ref(false);
70
72
  const isActive = computed(() => state.activeTool.value === AnnotationType.SIGNATURE);
71
73
  function selectSignature(sig) {
@@ -3,7 +3,7 @@
3
3
  <button
4
4
  class="relative flex items-center justify-center w-8 h-8 rounded-md transition-colors cursor-pointer"
5
5
  :class="isActive ? 'bg-default text-highlighted' : 'text-muted hover:bg-default/50 hover:text-highlighted'"
6
- title="Stamp"
6
+ :title="t('stamp')"
7
7
  >
8
8
  <UIcon name="i-lucide-stamp" class="text-lg" />
9
9
  </button>
@@ -33,8 +33,10 @@
33
33
  <script setup>
34
34
  import { ref, computed } from "vue";
35
35
  import { useViewerState } from "../../composables/useViewerState";
36
+ import { useKviewerI18n } from "../../composables/useKviewerI18n";
36
37
  import { AnnotationType } from "../../annotation/engine/types";
37
38
  const state = useViewerState();
39
+ const { t } = useKviewerI18n();
38
40
  const popoverOpen = ref(false);
39
41
  const isActive = computed(() => state.activeTool.value === AnnotationType.STAMP);
40
42
  function selectStamp(stamp) {
@@ -0,0 +1,10 @@
1
+ import type { AnnotationType } from '../../annotation/engine/types.js';
2
+ /** A single "select an annotation type" toolbar button. Parametrized by
3
+ * the tool it activates, so every draw/form/eraser button is one instance
4
+ * of this component instead of bespoke markup. */
5
+ type __VLS_Props = {
6
+ type: AnnotationType | 'eraser';
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <ToolButton
3
+ :tool="def"
4
+ :active="state.activeTool.value === props.type"
5
+ :indicator-color="indicatorColor"
6
+ @click="state.selectTool(props.type)"
7
+ />
8
+ </template>
9
+
10
+ <script setup>
11
+ import { computed, inject, ref } from "vue";
12
+ import { useViewerState } from "../../composables/useViewerState";
13
+ import { annotationDefinitions } from "../../annotation/engine/config";
14
+ import { STYLE_VERSION_KEY } from "../../toolbar-tools";
15
+ import ToolButton from "../ToolButton.vue";
16
+ const props = defineProps({
17
+ type: { type: [Number, String], required: true }
18
+ });
19
+ const state = useViewerState();
20
+ const styleVersion = inject(STYLE_VERSION_KEY, ref(0));
21
+ function getDef(type) {
22
+ return annotationDefinitions.find((d) => d.type === type);
23
+ }
24
+ const def = computed(() => {
25
+ if (props.type === "eraser") return { name: "Eraser", icon: "i-lucide-eraser" };
26
+ return getDef(props.type);
27
+ });
28
+ const indicatorColor = computed(() => {
29
+ void styleVersion.value;
30
+ if (props.type === "eraser") return void 0;
31
+ return getDef(props.type)?.style?.color;
32
+ });
33
+ </script>
@@ -0,0 +1,10 @@
1
+ import type { AnnotationType } from '../../annotation/engine/types.js';
2
+ /** A single "select an annotation type" toolbar button. Parametrized by
3
+ * the tool it activates, so every draw/form/eraser button is one instance
4
+ * of this component instead of bespoke markup. */
5
+ type __VLS_Props = {
6
+ type: AnnotationType | 'eraser';
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -4,12 +4,14 @@
4
4
  :variant="state.activeTool.value === 'marquee' ? 'soft' : 'ghost'"
5
5
  color="neutral"
6
6
  size="xs"
7
- title="Marquee Select"
7
+ :title="t('marqueeSelect')"
8
8
  @click="state.selectTool('marquee')"
9
9
  />
10
10
  </template>
11
11
 
12
12
  <script setup>
13
13
  import { useViewerState } from "../../composables/useViewerState";
14
+ import { useKviewerI18n } from "../../composables/useKviewerI18n";
14
15
  const state = useViewerState();
16
+ const { t } = useKviewerI18n();
15
17
  </script>
@@ -13,23 +13,25 @@
13
13
  import { computed } from "vue";
14
14
  import { usePageSettings } from "../../composables/usePageSettings";
15
15
  import { useViewerState } from "../../composables/useViewerState";
16
+ import { useKviewerI18n } from "../../composables/useKviewerI18n";
16
17
  const settings = usePageSettings();
17
18
  const state = useViewerState();
19
+ const { t } = useKviewerI18n();
18
20
  const menuItems = computed(() => [
19
21
  [
20
22
  {
21
- label: "Seiten\xFCbergang",
23
+ label: t("pageTransition"),
22
24
  type: "label"
23
25
  },
24
26
  {
25
- label: "Fortlaufend",
27
+ label: t("continuous"),
26
28
  icon: "i-lucide-rows-3",
27
29
  type: "checkbox",
28
30
  checked: settings.pageTransition.value === "continuous",
29
31
  onUpdateChecked: () => settings.setPageTransition("continuous")
30
32
  },
31
33
  {
32
- label: "Seite f\xFCr Seite",
34
+ label: t("pageByPage"),
33
35
  icon: "i-lucide-file",
34
36
  type: "checkbox",
35
37
  checked: settings.pageTransition.value === "single-page",
@@ -38,34 +40,34 @@ const menuItems = computed(() => [
38
40
  ],
39
41
  [
40
42
  {
41
- label: "Drehen",
43
+ label: t("rotate"),
42
44
  type: "label"
43
45
  },
44
46
  {
45
- label: "Im Uhrzeigersinn",
47
+ label: t("rotateClockwise"),
46
48
  icon: "i-lucide-rotate-cw",
47
49
  onSelect: () => settings.rotateCW()
48
50
  },
49
51
  {
50
- label: "Gegen den Uhrzeigersinn",
52
+ label: t("rotateCounterclockwise"),
51
53
  icon: "i-lucide-rotate-ccw",
52
54
  onSelect: () => settings.rotateCCW()
53
55
  }
54
56
  ],
55
57
  [
56
58
  {
57
- label: "Layout",
59
+ label: t("layout"),
58
60
  type: "label"
59
61
  },
60
62
  {
61
- label: "Einseitig",
63
+ label: t("singlePage"),
62
64
  icon: "i-lucide-square",
63
65
  type: "checkbox",
64
66
  checked: settings.layoutMode.value === "single",
65
67
  onUpdateChecked: () => settings.setLayoutMode("single")
66
68
  },
67
69
  {
68
- label: "Zweiseitig",
70
+ label: t("twoPages"),
69
71
  icon: "i-lucide-columns-2",
70
72
  type: "checkbox",
71
73
  checked: settings.layoutMode.value === "double",
@@ -74,7 +76,7 @@ const menuItems = computed(() => [
74
76
  ],
75
77
  [
76
78
  {
77
- label: "Stiftmodus",
79
+ label: t("stylusMode"),
78
80
  icon: "i-lucide-pen-tool",
79
81
  type: "checkbox",
80
82
  checked: state.stylusMode.value,
@@ -83,7 +85,7 @@ const menuItems = computed(() => [
83
85
  ],
84
86
  [
85
87
  {
86
- label: "Vollbildansicht",
88
+ label: t("fullscreen"),
87
89
  icon: "i-lucide-maximize",
88
90
  onSelect: () => settings.toggleFullscreen()
89
91
  }