@macrulez/mediatoolz 0.0.0-stage → 0.1.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.
Files changed (174) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +182 -2
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +243 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/image-batch/backup.d.ts +22 -0
  7. package/dist/commands/image-batch/backup.js +55 -0
  8. package/dist/commands/image-batch/backup.js.map +1 -0
  9. package/dist/commands/image-batch/cache.d.ts +15 -0
  10. package/dist/commands/image-batch/cache.js +38 -0
  11. package/dist/commands/image-batch/cache.js.map +1 -0
  12. package/dist/commands/image-batch/cli.d.ts +15 -0
  13. package/dist/commands/image-batch/cli.js +323 -0
  14. package/dist/commands/image-batch/cli.js.map +1 -0
  15. package/dist/commands/image-batch/codecs.d.ts +38 -0
  16. package/dist/commands/image-batch/codecs.js +366 -0
  17. package/dist/commands/image-batch/codecs.js.map +1 -0
  18. package/dist/commands/image-batch/config.d.ts +121 -0
  19. package/dist/commands/image-batch/config.js +690 -0
  20. package/dist/commands/image-batch/config.js.map +1 -0
  21. package/dist/commands/image-batch/errors.d.ts +5 -0
  22. package/dist/commands/image-batch/errors.js +8 -0
  23. package/dist/commands/image-batch/errors.js.map +1 -0
  24. package/dist/commands/image-batch/geometry.d.ts +26 -0
  25. package/dist/commands/image-batch/geometry.js +90 -0
  26. package/dist/commands/image-batch/geometry.js.map +1 -0
  27. package/dist/commands/image-batch/inputs.d.ts +22 -0
  28. package/dist/commands/image-batch/inputs.js +87 -0
  29. package/dist/commands/image-batch/inputs.js.map +1 -0
  30. package/dist/commands/image-batch/manage-cli.d.ts +2 -0
  31. package/dist/commands/image-batch/manage-cli.js +276 -0
  32. package/dist/commands/image-batch/manage-cli.js.map +1 -0
  33. package/dist/commands/image-batch/manage.d.ts +51 -0
  34. package/dist/commands/image-batch/manage.js +1041 -0
  35. package/dist/commands/image-batch/manage.js.map +1 -0
  36. package/dist/commands/image-batch/overwrite.d.ts +14 -0
  37. package/dist/commands/image-batch/overwrite.js +31 -0
  38. package/dist/commands/image-batch/overwrite.js.map +1 -0
  39. package/dist/commands/image-batch/plan.d.ts +86 -0
  40. package/dist/commands/image-batch/plan.js +396 -0
  41. package/dist/commands/image-batch/plan.js.map +1 -0
  42. package/dist/commands/image-batch/process.d.ts +8 -0
  43. package/dist/commands/image-batch/process.js +97 -0
  44. package/dist/commands/image-batch/process.js.map +1 -0
  45. package/dist/commands/image-batch/report.d.ts +6 -0
  46. package/dist/commands/image-batch/report.js +251 -0
  47. package/dist/commands/image-batch/report.js.map +1 -0
  48. package/dist/commands/image-batch/restore.d.ts +29 -0
  49. package/dist/commands/image-batch/restore.js +125 -0
  50. package/dist/commands/image-batch/restore.js.map +1 -0
  51. package/dist/commands/image-batch/run.d.ts +127 -0
  52. package/dist/commands/image-batch/run.js +656 -0
  53. package/dist/commands/image-batch/run.js.map +1 -0
  54. package/dist/commands/image-batch/sharpen-ask.d.ts +6 -0
  55. package/dist/commands/image-batch/sharpen-ask.js +53 -0
  56. package/dist/commands/image-batch/sharpen-ask.js.map +1 -0
  57. package/dist/commands/image-batch/sharpen-manage.d.ts +26 -0
  58. package/dist/commands/image-batch/sharpen-manage.js +238 -0
  59. package/dist/commands/image-batch/sharpen-manage.js.map +1 -0
  60. package/dist/commands/image-batch/sharpen-presets.d.ts +24 -0
  61. package/dist/commands/image-batch/sharpen-presets.js +145 -0
  62. package/dist/commands/image-batch/sharpen-presets.js.map +1 -0
  63. package/dist/commands/image-batch/sharpen.d.ts +46 -0
  64. package/dist/commands/image-batch/sharpen.js +159 -0
  65. package/dist/commands/image-batch/sharpen.js.map +1 -0
  66. package/dist/commands/image-batch/template.d.ts +45 -0
  67. package/dist/commands/image-batch/template.js +142 -0
  68. package/dist/commands/image-batch/template.js.map +1 -0
  69. package/dist/commands/image-hash/cache.d.ts +14 -0
  70. package/dist/commands/image-hash/cache.js +40 -0
  71. package/dist/commands/image-hash/cache.js.map +1 -0
  72. package/dist/commands/image-hash/core.d.ts +61 -0
  73. package/dist/commands/image-hash/core.js +302 -0
  74. package/dist/commands/image-hash/core.js.map +1 -0
  75. package/dist/commands/image-hash/hash.d.ts +21 -0
  76. package/dist/commands/image-hash/hash.js +104 -0
  77. package/dist/commands/image-hash/hash.js.map +1 -0
  78. package/dist/commands/image-hash/report.d.ts +3 -0
  79. package/dist/commands/image-hash/report.js +96 -0
  80. package/dist/commands/image-hash/report.js.map +1 -0
  81. package/dist/commands/image-hash/run.d.ts +61 -0
  82. package/dist/commands/image-hash/run.js +317 -0
  83. package/dist/commands/image-hash/run.js.map +1 -0
  84. package/dist/commands/image-hash/sharp-loader.d.ts +64 -0
  85. package/dist/commands/image-hash/sharp-loader.js +81 -0
  86. package/dist/commands/image-hash/sharp-loader.js.map +1 -0
  87. package/dist/commands/ui/cli.d.ts +2 -0
  88. package/dist/commands/ui/cli.js +78 -0
  89. package/dist/commands/ui/cli.js.map +1 -0
  90. package/dist/commands/ui/fs-routes.d.ts +7 -0
  91. package/dist/commands/ui/fs-routes.js +135 -0
  92. package/dist/commands/ui/fs-routes.js.map +1 -0
  93. package/dist/commands/ui/http.d.ts +41 -0
  94. package/dist/commands/ui/http.js +81 -0
  95. package/dist/commands/ui/http.js.map +1 -0
  96. package/dist/commands/ui/jobs.d.ts +15 -0
  97. package/dist/commands/ui/jobs.js +73 -0
  98. package/dist/commands/ui/jobs.js.map +1 -0
  99. package/dist/commands/ui/modules/image-batch-backups.d.ts +2 -0
  100. package/dist/commands/ui/modules/image-batch-backups.js +116 -0
  101. package/dist/commands/ui/modules/image-batch-backups.js.map +1 -0
  102. package/dist/commands/ui/modules/image-batch-manage.d.ts +2 -0
  103. package/dist/commands/ui/modules/image-batch-manage.js +245 -0
  104. package/dist/commands/ui/modules/image-batch-manage.js.map +1 -0
  105. package/dist/commands/ui/modules/image-batch.d.ts +2 -0
  106. package/dist/commands/ui/modules/image-batch.js +225 -0
  107. package/dist/commands/ui/modules/image-batch.js.map +1 -0
  108. package/dist/commands/ui/modules/image-hash.d.ts +2 -0
  109. package/dist/commands/ui/modules/image-hash.js +158 -0
  110. package/dist/commands/ui/modules/image-hash.js.map +1 -0
  111. package/dist/commands/ui/modules/index.d.ts +2 -0
  112. package/dist/commands/ui/modules/index.js +4 -0
  113. package/dist/commands/ui/modules/index.js.map +1 -0
  114. package/dist/commands/ui/open-browser.d.ts +1 -0
  115. package/dist/commands/ui/open-browser.js +18 -0
  116. package/dist/commands/ui/open-browser.js.map +1 -0
  117. package/dist/commands/ui/scan.d.ts +20 -0
  118. package/dist/commands/ui/scan.js +57 -0
  119. package/dist/commands/ui/scan.js.map +1 -0
  120. package/dist/commands/ui/server.d.ts +18 -0
  121. package/dist/commands/ui/server.js +196 -0
  122. package/dist/commands/ui/server.js.map +1 -0
  123. package/dist/format/bytes.d.ts +2 -0
  124. package/dist/format/bytes.js +17 -0
  125. package/dist/format/bytes.js.map +1 -0
  126. package/dist/format/colors.d.ts +12 -0
  127. package/dist/format/colors.js +15 -0
  128. package/dist/format/colors.js.map +1 -0
  129. package/dist/format/diff-render.d.ts +2 -0
  130. package/dist/format/diff-render.js +35 -0
  131. package/dist/format/diff-render.js.map +1 -0
  132. package/dist/format/file-list.d.ts +6 -0
  133. package/dist/format/file-list.js +15 -0
  134. package/dist/format/file-list.js.map +1 -0
  135. package/dist/format/findings-list.d.ts +7 -0
  136. package/dist/format/findings-list.js +14 -0
  137. package/dist/format/findings-list.js.map +1 -0
  138. package/dist/format/style.d.ts +20 -0
  139. package/dist/format/style.js +36 -0
  140. package/dist/format/style.js.map +1 -0
  141. package/dist/format/table.d.ts +8 -0
  142. package/dist/format/table.js +44 -0
  143. package/dist/format/table.js.map +1 -0
  144. package/dist/format/vibes.d.ts +13 -0
  145. package/dist/format/vibes.js +82 -0
  146. package/dist/format/vibes.js.map +1 -0
  147. package/dist/index.d.ts +9 -0
  148. package/dist/index.js +6 -0
  149. package/dist/index.js.map +1 -0
  150. package/dist/ui/assets/ImageBatchPage-D3f4z6YA.js +4 -0
  151. package/dist/ui/assets/ImageBatchPage-DwaunraE.css +1 -0
  152. package/dist/ui/assets/ImageHashPage-D0xZlLGw.css +1 -0
  153. package/dist/ui/assets/ImageHashPage-DjIlDPu-.js +1 -0
  154. package/dist/ui/assets/SourcePicker-D8aZjDwP.css +1 -0
  155. package/dist/ui/assets/SourcePicker-Db0dG6zc.js +1 -0
  156. package/dist/ui/assets/index-D1s-23_t.css +1 -0
  157. package/dist/ui/assets/index-yiehSw5D.js +3 -0
  158. package/dist/ui/index.html +17 -0
  159. package/dist/utils/closest.d.ts +2 -0
  160. package/dist/utils/closest.js +35 -0
  161. package/dist/utils/closest.js.map +1 -0
  162. package/dist/utils/glob.d.ts +4 -0
  163. package/dist/utils/glob.js +86 -0
  164. package/dist/utils/glob.js.map +1 -0
  165. package/dist/utils/split-list.d.ts +1 -0
  166. package/dist/utils/split-list.js +4 -0
  167. package/dist/utils/split-list.js.map +1 -0
  168. package/dist/utils/tty.d.ts +60 -0
  169. package/dist/utils/tty.js +390 -0
  170. package/dist/utils/tty.js.map +1 -0
  171. package/dist/utils/walk.d.ts +21 -0
  172. package/dist/utils/walk.js +116 -0
  173. package/dist/utils/walk.js.map +1 -0
  174. package/package.json +82 -4
@@ -0,0 +1,15 @@
1
+ import { createStyle } from './style.js';
2
+ export function formatFileCountRows(entries, unit, options = {}) {
3
+ if (entries.length === 0)
4
+ return [];
5
+ const style = createStyle(options);
6
+ const fileWidth = Math.max(...entries.map((e) => e.file.length));
7
+ const countTexts = entries.map((e) => `${e.count} ${unit(e.count)}`);
8
+ const countWidth = Math.max(...countTexts.map((t) => t.length));
9
+ return entries.map((entry, i) => {
10
+ const filePart = style.path(entry.file.padEnd(fileWidth));
11
+ const countPart = style.value(countTexts[i].padStart(countWidth));
12
+ return ` ${filePart} ${countPart}`;
13
+ });
14
+ }
15
+ //# sourceMappingURL=file-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-list.js","sourceRoot":"","sources":["../../src/format/file-list.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAOxC,MAAM,UAAU,mBAAmB,CACjC,OAAyB,EACzB,IAA+B,EAC/B,UAAwB,EAAE;IAE1B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAEnC,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;IAClC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAChE,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IACpE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;IAE/D,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;QACzD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAE,UAAU,CAAC,CAAC,CAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAA;QAC7E,OAAO,KAAK,QAAQ,KAAK,SAAS,EAAE,CAAA;IACtC,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { VibesOptions } from './vibes.js';
2
+ export interface FindingRow {
3
+ location: string;
4
+ name: string;
5
+ tag: string;
6
+ }
7
+ export declare function formatFindingsRows(entries: FindingRow[], options?: VibesOptions): string[];
@@ -0,0 +1,14 @@
1
+ import { createStyle } from './style.js';
2
+ export function formatFindingsRows(entries, options = {}) {
3
+ if (entries.length === 0)
4
+ return [];
5
+ const style = createStyle(options);
6
+ const locationWidth = Math.max(...entries.map((e) => e.location.length));
7
+ const nameWidth = Math.max(...entries.map((e) => e.name.length));
8
+ return entries.map((entry) => {
9
+ const locationPart = style.location(entry.location.padEnd(locationWidth));
10
+ const namePart = style.name(entry.name.padEnd(nameWidth));
11
+ return ` ${locationPart} ${namePart} ${style.tag(entry.tag)}`.trimEnd();
12
+ });
13
+ }
14
+ //# sourceMappingURL=findings-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings-list.js","sourceRoot":"","sources":["../../src/format/findings-list.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAQxC,MAAM,UAAU,kBAAkB,CAAC,OAAqB,EAAE,UAAwB,EAAE;IAClF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAEnC,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;IAClC,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAA;IACxE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAEhE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC3B,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAA;QACzE,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;QACzD,OAAO,KAAK,YAAY,KAAK,QAAQ,KAAK,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAA;IAC5E,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type VibesOptions } from './vibes.js';
2
+ export interface Style {
3
+ enabled: boolean;
4
+ info(text: string): string;
5
+ problem(text: string): string;
6
+ heading(text: string): string;
7
+ success(text: string): string;
8
+ warn(text: string): string;
9
+ error(text: string): string;
10
+ hint(text: string): string;
11
+ path(text: string): string;
12
+ location(text: string): string;
13
+ name(text: string): string;
14
+ tag(text: string): string;
15
+ muted(text: string): string;
16
+ accent(text: string): string;
17
+ value(text: string): string;
18
+ bold(text: string): string;
19
+ }
20
+ export declare function createStyle(options?: VibesOptions): Style;
@@ -0,0 +1,36 @@
1
+ import { BOLD, CYAN, DIM, GRAY, GREEN, MAGENTA, RED, WHITE, YELLOW, colorize } from './colors.js';
2
+ import { isColorEnabled } from './vibes.js';
3
+ const FLAG_PATTERN = /(?<![\w-])(--?[a-zA-Z][\w-]*)/g;
4
+ const LOCATION_PATTERN = /^(.*?)((?::\d+)+)(\s*)$/;
5
+ export function createStyle(options = {}) {
6
+ const enabled = isColorEnabled(options);
7
+ const wrap = (color, text) => (enabled && text ? colorize(color, text) : text);
8
+ return {
9
+ enabled,
10
+ info: (text) => wrap(GRAY, text),
11
+ problem: (text) => wrap(BOLD + RED, text),
12
+ heading: (text) => wrap(BOLD + YELLOW, text),
13
+ success: (text) => wrap(BOLD + GREEN, text),
14
+ warn: (text) => wrap(YELLOW, text),
15
+ error: (text) => wrap(RED, text),
16
+ hint: (text) => enabled
17
+ ? colorize(DIM, text).replace(FLAG_PATTERN, (flag) => `\x1b[0m${CYAN}${flag}\x1b[0m${DIM}`)
18
+ : text,
19
+ path: (text) => wrap(CYAN, text),
20
+ location: (text) => {
21
+ if (!enabled)
22
+ return text;
23
+ const match = LOCATION_PATTERN.exec(text);
24
+ if (!match)
25
+ return colorize(CYAN, text);
26
+ return `${colorize(CYAN, match[1])}${colorize(GRAY, match[2])}${match[3]}`;
27
+ },
28
+ name: (text) => wrap(YELLOW, text),
29
+ tag: (text) => wrap(GRAY, text),
30
+ muted: (text) => wrap(DIM, text),
31
+ accent: (text) => wrap(MAGENTA, text),
32
+ value: (text) => wrap(WHITE, text),
33
+ bold: (text) => wrap(BOLD, text),
34
+ };
35
+ }
36
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/format/style.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { VibesOptions } from './vibes.js';
2
+ export interface TableColumn {
3
+ header: string;
4
+ align?: 'left' | 'right';
5
+ shrink?: boolean;
6
+ style?: (text: string) => string;
7
+ }
8
+ export declare function renderTable(columns: TableColumn[], rows: string[][], options?: VibesOptions, availableWidth?: number | null): string[];
@@ -0,0 +1,44 @@
1
+ import { createStyle } from './style.js';
2
+ const MIN_SHRUNK_WIDTH = 12;
3
+ function shrinkLeft(text, width) {
4
+ if (text.length <= width)
5
+ return text;
6
+ return `…${text.slice(text.length - (width - 1))}`;
7
+ }
8
+ function fitWidths(widths, columns, available) {
9
+ if (available === null)
10
+ return widths;
11
+ const chrome = widths.length * 3 + 1;
12
+ const total = widths.reduce((sum, w) => sum + w, 0) + chrome;
13
+ const shrinkIndex = columns.findIndex((c) => c.shrink);
14
+ if (total <= available || shrinkIndex === -1)
15
+ return widths;
16
+ const fitted = [...widths];
17
+ const target = widths[shrinkIndex] - (total - available);
18
+ fitted[shrinkIndex] = Math.max(MIN_SHRUNK_WIDTH, target);
19
+ return fitted;
20
+ }
21
+ export function renderTable(columns, rows, options = {}, availableWidth = process.stdout.isTTY ? (process.stdout.columns ?? null) : null) {
22
+ const s = createStyle(options);
23
+ const natural = columns.map((column, i) => Math.max(column.header.length, ...rows.map((row) => (row[i] ?? '').length)));
24
+ const widths = fitWidths(natural, columns, availableWidth);
25
+ const cell = (column, index, raw, header) => {
26
+ const width = widths[index];
27
+ const text = column.shrink ? shrinkLeft(raw, width) : raw;
28
+ const padded = column.align === 'right' ? text.padStart(width) : text.padEnd(width);
29
+ if (header)
30
+ return s.bold(padded);
31
+ return column.style ? column.style(padded) : padded;
32
+ };
33
+ const rule = (left, mid, right) => s.muted(`${left}${widths.map((w) => '─'.repeat(w + 2)).join(mid)}${right}`);
34
+ const bar = s.muted('│');
35
+ const line = (texts, header) => `${bar} ${columns.map((column, i) => cell(column, i, texts[i] ?? '', header)).join(` ${bar} `)} ${bar}`;
36
+ return [
37
+ rule('┌', '┬', '┐'),
38
+ line(columns.map((c) => c.header), true),
39
+ rule('├', '┼', '┤'),
40
+ ...rows.map((row) => line(row, false)),
41
+ rule('└', '┴', '┘'),
42
+ ];
43
+ }
44
+ //# sourceMappingURL=table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.js","sourceRoot":"","sources":["../../src/format/table.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export interface VibesOptions {
2
+ /** --quiet: suppress everything but the bare essentials */
3
+ quiet?: boolean;
4
+ /** --plain: force plain output even in a real TTY (explicit opt-out) */
5
+ plain?: boolean;
6
+ /** --color: force colored output even when piped or in CI */
7
+ color?: boolean;
8
+ }
9
+ export declare function isColorEnabled(options?: VibesOptions): boolean;
10
+ export declare function isFancyOutputEnabled(options?: VibesOptions): boolean;
11
+ export declare function banner(options?: VibesOptions): string;
12
+ export declare function cleanCelebration(options?: VibesOptions): string;
13
+ export declare function firstRunAside(options?: VibesOptions): string | null;
@@ -0,0 +1,82 @@
1
+ import { BOLD, CYAN, GREEN, colorize } from './colors.js';
2
+ export function isColorEnabled(options = {}) {
3
+ if (options.plain)
4
+ return false;
5
+ if (options.color)
6
+ return true;
7
+ if (process.env.NO_COLOR)
8
+ return false;
9
+ const force = process.env.FORCE_COLOR;
10
+ if (force !== undefined && force !== '')
11
+ return force !== '0' && force !== 'false';
12
+ if (process.env.CI)
13
+ return false;
14
+ if (process.env.TERM === 'dumb')
15
+ return false;
16
+ return Boolean(process.stdout.isTTY);
17
+ }
18
+ // Fancy output (color, banner, celebration copy) only makes sense for a
19
+ // human watching a real terminal in real time — auto-off the moment any of
20
+ // that isn't true, no flag required. `--json`/`--quiet`/`--plain` are
21
+ // additional explicit opt-outs on top of this auto-detection.
22
+ export function isFancyOutputEnabled(options = {}) {
23
+ if (options.quiet || options.plain)
24
+ return false;
25
+ if (process.env.NO_COLOR)
26
+ return false;
27
+ if (process.env.CI)
28
+ return false;
29
+ if (!process.stdout.isTTY)
30
+ return false;
31
+ return true;
32
+ }
33
+ function pick(pool) {
34
+ const item = pool[Math.floor(Math.random() * pool.length)];
35
+ // pool is always non-empty at every call site below — see the arrays
36
+ // themselves, not worth threading `| undefined` through every caller.
37
+ return item;
38
+ }
39
+ const BANNERS = [
40
+ '🧰 mediatoolz',
41
+ '🧰 mediatoolz — chores, automated',
42
+ '🧰 mediatoolz, reporting for duty',
43
+ ];
44
+ // One compact line, shown once per invocation — never a multi-line ASCII
45
+ // block. That's reserved for the all-clean celebration below, where it's
46
+ // actually earned.
47
+ export function banner(options = {}) {
48
+ const text = pick(BANNERS);
49
+ return isColorEnabled(options) ? colorize(BOLD + CYAN, text) : text;
50
+ }
51
+ const CLEAN_LINES = [
52
+ 'Nothing to see here. Suspiciously clean, even.',
53
+ 'Spotless. Go treat yourself.',
54
+ '0 findings. The codebase gods are pleased.',
55
+ 'Clean sweep. No notes.',
56
+ "All clear — didn't even break a sweat.",
57
+ 'Nothing found. Somebody already did their homework.',
58
+ ];
59
+ const CLEAN_ART = [
60
+ [' ✨', ' ( ノ^ヮ^)ノ', ' good stuff'].join('\n'),
61
+ [' ┏(^0^)┛', ' nothing to fix here'].join('\n'),
62
+ [' ( •_•)>⌐■-■', ' (⌐■_■)', ' deal with it'].join('\n'),
63
+ ];
64
+ // The one moment ASCII art is allowed at all — a command found ZERO
65
+ // issues. Small, a few lines max, never on a run that actually found
66
+ // something (see features.md: don't joke over a real problem report).
67
+ export function cleanCelebration(options = {}) {
68
+ const line = pick(CLEAN_LINES);
69
+ const color = isColorEnabled(options);
70
+ const styledLine = color ? colorize(BOLD + GREEN, line) : line;
71
+ if (!isFancyOutputEnabled(options))
72
+ return styledLine;
73
+ const art = pick(CLEAN_ART);
74
+ return `${styledLine}\n${color ? colorize(GREEN, art) : art}`;
75
+ }
76
+ const FIRST_RUN_LINES = ["First time here? Let's see what we've got.", 'Taking a look...'];
77
+ export function firstRunAside(options = {}) {
78
+ if (!isFancyOutputEnabled(options))
79
+ return null;
80
+ return pick(FIRST_RUN_LINES);
81
+ }
82
+ //# sourceMappingURL=vibes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vibes.js","sourceRoot":"","sources":["../../src/format/vibes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export { runImageHash } from './commands/image-hash/run.js';
2
+ export type { ImageHashRunOptions, ImageHashReport, ImageHashError, ImageHashProgress, OutdatedOutput, } from './commands/image-hash/run.js';
3
+ export { renderImageHashReport } from './commands/image-hash/report.js';
4
+ export type { Components, HashEntry, HashType, OutputFormat } from './commands/image-hash/core.js';
5
+ export { runImageBatch } from './commands/image-batch/run.js';
6
+ export type { BatchJobResult, BatchOverrides, ImageBatchOptions, ImageBatchReport, } from './commands/image-batch/run.js';
7
+ export { renderImageBatchReport } from './commands/image-batch/report.js';
8
+ export { parseConfig } from './commands/image-batch/config.js';
9
+ export type { BatchConfig } from './commands/image-batch/config.js';
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { runImageHash } from './commands/image-hash/run.js';
2
+ export { renderImageHashReport } from './commands/image-hash/report.js';
3
+ export { runImageBatch } from './commands/image-batch/run.js';
4
+ export { renderImageBatchReport } from './commands/image-batch/report.js';
5
+ export { parseConfig } from './commands/image-batch/config.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAQ3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAA;AAGvE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAO7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAA;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA"}
@@ -0,0 +1,4 @@
1
+ import{d as oe,D as De,b as t,e as s,F as V,g as L,x as ne,t as a,E as Me,_ as ie,o as pe,f as e,k as d,l as E,u as Q,G as ce,H as et,y as R,z as se,j as q,m as Z,I as fe,i as be,p as ae,r as c,c as T,v as Y,J as tt,K as he,w as ye,a as Ve,A as ve,C as Le,n as Se,B as st,L as Ae}from"./index-yiehSw5D.js";import{F as xe,S as lt}from"./SourcePicker-Db0dG6zc.js";const at=["aria-label"],nt=["aria-checked","title","onClick"],ot=oe({__name:"UiSegmented",props:Me({options:{},label:{}},{modelValue:{required:!0},modelModifiers:{}}),emits:["update:modelValue"],setup(n){const b=De(n,"modelValue");return(i,f)=>(t(),s("div",{class:"seg",role:"radiogroup","aria-label":n.label},[(t(!0),s(V,null,L(n.options,v=>(t(),s("button",{key:v.value,class:ne(["seg__btn",{"seg__btn--on":b.value===v.value}]),role:"radio","aria-checked":b.value===v.value,title:v.hint,onClick:N=>b.value=v.value},a(v.label),11,nt))),128))],8,at))}}),ge=ie(ot,[["__scopeId","data-v-56d7f4ee"]]),it={class:"view"},ut={key:0,class:"card block"},rt={key:0,class:"notice"},dt={key:1,class:"notice notice--error"},ct={key:2,class:"muted"},vt={class:"list"},pt={class:"item__main"},mt={class:"muted"},ft=["title"],gt={class:"item__actions"},ht=["onClick"],bt=["onClick"],yt={class:"custom"},kt=["disabled"],$t={key:1,class:"card block"},_t={class:"head"},wt={class:"muted"},Ct={class:"item__path"},St={key:0,class:"notice"},Vt={key:1,class:"notice notice--error"},xt={class:"chips"},Et={key:2,class:"notice notice--warn"},zt={class:"actions"},At=["disabled"],Nt={key:0,class:"check"},Bt={class:"rows"},Ot=["src"],jt=["src"],Tt={class:"row__text"},Ut=["title"],It={class:"muted small"},Dt={key:0,class:"small row__message"},Mt={key:3,class:"actions"},Lt={key:4,class:"muted small"},Ne="mediatoolz-ui.open-backup",Ce=30,Rt=oe({__name:"BackupsView",props:{cwd:{}},setup(n){const b={restored:"restored","would-restore":"can be restored",modified:"changed since","missing-backup":"copy missing","damaged-backup":"copy damaged",error:"error"},i=c(""),f=c([]),v=c(),N=c(""),y=c(""),z=c(!1),U=c(!1),I=c(Ce),D=c(""),B=c(!1),S=T(()=>{const x=v.value?.counts;return x?x.modified+x["missing-backup"]+x["damaged-backup"]+x.error:0}),h=T(()=>v.value?.counts["would-restore"]??0),m=T(()=>v.value?.entries.slice(0,I.value)??[]);function r(x){const C=new Date(x);return Number.isNaN(C.getTime())?x:C.toLocaleString()}function _(x){return x==="would-restore"||x==="restored"?"good":x==="modified"?"warn":"bad"}function $(x){N.value=x instanceof Error?x.message:String(x)}async function F(){try{const x=await Y.get("/api/image-batch/backups");i.value=x.root,f.value=x.backups}catch(x){$(x)}}async function J(x){N.value="",y.value="",U.value=!1,I.value=Ce;try{v.value=await Y.get(`/api/image-batch/backup?dir=${encodeURIComponent(x)}`)}catch(C){v.value=void 0,$(C)}}function K(x){const C=x?.[0]??D.value;B.value=!1,C.trim()!==""&&(D.value=C,J(C))}async function M(){const x=v.value;if(!x)return;const C=U.value?x.files:h.value;if(window.confirm(`Put back ${C} original file${C===1?"":"s"} from this backup?`+(U.value?" Files changed after the replacement will be overwritten too.":""))){z.value=!0;try{const P=await Y.post("/api/image-batch/backup/restore",{dir:x.dir,force:U.value});y.value=`Restored ${P.counts.restored} file${P.counts.restored===1?"":"s"}.`,await J(x.dir)}catch(P){$(P)}finally{z.value=!1}}}async function te(x){if(window.confirm("Delete this backup for good? The saved originals cannot be recovered afterwards."))try{await Y.post("/api/image-batch/backup/delete",{dir:x}),y.value="The backup was deleted.",v.value?.dir===x&&(v.value=void 0),await F()}catch(C){$(C)}}return pe(async()=>{await F();try{const x=localStorage.getItem(Ne);x&&(localStorage.removeItem(Ne),await J(x))}catch{v.value=void 0}}),(x,C)=>(t(),s("div",it,[v.value?(t(),s("section",$t,[e("div",_t,[e("div",null,[e("h2",null,"Backup of "+a(r(v.value.createdAt)),1),e("p",wt,a(v.value.files)+" file"+a(v.value.files===1?"":"s")+" · "+a(Q(ce)(v.value.bytesBefore))+" originals, "+a(Q(ce)(v.value.bytesAfter))+" after the replacement ",1),e("code",Ct,a(v.value.dir),1)]),e("button",{class:"btn btn--ghost",onClick:C[3]||(C[3]=A=>(v.value=void 0,F()))},[q(Z,{name:"close",size:16}),C[15]||(C[15]=E(" Back to the list ",-1))])]),y.value?(t(),s("p",St,a(y.value),1)):d("",!0),N.value?(t(),s("p",Vt,a(N.value),1)):d("",!0),e("div",xt,[(t(!0),s(V,null,L(v.value.counts,(A,P)=>R((t(),s("span",{key:P,class:ne(["chip",`chip--${_(P)}`])},a(A)+" "+a(b[P]),3)),[[tt,A>0]])),128))]),v.value.counts.modified?(t(),s("p",Et,a(v.value.counts.modified)+" file"+a(v.value.counts.modified===1?" was":"s were")+" changed after the replacement. They are left alone unless you tick the box below. ",1)):d("",!0),e("div",zt,[e("button",{class:"btn btn--primary",disabled:z.value||h.value===0&&!U.value,onClick:M}," Restore "+a(U.value?"everything":`${h.value} file${h.value===1?"":"s"}`),9,At),v.value.counts.modified?(t(),s("label",Nt,[R(e("input",{"onUpdate:modelValue":C[4]||(C[4]=A=>U.value=A),type:"checkbox"},null,512),[[fe,U.value]]),C[16]||(C[16]=E(" Overwrite files that changed since ",-1))])):d("",!0),e("button",{class:"btn btn--danger",onClick:C[5]||(C[5]=A=>te(v.value.dir))},"Delete this backup")]),e("ul",Bt,[(t(!0),s(V,null,L(m.value,A=>(t(),s("li",{key:A.path,class:"row"},[e("img",{class:"row__thumb",loading:"lazy",alt:"Original",src:Q(be)(A.backup,120)},null,8,Ot),C[17]||(C[17]=e("span",{class:"row__arrow muted"},"⇄",-1)),e("img",{class:"row__thumb",loading:"lazy",alt:"Now",src:Q(be)(A.path,120)},null,8,jt),e("div",Tt,[e("span",{class:"row__name",title:A.path},a(A.path),9,Ut),e("span",It," original "+a(Q(ce)(A.bytesBefore))+" · replaced with "+a(Q(ce)(A.bytesAfter)),1),A.message?(t(),s("span",Dt,a(A.message),1)):d("",!0)]),e("span",{class:ne(["chip",`chip--${_(A.status)}`])},a(b[A.status]),3)]))),128))]),v.value&&I.value<v.value.entries.length?(t(),s("div",Mt,[e("button",{class:"btn",onClick:C[6]||(C[6]=A=>I.value+=Ce)}," Show more ("+a(v.value.entries.length-I.value)+" left) ",1),e("button",{class:"btn",onClick:C[7]||(C[7]=A=>I.value=v.value.entries.length)}," Show all ("+a(v.value.entries.length)+") ",1)])):d("",!0),S.value?(t(),s("p",Lt," The left picture is the saved original, the right one is the file as it is now on disk. ")):d("",!0)])):(t(),s("section",ut,[C[14]||(C[14]=e("div",{class:"head"},[e("div",null,[e("h2",null,"Backups"),e("p",{class:"muted"}," “Replace the originals” copies each file here first, so a replacement can be undone. ")])],-1)),y.value?(t(),s("p",rt,a(y.value),1)):d("",!0),N.value?(t(),s("p",dt,a(N.value),1)):d("",!0),f.value.length===0?(t(),s("p",ct,[C[9]||(C[9]=E(" No backups in ",-1)),e("code",null,a(i.value),1),C[10]||(C[10]=E(" yet. ",-1))])):d("",!0),e("ul",vt,[(t(!0),s(V,null,L(f.value,A=>(t(),s("li",{key:A.dir,class:"item"},[e("div",pt,[e("strong",null,a(r(A.createdAt)),1),e("span",mt,a(A.files)+" file"+a(A.files===1?"":"s")+" · "+a(Q(ce)(A.bytesBefore))+" → "+a(Q(ce)(A.bytesAfter)),1),e("code",{class:"item__path",title:A.dir},a(A.dir),9,ft)]),e("div",gt,[e("button",{class:"btn btn--small",onClick:P=>J(A.dir)},"Open",8,ht),e("button",{class:"btn btn--small btn--danger",onClick:P=>te(A.dir)},"Delete",8,bt)])]))),128))]),e("div",yt,[C[12]||(C[12]=e("h3",null,"Another backup folder",-1)),C[13]||(C[13]=e("p",{class:"muted small"},[E(" If you chose your own backup folder, pick the dated folder inside it (the one with a "),e("code",null,"journal.json"),E("). ")],-1)),e("form",{class:"pathrow",onSubmit:C[2]||(C[2]=et(A=>K(),["prevent"]))},[R(e("input",{"onUpdate:modelValue":C[0]||(C[0]=A=>D.value=A),class:"input",spellcheck:"false",placeholder:"Path to the dated backup folder"},null,512),[[se,D.value]]),e("button",{class:"btn",type:"button",onClick:C[1]||(C[1]=A=>B.value=!0)},[q(Z,{name:"folder",size:16}),C[11]||(C[11]=E(" Browse ",-1))]),e("button",{class:"btn btn--primary",type:"submit",disabled:D.value.trim()===""}," Open ",8,kt)],32)])])),B.value?(t(),ae(xe,{key:2,start:n.cwd,mode:"folder",title:"Choose the backup folder",onClose:C[8]||(C[8]=A=>B.value=!1),onConfirm:K},null,8,["start"])):d("",!0)]))}}),Ft=ie(Rt,[["__scopeId","data-v-1e694fea"]]),ke=Symbol("image-batch-navigate"),Ee=Symbol("image-batch-project"),Re={cwd:"",name:"this project",home:""};function Pt(n,b){const i=n.split(/[\\/]/).filter(Boolean);return{cwd:n,home:b,name:i[i.length-1]??n}}const qt={class:"sharpen"},Jt={key:0,class:"grid"},Kt={class:"field"},Ht=["value"],Wt=["value"],Gt={key:1,class:"hint muted"},Qt={class:"grid"},Yt={class:"field"},Zt=["value"],Xt=["value"],es={class:"field"},ts=["value"],ss=["value"],ls={key:0,class:"grid"},as={class:"muted"},ns=["value","onInput"],os=oe({__name:"SharpenSettings",props:Me({options:{},manage:{type:Boolean}},{modelValue:{required:!0},modelModifiers:{}}),emits:["update:modelValue"],setup(n){const b=n,i=he(ke,()=>{}),f=De(n,"modelValue"),v=c([]),N=c(!1),y=T(()=>v.value.filter(S=>!S.error)),z=T(()=>v.value.find(S=>S.name===f.value.preset));function U(S){const h=S.fields??{};return Object.entries(h).map(([m,r])=>`${m} ${r}`).join(", ")}function I(S){f.value={...f.value,mode:S},S==="preset"&&!f.value.preset&&y.value[0]&&(f.value={...f.value,mode:S,preset:y.value[0].name})}function D(S,h){f.value={...f.value,fine:{...f.value.fine,[S]:h}}}function B(S){const h=b.options.sharpen.fine.find(m=>m.key===S);return h?`${h.range[0]}–${h.range[1]}`:""}return pe(async()=>{try{v.value=await Y.get("/api/image-batch/sharpen-presets")}catch{v.value=[]}}),(S,h)=>(t(),s("div",qt,[q(ge,{"model-value":f.value.mode,label:"Sharpening",options:[{value:"none",label:"None"},{value:"preset",label:"Saved preset"},{value:"custom",label:"Custom"}],"onUpdate:modelValue":I},null,8,["model-value"]),f.value.mode==="preset"?(t(),s("div",Jt,[e("label",Kt,[h[5]||(h[5]=e("span",null,"Preset",-1)),e("select",{class:"select",value:f.value.preset,onChange:h[0]||(h[0]=m=>f.value={...f.value,preset:m.target.value})},[(t(!0),s(V,null,L(y.value,m=>(t(),s("option",{key:m.name,value:m.name},a(m.name)+" · "+a(m.scope),9,Wt))),128))],40,Ht)]),n.manage?(t(),s("button",{key:0,class:"link",onClick:h[1]||(h[1]=m=>Q(i)("sharpen"))},"Manage presets")):d("",!0),z.value?(t(),s("p",Gt,[z.value.description?(t(),s(V,{key:0},[E(a(z.value.description)+" — ",1)],64)):d("",!0),E(" "+a(U(z.value)),1)])):d("",!0)])):d("",!0),f.value.mode==="custom"?(t(),s(V,{key:1},[e("div",Qt,[e("label",Yt,[h[6]||(h[6]=e("span",null,"Sharpen for",-1)),e("select",{class:"select",value:f.value.for,onChange:h[2]||(h[2]=m=>f.value={...f.value,for:m.target.value})},[(t(!0),s(V,null,L(n.options.sharpen.targets,m=>(t(),s("option",{key:m,value:m},a(m),9,Xt))),128))],40,Zt)]),e("label",es,[h[7]||(h[7]=e("span",null,"Amount",-1)),e("select",{class:"select",value:f.value.amount,onChange:h[3]||(h[3]=m=>f.value={...f.value,amount:m.target.value})},[(t(!0),s(V,null,L(n.options.sharpen.amounts,m=>(t(),s("option",{key:m,value:m},a(m),9,ss))),128))],40,ts)])]),e("button",{class:"link",onClick:h[4]||(h[4]=m=>N.value=!N.value)},a(N.value?"Hide":"Show")+" fine settings ",1),N.value?(t(),s("div",ls,[(t(!0),s(V,null,L(n.options.sharpen.fine,m=>(t(),s("label",{key:m.key,class:"field"},[e("span",null,[E(a(m.key)+" ",1),e("small",as,"("+a(B(m.key))+")",1)]),e("input",{class:"input",inputmode:"decimal",placeholder:"table value",value:f.value.fine[m.key],onInput:r=>D(m.key,r.target.value)},null,40,ns)]))),128))])):d("",!0)],64)):d("",!0)]))}}),Fe=ie(os,[["__scopeId","data-v-1d132e3d"]]);function Pe(){return{mode:"none",preset:"",for:"screen",amount:"standard",fine:{radius:"",flat:"",jagged:"",threshold:""}}}function is(n){if(n.mode==="none")return;if(n.mode==="preset")return n.preset?{preset:n.preset}:void 0;const b={for:n.for,amount:n.amount};for(const[i,f]of Object.entries(n.fine))f.trim()!==""&&(b[i]=Number(f));return b}const qe=["widths","heights","size","longEdge","shortEdge","megapixels","percent"],us={widths:[800,1600],heights:[600],size:"1920x1080",longEdge:[1600],shortEdge:[1080],megapixels:[2],percent:[50]};function rs(n,b,i){const f={...n};return i===void 0||i===""||Array.isArray(i)&&i.length===0||typeof i=="number"&&Number.isNaN(i)?delete f[b]:f[b]=i,f}function ds(n){for(const b of qe)if(n[b]!==void 0)return b;return"original"}function cs(n,b){const i={...n};for(const f of qe)delete i[f];return delete i.matchOrientation,b!=="original"&&(i[b]=us[b]),i}function vs(n,b){if(b==="original")return"";const i=n[b];return Array.isArray(i)?i.join(", "):String(i??"")}function ps(n){const b=n.split(/[,\s]+/).filter(Boolean);if(b.length===0)return[];const i=b.map(Number);return i.some(f=>!Number.isFinite(f))?void 0:i}function Be(n){const b=Pe();if(n==null)return{state:b,lossy:!1};if(typeof n=="string")return{state:{...b,mode:"preset",preset:n},lossy:!1};if(typeof n!="object"||Array.isArray(n))return{state:b,lossy:!0};const i=n;if(typeof i.preset=="string")return Object.keys(i).filter(N=>N!=="preset").length===0?{state:{...b,mode:"preset",preset:i.preset},lossy:!1}:{state:{...b,mode:"preset",preset:i.preset},lossy:!0};const f={...b,mode:"custom"};typeof i.for=="string"&&(f.for=i.for),typeof i.amount=="string"&&(f.amount=i.amount);for(const v of["radius","flat","jagged","threshold"])i[v]!==void 0&&(f.fine[v]=String(i[v]));return{state:f,lossy:!1}}function Oe(n){if(n.mode==="none")return;if(n.mode==="preset")return n.preset||void 0;const b={for:n.for,amount:n.amount};for(const[i,f]of Object.entries(n.fine))f.trim()!==""&&(b[i]=Number(f));return b}function ms(){return{name:"New config",outputs:[{id:"responsive",widths:[400,800,1200],formats:["webp","jpg"],quality:"high"}]}}function fs(n){return{id:`recipe-${n}`,widths:[800],formats:["webp"],quality:"high"}}const gs={class:"recipe"},hs={key:0,class:"grid"},bs={class:"field"},ys=["value"],ks={key:0,class:"field"},$s=["value"],_s=["value"],ws={class:"group"},Cs={class:"grid"},Ss={class:"field"},Vs=["value"],xs=["value"],Es={key:0,class:"field"},zs=["value"],As={key:1,class:"field"},Ns=["value"],Bs={key:2,class:"field"},Os=["value"],js=["value"],Ts={key:0,class:"check"},Us=["checked"],Is={class:"group"},Ds={key:0,class:"notice notice--warn"},Ms={key:1,class:"pills"},Ls=["checked","onChange"],Rs={key:0,class:"muted small"},Fs={class:"grid"},Ps={key:0,class:"field"},qs=["value"],Js=["value"],Ks={key:1,class:"field"},Hs=["value"],Ws={key:2,class:"notice notice--warn"},Gs={class:"field"},Qs=["value"],Ys={class:"group"},Zs=["value","placeholder"],Xs={class:"pills"},el=["onClick"],tl={class:"group"},sl={key:0,class:"notice notice--warn"},ll={class:"group"},al={key:0,class:"grid"},nl=["value","onChange"],ol=["value"],il=["value","onChange"],ul={class:"field"},rl=["value"],dl={class:"field"},cl=["value"],vl=oe({__name:"RecipeForm",props:{modelValue:{},options:{},presetNames:{},isDefaults:{type:Boolean}},emits:["update:modelValue"],setup(n,{emit:b}){const i=n,f=b,v={original:"Keep the original size",widths:"By width (px)",heights:"By height (px)",size:"Fit into a box",longEdge:"By the long side (px)",shortEdge:"By the short side (px)",megapixels:"By area (megapixels)",percent:"By percentage"},N=[{key:"metadata",label:"Metadata",options:["strip","keep","keep-icc"]},{key:"rotate",label:"Rotate (degrees)",options:["90","180","270"]}],y=[{key:"withoutEnlargement",label:"Never enlarge"},{key:"autoOrient",label:"Turn by EXIF orientation"},{key:"grayscale",label:"Grayscale"},{key:"flip",label:"Flip vertically"},{key:"flop",label:"Mirror horizontally"}],z=c(""),U=c(!1),I=c(!1),D=c(!1),B=c(Be(i.modelValue.sharpen).state),S=T(()=>ds(i.modelValue)),h=T(()=>Array.isArray(i.modelValue.formats)?i.modelValue.formats:void 0),m=T(()=>i.modelValue.formats!==void 0&&h.value===void 0),r=T(()=>i.modelValue.quality),_=T(()=>r.value!==void 0&&typeof r.value=="object"),$=T(()=>{const k=r.value;return k===void 0?"":typeof k=="number"?"custom":String(k)}),F=T(()=>typeof i.modelValue.name=="string"?i.modelValue.name:""),J=T(()=>i.modelValue.maxBytes===void 0?"":String(i.modelValue.maxBytes)),K=T(()=>{const k=i.modelValue.sharpen;return k!==void 0&&typeof k!="string"&&typeof k!="object"});function M(k,l){f("update:modelValue",rs(i.modelValue,k,l))}function te(){z.value=vs(i.modelValue,S.value),U.value=!1}ye(S,te,{immediate:!0}),ye(()=>i.modelValue.sharpen,k=>{const l=Be(k);D.value=l.lossy,JSON.stringify(Oe(B.value))!==JSON.stringify(k)&&(B.value=l.state)},{immediate:!0});function x(k){f("update:modelValue",cs(i.modelValue,k.target.value))}function C(k){z.value=k;const l=ps(k);U.value=l===void 0,l&&l.length>0&&S.value!=="original"&&S.value!=="size"&&M(S.value,l)}function A(k){const l=h.value??[];M("formats",l.includes(k)?l.filter(u=>u!==k):[...l,k])}function P(k){k===""?M("quality",void 0):k==="custom"?M("quality",82):M("quality",k)}function le(k){const l=k.trim();l===""?M("maxBytes",void 0):M("maxBytes",/^\d+$/.test(l)?Number(l):l)}function re(k){B.value=k,M("sharpen",Oe(k))}function ue(k){const l=i.modelValue[k];return l===void 0?"":String(l)}function ee(k,l){M(k,l===""?void 0:l==="true")}function de(k){M("name",(F.value||"{dir}/{name}")+"{"+k+"}")}function X(k){return"{"+k+"}"}return(k,l)=>(t(),s("div",gs,[n.isDefaults?d("",!0):(t(),s("div",hs,[e("label",bs,[l[13]||(l[13]=e("span",null,"Recipe name",-1)),e("input",{class:"input",spellcheck:"false",value:String(n.modelValue.id??""),onInput:l[0]||(l[0]=u=>M("id",u.target.value))},null,40,ys)]),n.presetNames&&n.presetNames.length?(t(),s("label",ks,[l[15]||(l[15]=e("span",null,"Based on preset",-1)),e("select",{class:"select",value:String(n.modelValue.preset??""),onChange:l[1]||(l[1]=u=>M("preset",u.target.value))},[l[14]||(l[14]=e("option",{value:""},"None",-1)),(t(!0),s(V,null,L(n.presetNames,u=>(t(),s("option",{key:u,value:u},a(u),9,_s))),128))],40,$s)])):d("",!0)])),e("div",ws,[l[21]||(l[21]=e("h4",null,"Size",-1)),e("div",Cs,[e("label",Ss,[l[16]||(l[16]=e("span",null,"Method",-1)),e("select",{class:"select",value:S.value,onChange:x},[(t(!0),s(V,null,L(n.options.sizeMethods,u=>(t(),s("option",{key:u,value:u},a(v[u]),9,xs))),128))],40,Vs)]),S.value==="size"?(t(),s("label",Es,[l[17]||(l[17]=e("span",null,"Box (width × height)",-1)),e("input",{class:"input",spellcheck:"false",value:String(n.modelValue.size??""),onInput:l[2]||(l[2]=u=>M("size",u.target.value))},null,40,zs)])):S.value!=="original"?(t(),s("label",As,[l[18]||(l[18]=e("span",null,"Values (comma-separated)",-1)),e("input",{class:ne(["input",{"input--bad":U.value}]),spellcheck:"false",value:z.value,onInput:l[3]||(l[3]=u=>C(u.target.value))},null,42,Ns)])):d("",!0),S.value==="size"?(t(),s("label",Bs,[l[19]||(l[19]=e("span",null,"How it fills the box",-1)),e("select",{class:"select",value:String(n.modelValue.fit??"inside"),onChange:l[4]||(l[4]=u=>M("fit",u.target.value))},[(t(!0),s(V,null,L(n.options.fitModes,u=>(t(),s("option",{key:u,value:u},a(u),9,js))),128))],40,Os)])):d("",!0)]),S.value==="size"?(t(),s("label",Ts,[e("input",{type:"checkbox",checked:n.modelValue.matchOrientation===!0,onChange:l[5]||(l[5]=u=>M("matchOrientation",u.target.checked||void 0))},null,40,Us),l[20]||(l[20]=E(" Turn the box around for portrait images ",-1))])):d("",!0)]),e("div",Is,[l[27]||(l[27]=e("h4",null,"Formats and quality",-1)),m.value?(t(),s("p",Ds," The formats of this recipe are set in a form the editor cannot show. Use the JSON tab. ")):(t(),s("div",Ms,[(t(!0),s(V,null,L(n.options.formats,u=>(t(),s("label",{key:u,class:ne(["pill",{"pill--on":h.value?.includes(u)}])},[e("input",{type:"checkbox",checked:h.value?.includes(u),onChange:H=>A(u)},null,40,Ls),E(" "+a(u),1)],2))),128)),h.value?d("",!0):(t(),s("span",Rs,"none chosen — inherited from the defaults"))])),e("div",Fs,[_.value?d("",!0):(t(),s("label",Ps,[l[24]||(l[24]=e("span",null,"Quality",-1)),e("select",{class:"select",value:$.value,onChange:l[6]||(l[6]=u=>P(u.target.value))},[l[22]||(l[22]=e("option",{value:""},"Default (high)",-1)),(t(!0),s(V,null,L(n.options.qualityLevels,u=>(t(),s("option",{key:u,value:u},a(u),9,Js))),128)),l[23]||(l[23]=e("option",{value:"custom"},"A number…",-1))],40,qs)])),$.value==="custom"?(t(),s("label",Ks,[l[25]||(l[25]=e("span",null,"Quality (1–100)",-1)),e("input",{class:"input",inputmode:"numeric",value:String(n.modelValue.quality),onInput:l[7]||(l[7]=u=>M("quality",Number(u.target.value)))},null,40,Hs)])):d("",!0),_.value?(t(),s("p",Ws," Quality is set per format here. Use the JSON tab to change it. ")):d("",!0),e("label",Gs,[l[26]||(l[26]=e("span",null,"Largest file size",-1)),e("input",{class:"input",spellcheck:"false",placeholder:"e.g. 200KB (optional)",value:J.value,onInput:l[8]||(l[8]=u=>le(u.target.value))},null,40,Qs)])])]),e("div",Ys,[l[28]||(l[28]=e("h4",null,"File names",-1)),e("input",{class:"input",spellcheck:"false",value:F.value,placeholder:n.options.defaultTemplates[S.value],"aria-label":"File name template",onInput:l[9]||(l[9]=u=>M("name",u.target.value))},null,40,Zs),e("div",Xs,[(t(!0),s(V,null,L(n.options.templateVariables,u=>(t(),s("button",{key:u,class:"pill pill--button",onClick:H=>de(u)},a(X(u)),9,el))),128))])]),e("div",tl,[l[29]||(l[29]=e("h4",null,"Sharpening",-1)),D.value||K.value?(t(),s("p",sl," This sharpening combines a preset with extra values. Use the JSON tab to edit it. ")):(t(),ae(Fe,{key:1,"model-value":B.value,options:n.options,"onUpdate:modelValue":re},null,8,["model-value","options"]))]),e("div",ll,[e("button",{class:"link",onClick:l[10]||(l[10]=u=>I.value=!I.value)},a(I.value?"Hide":"Show")+" advanced settings ",1),I.value?(t(),s("div",al,[(t(),s(V,null,L(N,u=>e("label",{key:u.key,class:"field"},[e("span",null,a(u.label),1),e("select",{class:"select",value:u.key==="rotate"?String(n.modelValue.rotate??""):String(n.modelValue[u.key]??""),onChange:H=>M(u.key,u.key==="rotate"&&H.target.value?Number(H.target.value):H.target.value)},[l[30]||(l[30]=e("option",{value:""},"Default",-1)),(t(!0),s(V,null,L(u.options,H=>(t(),s("option",{key:H,value:H},a(H),9,ol))),128))],40,nl)])),64)),(t(),s(V,null,L(y,u=>e("label",{key:u.key,class:"field"},[e("span",null,a(u.label),1),e("select",{class:"select",value:ue(u.key),onChange:H=>ee(u.key,H.target.value)},[...l[31]||(l[31]=[e("option",{value:""},"Default",-1),e("option",{value:"true"},"Yes",-1),e("option",{value:"false"},"No",-1)])],40,il)])),64)),e("label",ul,[l[32]||(l[32]=e("span",null,"Blur (sigma)",-1)),e("input",{class:"input",inputmode:"decimal",value:n.modelValue.blur===void 0?"":String(n.modelValue.blur),onInput:l[11]||(l[11]=u=>M("blur",u.target.value===""?void 0:Number(u.target.value)))},null,40,rl)]),e("label",dl,[l[33]||(l[33]=e("span",null,"Background (#rrggbb)",-1)),e("input",{class:"input",spellcheck:"false",value:String(n.modelValue.background??""),onInput:l[12]||(l[12]=u=>M("background",u.target.value))},null,40,cl)])])):d("",!0)])]))}}),je=ie(vl,[["__scopeId","data-v-6d67983e"]]),pl={class:"card block"},ml={class:"head"},fl={key:0,class:"muted"},gl={class:"grid"},hl={class:"field"},bl={class:"field"},yl={value:"project"},kl={class:"field"},$l=["value"],_l={class:"field"},wl=["value"],Cl={key:0,class:"notice"},Sl={class:"panel"},Vl={class:"panel__body"},xl={class:"recipes"},El={class:"panel__head"},zl=["onClick"],Al={class:"muted small"},Nl={class:"panel__actions"},Bl=["disabled","onClick"],Ol=["disabled","onClick"],jl=["onClick"],Tl=["onClick"],Ul={key:0,class:"panel__body"},Il=["value"],Dl={key:0,class:"notice notice--error"},Ml={key:0,class:"lines"},Ll={key:3,class:"notice notice--error"},Rl={class:"actions"},Fl=["disabled"],Pl=oe({__name:"ConfigEditor",props:{options:{},initial:{},copyOf:{}},emits:["saved","cancel"],setup(n,{emit:b}){const i=n,f=b,v=he(Ee,Re),N=c(i.initial!==void 0),y=c(i.copyOf?structuredClone(i.copyOf.raw):ms()),z=c(i.initial?i.initial.name:i.copyOf?`${i.copyOf.name}-copy`:""),U=c(i.initial?.scope??"project"),I=c("form"),D=c(""),B=c(""),S=c(),h=c(""),m=c(!1),r=c(0);let _;const $=T(()=>Array.isArray(y.value.outputs)?y.value.outputs:[]),F=T(()=>y.value.defaults&&typeof y.value.defaults=="object"?y.value.defaults:{}),J=T(()=>y.value.presets&&typeof y.value.presets=="object"?Object.keys(y.value.presets):[]),K=T(()=>{const w=[];return J.value.length&&w.push(`${J.value.length} preset(s)`),Array.isArray(y.value.match)&&y.value.match.length&&w.push(`${y.value.match.length} rule(s)`),y.value.placeholders&&w.push("placeholders"),w}),M=T(()=>(S.value?.lines??[]).join(`
2
+ `)),te=T(()=>!m.value&&z.value.trim()!==""&&S.value?.ok===!0&&B.value==="");function x(w){y.value=w}function C(w,g){const j={...y.value};g.trim()===""?delete j[w]:j[w]=g,x(j)}function A(w){const g={...y.value};Object.keys(w).length===0?delete g.defaults:g.defaults=w,x(g)}function P(w,g){const j=[...$.value];j[w]=g,x({...y.value,outputs:j})}function le(){x({...y.value,outputs:[...$.value,fs($.value.length+1)]}),r.value=$.value.length}function re(w){x({...y.value,outputs:$.value.filter((g,j)=>j!==w)}),r.value=Math.max(0,r.value-1)}function ue(w){const g=structuredClone($.value[w]??{});typeof g.id=="string"&&(g.id=`${g.id}-copy`);const j=[...$.value];j.splice(w+1,0,g),x({...y.value,outputs:j}),r.value=w+1}function ee(w,g){const j=w+g;if(j<0||j>=$.value.length)return;const W=[...$.value],[G]=W.splice(w,1);W.splice(j,0,G),x({...y.value,outputs:W}),r.value=j}function de(w,g){return typeof w.id=="string"&&w.id?w.id:`Recipe ${g+1}`}function X(w){const g=[];for(const j of["widths","heights","longEdge","shortEdge","megapixels","percent"])Array.isArray(w[j])&&g.push(`${j} ${w[j].join(", ")}`);return typeof w.size=="string"&&g.push(`box ${w.size}`),Array.isArray(w.formats)&&g.push(w.formats.join("/")),g.join(" · ")}function k(w){D.value=w;try{const g=JSON.parse(w);if(g===null||typeof g!="object"||Array.isArray(g)){B.value="A config is a JSON object.";return}B.value="",y.value=g}catch(g){B.value=g instanceof Error?g.message:String(g)}}function l(w){if(w==="json"){D.value=JSON.stringify(y.value,null,2),B.value="",I.value="json";return}B.value||(I.value="form")}async function u(){try{S.value=await Y.post("/api/image-batch/config/validate",{raw:y.value})}catch(w){S.value={ok:!1,error:w instanceof Error?w.message:String(w)}}}ye(y,()=>{clearTimeout(_),_=setTimeout(()=>{u()},350)},{deep:!0});async function H(w=!1){m.value=!0,h.value="";try{await Y.post("/api/image-batch/config/save",{name:z.value,scope:U.value,raw:y.value,overwrite:w,...i.initial?{originalName:i.initial.name,originalScope:i.initial.scope}:{}}),f("saved",z.value)}catch(g){if(g instanceof Le&&g.status===409&&g.body.exists===!0){if(window.confirm(`${g.message}. Replace it?`)){await H(!0);return}}else h.value=g instanceof Error?g.message:String(g)}finally{m.value=!1}}return pe(async()=>{if(i.initial)try{const w=await Y.get(`/api/image-batch/config?name=${encodeURIComponent(i.initial.name)}&scope=${i.initial.scope}`);y.value=w.raw}catch(w){h.value=w instanceof Error?w.message:String(w)}finally{N.value=!1}u()}),Ve(()=>clearTimeout(_)),(w,g)=>(t(),s("section",pl,[e("div",ml,[e("h2",null,a(n.initial?`Edit ${n.initial.name}`:n.copyOf?"Copy of a config":"New config"),1),e("button",{class:"btn btn--ghost",onClick:g[0]||(g[0]=j=>f("cancel"))},[q(Z,{name:"close",size:16}),g[8]||(g[8]=E(" Close ",-1))])]),N.value?(t(),s("p",fl,"Loading…")):(t(),s(V,{key:1},[e("div",gl,[e("label",hl,[g[9]||(g[9]=e("span",null,"File name",-1)),R(e("input",{"onUpdate:modelValue":g[1]||(g[1]=j=>z.value=j),class:"input",spellcheck:"false",placeholder:"web"},null,512),[[se,z.value]])]),e("label",bl,[g[11]||(g[11]=e("span",null,"Where to keep it",-1)),R(e("select",{"onUpdate:modelValue":g[2]||(g[2]=j=>U.value=j),class:"select"},[e("option",yl,"This project ("+a(Q(v).name)+")",1),g[10]||(g[10]=e("option",{value:"global"},"All projects (your home folder)",-1))],512),[[ve,U.value]])]),e("label",kl,[g[12]||(g[12]=e("span",null,"Title",-1)),e("input",{class:"input",value:String(y.value.name??""),onInput:g[3]||(g[3]=j=>C("name",j.target.value))},null,40,$l)]),e("label",_l,[g[13]||(g[13]=e("span",null,"Description",-1)),e("input",{class:"input",value:String(y.value.description??""),onInput:g[4]||(g[4]=j=>C("description",j.target.value))},null,40,wl)])]),q(ge,{"model-value":I.value,label:"Editor mode",options:[{value:"form",label:"Form"},{value:"json",label:"JSON"}],"onUpdate:modelValue":l},null,8,["model-value"]),I.value==="form"?(t(),s(V,{key:0},[K.value.length?(t(),s("p",Cl," This config also has "+a(K.value.join(", "))+". They are kept as they are; edit them in the JSON tab. ",1)):d("",!0),e("details",Sl,[g[14]||(g[14]=e("summary",null,"Defaults for every recipe",-1)),e("div",Vl,[q(je,{"model-value":F.value,options:n.options,"preset-names":J.value,"is-defaults":"","onUpdate:modelValue":A},null,8,["model-value","options","preset-names"])])]),e("div",xl,[(t(!0),s(V,null,L($.value,(j,W)=>(t(),s("div",{key:W,class:ne(["panel",{"panel--open":r.value===W}])},[e("div",El,[e("button",{class:"panel__title",onClick:G=>r.value=r.value===W?-1:W},[e("strong",null,a(de(j,W)),1),e("span",Al,a(X(j)),1)],8,zl),e("div",Nl,[e("button",{class:"btn btn--ghost btn--small",disabled:W===0,"aria-label":"Move up",onClick:G=>ee(W,-1)},[q(Z,{name:"up",size:14})],8,Bl),e("button",{class:"btn btn--ghost btn--small",disabled:W===$.value.length-1,"aria-label":"Move down",onClick:G=>ee(W,1)},[q(Z,{name:"down",size:14})],8,Ol),e("button",{class:"btn btn--ghost btn--small","aria-label":"Duplicate recipe",onClick:G=>ue(W)},[q(Z,{name:"copy",size:14})],8,jl),e("button",{class:"btn btn--ghost btn--small btn--danger","aria-label":"Remove recipe",onClick:G=>re(W)},[q(Z,{name:"close",size:14})],8,Tl)])]),r.value===W?(t(),s("div",Ul,[q(je,{"model-value":j,options:n.options,"preset-names":J.value,"onUpdate:modelValue":G=>P(W,G)},null,8,["model-value","options","preset-names","onUpdate:modelValue"])])):d("",!0)],2))),128))]),e("button",{class:"btn",onClick:le},[q(Z,{name:"plus",size:16}),g[15]||(g[15]=E(" Add a recipe",-1))])],64)):(t(),s(V,{key:1},[e("textarea",{class:"json",spellcheck:"false",value:D.value,"aria-label":"Config JSON",onInput:g[5]||(g[5]=j=>k(j.target.value))},null,40,Il),B.value?(t(),s("p",Dl,a(B.value),1)):d("",!0)],64)),S.value?(t(),s("div",{key:2,class:ne(["check-panel",S.value.ok?"check-panel--ok":"check-panel--bad"]),role:"status"},[S.value.ok?(t(),s(V,{key:0},[g[16]||(g[16]=e("strong",null,"The config is valid.",-1)),S.value.lines?.length?(t(),s("pre",Ml,a(M.value),1)):d("",!0)],64)):(t(),s(V,{key:1},[E(a(S.value.error),1)],64))],2)):d("",!0),h.value?(t(),s("p",Ll,a(h.value),1)):d("",!0),e("div",Rl,[e("button",{class:"btn btn--primary",disabled:!te.value,onClick:g[6]||(g[6]=j=>H(!1))},[q(Z,{name:"save",size:15}),g[17]||(g[17]=E(" Save config ",-1))],8,Fl),e("button",{class:"btn",onClick:g[7]||(g[7]=j=>f("cancel"))},"Cancel")])],64))]))}}),ql=ie(Pl,[["__scopeId","data-v-5992429e"]]),Jl={class:"view"},Kl={key:1,class:"card block"},Hl={class:"head"},Wl={key:0,class:"notice"},Gl={key:1,class:"notice notice--error"},Ql={key:2,class:"muted"},Yl={class:"list"},Zl={class:"item__main"},Xl={class:"item__title"},ea={class:"tag"},ta={key:0,class:"tag"},sa={key:0,class:"item__error"},la={key:1,class:"muted"},aa=["title"],na={class:"item__actions"},oa=["onClick"],ia=["onClick"],ua=["onClick"],ra=oe({__name:"ConfigsView",props:{options:{}},setup(n){const b=c([]),i=c(""),f=c(""),v=c("list"),N=c(),y=c();async function z(){try{b.value=await Y.get("/api/image-batch/configs")}catch(h){i.value=h instanceof Error?h.message:String(h)}}function U(){N.value=void 0,y.value=void 0,v.value="edit"}function I(h){N.value=h,y.value=void 0,v.value="edit"}async function D(h){try{const m=await Y.get(`/api/image-batch/config?name=${encodeURIComponent(h.name)}&scope=${h.scope}`);N.value=void 0,y.value={name:h.name,raw:m.raw},v.value="edit"}catch(m){i.value=m instanceof Error?m.message:String(m)}}async function B(h){if(window.confirm(`Delete the config “${h.name}”? A .bak copy is kept next to it.`))try{await Y.post("/api/image-batch/config/delete",{name:h.name,scope:h.scope}),f.value=`Deleted ${h.name}.`,await z()}catch(m){i.value=m instanceof Error?m.message:String(m)}}async function S(h){v.value="list",f.value=`Saved ${h}.`,await z()}return pe(z),(h,m)=>(t(),s("div",Jl,[v.value==="edit"?(t(),ae(ql,{key:0,options:n.options,initial:N.value?{name:N.value.name,scope:N.value.scope}:void 0,"copy-of":y.value,onSaved:S,onCancel:m[0]||(m[0]=r=>v.value="list")},null,8,["options","initial","copy-of"])):(t(),s("section",Kl,[e("div",Hl,[m[2]||(m[2]=e("div",null,[e("h2",null,"Configs"),e("p",{class:"muted"}," A config is a reusable set of rules: sizes, formats, quality, names, sharpening. Apply it to any folder from the Convert tab. ")],-1)),e("button",{class:"btn btn--primary",onClick:U},[q(Z,{name:"plus",size:16}),m[1]||(m[1]=E(" New config ",-1))])]),f.value?(t(),s("p",Wl,a(f.value),1)):d("",!0),i.value?(t(),s("p",Gl,a(i.value),1)):d("",!0),b.value.length===0&&!i.value?(t(),s("p",Ql," No configs yet. Create the first one, or use quick settings on the Convert tab. ")):d("",!0),e("ul",Yl,[(t(!0),s(V,null,L(b.value,r=>(t(),s("li",{key:r.scope+r.name+r.path,class:"item"},[e("div",Zl,[e("div",Xl,[e("strong",null,a(r.name),1),e("span",ea,a(r.scope),1),r.kind!=="json"?(t(),s("span",ta,"."+a(r.kind),1)):d("",!0)]),r.error?(t(),s("p",sa,a(r.error),1)):(t(),s("p",la,[r.title?(t(),s(V,{key:0},[E(a(r.title)+" — ",1)],64)):d("",!0),E(" "+a(r.recipes)+" recipe"+a(r.recipes===1?"":"s")+" ",1),r.rules?(t(),s(V,{key:1},[E(", "+a(r.rules)+" rule"+a(r.rules===1?"":"s"),1)],64)):d("",!0),r.description?(t(),s(V,{key:2},[E(" · "+a(r.description),1)],64)):d("",!0)])),e("code",{class:"item__path",title:r.path},a(r.path),9,aa)]),e("div",na,[r.editable?(t(),s("button",{key:0,class:"btn btn--small",onClick:_=>I(r)},"Edit",8,oa)):d("",!0),r.editable&&!r.error?(t(),s("button",{key:1,class:"btn btn--small",onClick:_=>D(r)}," Duplicate ",8,ia)):d("",!0),e("button",{class:"btn btn--small btn--danger",onClick:_=>B(r)},"Delete",8,ua)])]))),128))])]))]))}}),da=ie(ra,[["__scopeId","data-v-5b011a56"]]),ca={class:"card block"},va={class:"head"},pa={class:"muted"},ma={class:"chips"},fa={key:0,class:"chip"},ga={key:1,class:"chip chip--good"},ha={key:0,class:"notice"},ba={key:1,class:"notice"},ya={key:2,class:"muted small"},ka={key:3,class:"muted small"},$a={key:4,class:"errors"},_a={class:"rows"},wa=["src"],Ca=["src"],Sa={key:1,class:"row__thumb row__thumb--empty muted"},Va={class:"row__text"},xa=["title"],Ea={class:"muted small"},za={key:0,class:"small row__message"},Aa={class:"row__sizes"},Na={key:0},Ba={key:1,class:"muted"},Oa={key:2},ja={key:5,class:"more"},Te=30,Ta=oe({__name:"BatchResults",props:{report:{}},setup(n){const b=n,i=he(ke,()=>{});function f(){try{b.report.backupDir&&localStorage.setItem("mediatoolz-ui.open-backup",b.report.backupDir)}catch{i("backups");return}i("backups")}const v=c(Te),N={written:"written","up-to-date":"up to date",skipped:"skipped",conflict:"conflict","would-write":"would write","would-overwrite":"would overwrite",replaced:"replaced",kept:"kept original","already-processed":"already processed","would-replace":"would replace","would-keep":"would keep",error:"error"},y=new Set(["written","replaced","up-to-date"]),z=new Set(["error","conflict"]),U=new Set(["written","replaced","up-to-date","skipped","kept"]),I=T(()=>Object.entries(b.report.counts).filter(([,r])=>r>0)),D=T(()=>{const{bytesIn:r,bytesOut:_}=b.report;if(!(b.report.dryRun||r<=0||_<=0))return{bytes:r-_,percent:Math.round((1-_/r)*100)}}),B=T(()=>b.report.results.slice(0,v.value));function S(r){return y.has(r)?"good":z.has(r)?"bad":"neutral"}function h(r){if(r.before===void 0||r.bytes===void 0||r.before<=0)return"";const _=Math.round((r.bytes/r.before-1)*100);return`${_>0?"+":""}${_}%`}function m(r){return!b.report.dryRun&&U.has(r.status)&&r.status!=="skipped"}return(r,_)=>(t(),s("section",ca,[e("div",va,[e("h2",null,a(n.report.dryRun?"Plan":"Result"),1),e("span",pa,[E(a(n.report.results.length)+" file"+a(n.report.results.length===1?"":"s")+" ",1),n.report.configLabel?(t(),s(V,{key:0},[E(" · config "+a(n.report.configLabel),1)],64)):d("",!0)])]),e("div",ma,[(t(!0),s(V,null,L(I.value,([$,F])=>(t(),s("span",{key:$,class:ne(["chip",`chip--${S($)}`])},a(F)+" "+a(N[$]),3))),128)),n.report.deduped?(t(),s("span",fa,a(n.report.deduped)+" merged (same result)",1)):d("",!0),D.value?(t(),s("span",ga,[E(" saved "+a(Q(ce)(Math.max(D.value.bytes,0)))+" ",1),D.value.percent>0?(t(),s(V,{key:0},[E("(−"+a(D.value.percent)+"%)",1)],64)):d("",!0)])):d("",!0)]),n.report.dryRun?(t(),s("p",ha," This is a preview: nothing was written. Press “Convert” to do it. ")):d("",!0),n.report.backupDir&&!n.report.dryRun?(t(),s("p",ba,[_[2]||(_[2]=E(" Originals were copied to ",-1)),e("code",null,a(n.report.backupDir),1),_[3]||(_[3]=E(" first. ",-1)),e("button",{class:"link",onClick:f},"View or restore this backup")])):d("",!0),n.report.derived.length?(t(),s("p",ya,a(n.report.derived.length)+" earlier result"+a(n.report.derived.length===1?" was":"s were")+" not treated as sources. ",1)):d("",!0),n.report.vectors.length?(t(),s("p",ka,a(n.report.vectors.length)+" SVG file"+a(n.report.vectors.length===1?" was":"s were")+" left out of the replacement. ",1)):d("",!0),n.report.failures.length?(t(),s("ul",$a,[(t(!0),s(V,null,L(n.report.failures,$=>(t(),s("li",{key:$.file},[e("code",null,a($.file),1),E(" — "+a($.message),1)]))),128))])):d("",!0),e("ul",_a,[(t(!0),s(V,null,L(B.value,$=>(t(),s("li",{key:`${$.sourceAbs}|${$.outputAbs}|${$.recipe}`,class:"row"},[e("img",{class:"row__thumb",loading:"lazy",alt:"",src:Q(be)($.sourceAbs,120)},null,8,wa),_[4]||(_[4]=e("span",{class:"row__arrow muted"},"→",-1)),m($)?(t(),s("img",{key:0,class:"row__thumb",loading:"lazy",alt:"",src:Q(be)($.outputAbs,120)},null,8,Ca)):(t(),s("span",Sa,a(n.report.dryRun?"plan":"—"),1)),e("div",Va,[e("span",{class:"row__name",title:$.outputAbs},a($.output),9,xa),e("span",Ea,[E(" from "+a($.source)+" · "+a($.width)+"×"+a($.height)+" · "+a($.format)+" ",1),$.recipe?(t(),s(V,{key:0},[E(" · "+a($.recipe),1)],64)):d("",!0)]),$.message?(t(),s("span",za,a($.message),1)):d("",!0)]),e("div",Aa,[$.before!==void 0?(t(),s("span",Na,a(Q(ce)($.before)),1)):d("",!0),$.before!==void 0&&$.bytes!==void 0?(t(),s("span",Ba,"→")):d("",!0),$.bytes!==void 0?(t(),s("strong",Oa,a(Q(ce)($.bytes)),1)):d("",!0),h($)?(t(),s("span",{key:3,class:ne(["row__delta",{"row__delta--down":h($).startsWith("-")}])},a(h($)),3)):d("",!0)]),e("span",{class:ne(["chip",`chip--${S($.status)}`])},a(N[$.status]),3)]))),128))]),v.value<n.report.results.length?(t(),s("div",ja,[e("button",{class:"btn",onClick:_[0]||(_[0]=$=>v.value+=Te)}," Show more ("+a(n.report.results.length-v.value)+" left) ",1),e("button",{class:"btn",onClick:_[1]||(_[1]=$=>v.value=n.report.results.length)}," Show all ("+a(n.report.results.length)+") ",1)])):d("",!0)]))}}),Ua=ie(Ta,[["__scopeId","data-v-56eb5236"]]),Ia={class:"view"},Da={key:0,class:"notice notice--warn"},Ma={class:"card block"},La={class:"pathrow"},Ra={class:"check"},Fa={key:1,class:"muted"},Pa={class:"check"},qa={class:"check"},Ja={key:3,class:"field narrow"},Ka={class:"card block"},Ha={class:"head"},Wa={key:0,class:"muted"},Ga={class:"field narrow"},Qa=["value","disabled"],Ya={key:0,class:"muted"},Za={class:"group"},Xa={class:"check"},en={key:0,class:"muted small"},tn={key:1,class:"grid"},sn={class:"field"},ln=["value"],an={key:0,class:"field"},nn={key:1,class:"field"},on=["placeholder"],un={key:2,class:"field"},rn=["value"],dn={key:2,class:"check"},cn={key:3,class:"muted small"},vn={class:"group"},pn={key:0,class:"muted small"},mn={key:1,class:"pills"},fn=["checked","onChange"],gn={class:"grid"},hn={class:"field"},bn=["value"],yn={key:0,class:"field"},kn={class:"field"},$n={key:0,class:"group"},_n=["placeholder"],wn={class:"pills"},Cn=["onClick"],Sn={class:"muted small"},Vn={class:"group"},xn={class:"runbar"},En=["disabled"],zn=["disabled"],An={key:0,class:"muted"},Nn={key:1,class:"card block",role:"status"},Bn={class:"progress"},On={class:"muted"},jn={key:2,class:"notice notice--error"},Tn=oe({__name:"ConvertView",props:{cwd:{},options:{}},setup(n){const b=n,i=he(ke,()=>{}),f={original:"Keep the original size",widths:"By width (px)",heights:"By height (px)",size:"Fit into a box",longEdge:"By the long side (px)",shortEdge:"By the short side (px)",megapixels:"By area (megapixels)",percent:"By percentage"},v={original:"",widths:"e.g. 400, 800, 1200 — one file per size",heights:"e.g. 300, 600",size:"",longEdge:"e.g. 1600 — the same for landscape and portrait",shortEdge:"e.g. 1080",megapixels:"e.g. 2, 0.5",percent:"e.g. 50, 25"},N={name:"photo",ext:"jpg",orig:"photo.jpg",dir:"trips/2026",format:"webp",width:"800",height:"600",size:"800x600",long:"800",short:"600",mp:"0.48",percent:"50",scale:"2",index:"001",hash:"a1b2c3d4",date:"2026-10-08"},y=T(()=>b.options),z=c([]),U=c([]),I=c(!0),D=c(0),B=c("out"),S=c(""),h=c(!1),m=c("skip"),r=c(!0),_=c(!0),$=c(!1),F=c("quick"),J=c(""),K=c(!1),M=c("widths"),te=c("800, 1600"),x=c("1920x1080"),C=c("inside"),A=c(!1),P=c(["webp"]),le=c(""),re=c("82"),ue=c(""),ee=c(""),de=c(Pe()),X=c("idle"),k=c({done:0,total:0}),l=c(""),u=c();let H;const w=T(()=>K.value?M.value:"original"),g=T(()=>(y.value?.sizeMethods??[]).filter(O=>O!=="original")),j=T(()=>b.cwd.includes("\\")?"\\":"/"),W=T(()=>l.value.toLowerCase().includes("sharp")),G=T(()=>z.value.find(O=>O.name===J.value)),Je=T(()=>k.value.total>0?Math.round(k.value.done/k.value.total*100):0),$e=T(()=>y.value?.defaultTemplates[w.value]??"{dir}/{name}.{format}"),Ke=T(()=>(ee.value.trim()||$e.value).replace(/\{([a-zA-Z]+)(?::(\d+))?\}/g,(o,p)=>N[p]??`{${p}}`)),_e=T(()=>U.value.length===0?"Add at least one folder or image.":D.value===0&&U.value.length>0?"No images found in the added sources.":B.value==="out"&&S.value.trim()===""?"Choose the folder for the results.":F.value==="config"&&J.value===""?"Choose a saved config.":F.value==="quick"&&B.value!=="replace"&&P.value.length===0?"Pick at least one format.":""),ze=T(()=>_e.value===""&&X.value!=="running");function He(O){D.value=O?.total.files??0,U.value.length>0&&O===void 0&&(D.value=1)}function We(O){P.value=P.value.includes(O)?P.value.filter(o=>o!==O):[...P.value,O]}function Ge(O){return"{"+O+"}"}function Qe(O){S.value=O[0]??S.value,$.value=!1}function Ye(O){ee.value=(ee.value||$e.value)+`{${O}}`}function Ze(){const O=U.value[0];if(S.value!==""||!O)return;const o=O.replace(/[\\/]+$/,"");S.value=`${o}${j.value}converted`}function Xe(){const O={sizeMethod:w.value};w.value==="size"?(O.box=x.value,O.fit=C.value,O.matchOrientation=A.value):w.value!=="original"&&(O.values=te.value.split(/[,\s]+/).filter(Boolean).map(Number)),O.formats=B.value==="replace"?["original"]:P.value,le.value==="custom"?O.quality=Number(re.value):le.value&&(O.quality=le.value),ue.value.trim()&&(O.maxBytes=ue.value.trim()),B.value!=="replace"&&ee.value.trim()&&(O.name=ee.value.trim());const o=is(de.value);return o&&(O.sharpen=o),O}async function we(O,o=!1){if(!(!O&&B.value==="replace"&&!window.confirm(`This rewrites ${D.value} original file${D.value===1?"":"s"} in place.`+(r.value?" The originals are copied to a backup folder first.":" No backup will be made.")+`
3
+
4
+ Continue?`))){H?.(),X.value="running",l.value="",u.value=void 0,k.value={done:0,total:0};try{const{jobId:p}=await Y.post("/api/image-batch/run",{paths:U.value,recursive:I.value,placement:B.value,out:S.value,flat:h.value,overwrite:m.value,backup:r.value,onlyIfSmaller:_.value,config:F.value==="config"?J.value:void 0,settings:F.value==="quick"?Xe():void 0,dryRun:O,installSharp:o});H=st(p,{onProgress:me=>{k.value=me},onDone:me=>{u.value=me,X.value="done"},onFailed:me=>{l.value=me,X.value="failed"}})}catch(p){l.value=p instanceof Error?p.message:String(p),X.value="failed"}}}return pe(async()=>{try{z.value=await Y.get("/api/image-batch/configs")}catch(O){l.value=O instanceof Error?O.message:String(O),X.value="failed"}}),Ve(()=>H?.()),(O,o)=>(t(),s("div",Ia,[y.value&&!y.value.sharp?(t(),s("div",Da," The sharp image library is not installed yet. Previews are limited, and the first run will offer to install it. ")):d("",!0),q(lt,{sources:U.value,"onUpdate:sources":o[0]||(o[0]=p=>U.value=p),recursive:I.value,"onUpdate:recursive":o[1]||(o[1]=p=>I.value=p),"scan-url":"/api/image-batch/scan",start:n.cwd,onScanned:He},null,8,["sources","recursive","start"]),e("section",Ma,[o[35]||(o[35]=e("h2",null,"Where the results go",-1)),q(ge,{modelValue:B.value,"onUpdate:modelValue":[o[2]||(o[2]=p=>B.value=p),Ze],label:"Placement",options:[{value:"out",label:"A separate folder",hint:"the sources are not touched"},{value:"beside",label:"Next to each original",hint:"new files beside the sources"},{value:"replace",label:"Replace the originals",hint:"rewrites the files, with a backup"}]},null,8,["modelValue"]),B.value==="out"?(t(),s(V,{key:0},[e("div",La,[R(e("input",{"onUpdate:modelValue":o[3]||(o[3]=p=>S.value=p),class:"input",spellcheck:"false",placeholder:"Folder for the results","aria-label":"Folder for the results"},null,512),[[se,S.value]]),e("button",{class:"btn",onClick:o[4]||(o[4]=p=>$.value=!0)},[q(Z,{name:"folder",size:16}),o[28]||(o[28]=E(" Browse ",-1))])]),e("label",Ra,[R(e("input",{"onUpdate:modelValue":o[5]||(o[5]=p=>h.value=p),type:"checkbox"},null,512),[[fe,h.value]]),o[29]||(o[29]=E(" Put everything in one folder (no subfolders) ",-1))])],64)):B.value==="beside"?(t(),s("p",Fa," Each result is written next to its original, with the size or format in the name. ")):(t(),s(V,{key:2},[o[32]||(o[32]=e("p",{class:"notice notice--warn"}," Each original is overwritten with its converted version, keeping its own format. Use the preview first. ",-1)),e("label",Pa,[R(e("input",{"onUpdate:modelValue":o[6]||(o[6]=p=>r.value=p),type:"checkbox"},null,512),[[fe,r.value]]),o[30]||(o[30]=E(" Copy the originals to a backup folder first",-1))]),e("label",qa,[R(e("input",{"onUpdate:modelValue":o[7]||(o[7]=p=>_.value=p),type:"checkbox"},null,512),[[fe,_.value]]),o[31]||(o[31]=E(" Replace a file only when the result is smaller ",-1))])],64)),B.value!=="replace"?(t(),s("label",Ja,[o[34]||(o[34]=e("span",null,"If a result already exists",-1)),R(e("select",{"onUpdate:modelValue":o[8]||(o[8]=p=>m.value=p),class:"select"},[...o[33]||(o[33]=[e("option",{value:"skip"},"Skip it",-1),e("option",{value:"overwrite"},"Overwrite it",-1),e("option",{value:"error"},"Stop and report it",-1)])],512),[[ve,m.value]])])):d("",!0)]),e("section",Ka,[e("div",Ha,[o[36]||(o[36]=e("h2",null,"What to do",-1)),q(ge,{modelValue:F.value,"onUpdate:modelValue":o[9]||(o[9]=p=>F.value=p),label:"Settings source",options:[{value:"quick",label:"Quick settings"},{value:"config",label:"Saved config"}]},null,8,["modelValue"])]),F.value==="config"?(t(),s(V,{key:0},[z.value.length===0?(t(),s("p",Wa,[o[37]||(o[37]=E(" No configs yet. ",-1)),e("button",{class:"link",onClick:o[10]||(o[10]=p=>Q(i)("configs"))},"Create the first one"),o[38]||(o[38]=E(" or use quick settings. ",-1))])):(t(),s(V,{key:1},[e("label",Ga,[o[40]||(o[40]=e("span",null,"Config",-1)),R(e("select",{"onUpdate:modelValue":o[11]||(o[11]=p=>J.value=p),class:"select"},[o[39]||(o[39]=e("option",{value:"",disabled:""},"Choose a config…",-1)),(t(!0),s(V,null,L(z.value,p=>(t(),s("option",{key:p.name+p.scope,value:p.name,disabled:!!p.error},a(p.name)+" · "+a(p.scope),9,Qa))),128))],512),[[ve,J.value]])]),e("button",{class:"link",onClick:o[12]||(o[12]=p=>Q(i)("configs"))},"Manage configs"),G.value?(t(),s("p",Ya,[G.value.title?(t(),s(V,{key:0},[E(a(G.value.title)+" — ",1)],64)):d("",!0),E(" "+a(G.value.recipes)+" recipe"+a(G.value.recipes===1?"":"s")+" ",1),G.value.rules?(t(),s(V,{key:1},[E(", "+a(G.value.rules)+" rule"+a(G.value.rules===1?"":"s"),1)],64)):d("",!0),G.value.description?(t(),s(V,{key:2},[E(" · "+a(G.value.description),1)],64)):d("",!0)])):d("",!0)],64))],64)):y.value?(t(),s(V,{key:1},[e("div",Za,[o[47]||(o[47]=e("h3",null,"Size",-1)),e("label",Xa,[R(e("input",{"onUpdate:modelValue":o[13]||(o[13]=p=>K.value=p),type:"checkbox"},null,512),[[fe,K.value]]),o[41]||(o[41]=E(" Resize the images ",-1))]),K.value?d("",!0):(t(),s("p",en," The size does not change: the images are only converted to the chosen format. ")),K.value?(t(),s("div",tn,[e("label",sn,[o[42]||(o[42]=e("span",null,"Method",-1)),R(e("select",{"onUpdate:modelValue":o[14]||(o[14]=p=>M.value=p),class:"select"},[(t(!0),s(V,null,L(g.value,p=>(t(),s("option",{key:p,value:p},a(f[p]),9,ln))),128))],512),[[ve,M.value]])]),K.value&&w.value==="size"?(t(),s("label",an,[o[43]||(o[43]=e("span",null,"Box (width × height)",-1)),R(e("input",{"onUpdate:modelValue":o[15]||(o[15]=p=>x.value=p),class:"input",spellcheck:"false",placeholder:"1920x1080"},null,512),[[se,x.value]])])):K.value?(t(),s("label",nn,[o[44]||(o[44]=e("span",null,"Values",-1)),R(e("input",{"onUpdate:modelValue":o[16]||(o[16]=p=>te.value=p),class:"input",spellcheck:"false",placeholder:v[w.value]},null,8,on),[[se,te.value]])])):d("",!0),K.value&&w.value==="size"?(t(),s("label",un,[o[45]||(o[45]=e("span",null,"How it fills the box",-1)),R(e("select",{"onUpdate:modelValue":o[17]||(o[17]=p=>C.value=p),class:"select"},[(t(!0),s(V,null,L(y.value.fitModes,p=>(t(),s("option",{key:p,value:p},a(p),9,rn))),128))],512),[[ve,C.value]])])):d("",!0)])):d("",!0),w.value==="size"?(t(),s("label",dn,[R(e("input",{"onUpdate:modelValue":o[18]||(o[18]=p=>A.value=p),type:"checkbox"},null,512),[[fe,A.value]]),o[46]||(o[46]=E(" Turn the box around for portrait images ",-1))])):K.value&&v[w.value]?(t(),s("p",cn,a(v[w.value])+". Images are never enlarged. ",1)):d("",!0)]),e("div",vn,[o[53]||(o[53]=e("h3",null,"Formats",-1)),B.value==="replace"?(t(),s("p",pn," Each file keeps its own format when the originals are replaced. ")):(t(),s("div",mn,[(t(!0),s(V,null,L(y.value.formats,p=>(t(),s("label",{key:p,class:ne(["pill",{"pill--on":P.value.includes(p)}])},[e("input",{type:"checkbox",checked:P.value.includes(p),onChange:me=>We(p)},null,40,fn),E(" "+a(p),1)],2))),128))])),e("div",gn,[e("label",hn,[o[50]||(o[50]=e("span",null,"Quality",-1)),R(e("select",{"onUpdate:modelValue":o[19]||(o[19]=p=>le.value=p),class:"select"},[o[48]||(o[48]=e("option",{value:""},"Default (high)",-1)),(t(!0),s(V,null,L(y.value.qualityLevels,p=>(t(),s("option",{key:p,value:p},a(p),9,bn))),128)),o[49]||(o[49]=e("option",{value:"custom"},"A number…",-1))],512),[[ve,le.value]])]),le.value==="custom"?(t(),s("label",yn,[o[51]||(o[51]=e("span",null,"Quality (1–100)",-1)),R(e("input",{"onUpdate:modelValue":o[20]||(o[20]=p=>re.value=p),class:"input",inputmode:"numeric"},null,512),[[se,re.value]])])):d("",!0),e("label",kn,[o[52]||(o[52]=e("span",null,"Largest file size",-1)),R(e("input",{"onUpdate:modelValue":o[21]||(o[21]=p=>ue.value=p),class:"input",spellcheck:"false",placeholder:"e.g. 200KB (optional)"},null,512),[[se,ue.value]])])])]),B.value!=="replace"?(t(),s("div",$n,[o[55]||(o[55]=e("h3",null,"File names",-1)),R(e("input",{"onUpdate:modelValue":o[22]||(o[22]=p=>ee.value=p),class:"input",spellcheck:"false",placeholder:$e.value,"aria-label":"File name template"},null,8,_n),[[se,ee.value]]),e("div",wn,[(t(!0),s(V,null,L(y.value.templateVariables,p=>(t(),s("button",{key:p,class:"pill pill--button",onClick:me=>Ye(p)},a(Ge(p)),9,Cn))),128))]),e("p",Sn,[o[54]||(o[54]=E(" Example: ",-1)),e("code",null,a(Ke.value),1),ee.value.trim()?d("",!0):(t(),s(V,{key:0},[E(" (the default for this size method)")],64))])])):d("",!0),e("div",Vn,[o[56]||(o[56]=e("h3",null,"Sharpening",-1)),q(Fe,{modelValue:de.value,"onUpdate:modelValue":o[23]||(o[23]=p=>de.value=p),options:y.value,manage:""},null,8,["modelValue","options"])])],64)):d("",!0)]),e("div",xn,[e("button",{class:"btn",disabled:!ze.value,onClick:o[24]||(o[24]=p=>we(!0))},"Preview",8,En),e("button",{class:"btn btn--primary",disabled:!ze.value,onClick:o[25]||(o[25]=p=>we(!1))},[q(Z,{name:"play",size:15}),E(" "+a(X.value==="running"?"Working…":"Convert"),1)],8,zn),_e.value?(t(),s("span",An,a(_e.value),1)):d("",!0)]),X.value==="running"?(t(),s("div",Nn,[e("div",Bn,[e("div",{class:"progress__bar",style:Se({width:`${Je.value}%`})},null,4)]),e("span",On,a(k.value.done)+" of "+a(k.value.total||"…")+" files",1)])):d("",!0),X.value==="failed"?(t(),s("div",jn,[e("p",null,a(l.value),1),W.value?(t(),s("button",{key:0,class:"btn btn--small",onClick:o[26]||(o[26]=p=>we(!1,!0))}," Install sharp and retry ")):d("",!0)])):d("",!0),u.value?(t(),ae(Ua,{key:3,report:u.value},null,8,["report"])):d("",!0),$.value?(t(),ae(xe,{key:4,start:S.value||U.value[0]||n.cwd,mode:"folder",title:"Choose the folder for the results",onClose:o[27]||(o[27]=p=>$.value=!1),onConfirm:Qe},null,8,["start"])):d("",!0)]))}}),Un=ie(Tn,[["__scopeId","data-v-017cd3fb"]]),In=["aria-busy"],Dn={class:"compare__scroll"},Mn={class:"compare__stage"},Ln=["src"],Rn=["src"],Fn={class:"compare__tag compare__tag--left"},Pn={class:"compare__tag compare__tag--right"},qn=oe({__name:"UiCompare",props:{before:{},after:{},beforeLabel:{},afterLabel:{}},setup(n){const b=c(50),i=c(!0);return(f,v)=>(t(),s("div",{class:"compare","aria-busy":i.value},[e("div",Dn,[e("div",Mn,[e("img",{class:"compare__img",alt:"Before",src:n.before,onLoad:v[0]||(v[0]=N=>i.value=!1)},null,40,Ln),e("img",{class:"compare__img compare__img--top",alt:"After",src:n.after,style:Se({clipPath:`inset(0 0 0 ${b.value}%)`})},null,12,Rn),e("span",{class:"compare__line",style:Se({left:`${b.value}%`})},null,4),e("span",Fn,a(n.beforeLabel??"Before"),1),e("span",Pn,a(n.afterLabel??"After"),1)])]),R(e("input",{"onUpdate:modelValue":v[1]||(v[1]=N=>b.value=N),class:"compare__range",type:"range",min:"0",max:"100","aria-label":"Comparison position"},null,512),[[se,b.value,void 0,{number:!0}]])],8,In))}}),Jn=ie(qn,[["__scopeId","data-v-0ee1b3e3"]]),Kn={class:"card block"},Hn={class:"head"},Wn={class:"grid"},Gn={class:"field"},Qn={class:"field"},Yn={value:"project"},Zn={class:"field wide"},Xn={class:"grid"},eo={class:"field"},to=["value"],so={class:"field"},lo=["value"],ao={class:"grid"},no={class:"muted"},oo=["onUpdate:modelValue"],io={key:0,class:"notice notice--error"},uo={key:1,class:"muted small"},ro={class:"preview"},co={class:"head"},vo={class:"previewbar"},po={class:"field inline"},mo={key:0,class:"muted"},fo={key:2,class:"muted small"},go={key:2,class:"notice notice--error"},ho={class:"actions"},bo=["disabled"],Ue="mediatoolz-ui.sharpen-sample",yo=oe({__name:"SharpenEditor",props:{options:{},cwd:{},initial:{},copyOf:{}},emits:["saved","cancel"],setup(n,{emit:b}){const i=n,f=b,v=he(Ee,Re),N=["radius","flat","jagged","threshold"];function y(){return i.initial??i.copyOf}function z(k){const l=y()?.fields?.[k];return l===void 0?"":String(l)}const U=T(()=>i.initial!==void 0),I=c(i.initial?i.initial.name:i.copyOf?.name??""),D=c(i.initial&&i.initial.scope!=="built-in"?i.initial.scope:"project"),B=c(y()?.description??""),S=c(String(y()?.fields?.for??"")),h=c(String(y()?.fields?.amount??"")),m=c({radius:z("radius"),flat:z("flat"),jagged:z("jagged"),threshold:z("threshold")}),r=c(""),_=c("800"),$=c(!1),F=c(),J=c(""),K=c(""),M=c(!1),te=c("");let x;const C=T(()=>Object.fromEntries(i.options.sharpen.fine.map(k=>[k.key,k.range])));function A(){const k={};S.value&&(k.for=S.value),h.value&&(k.amount=h.value);for(const l of N)m.value[l].trim()!==""&&(k[l]=Number(m.value[l]));return k}function P(k){const l=new URLSearchParams({path:r.value,width:_.value||"800"});if(k){l.set("sharpen","1");for(const[u,H]of Object.entries(A()))l.set(u,String(H))}return`/api/image-batch/sharpen-preview?${l.toString()}&v=${te.value}`}const le=T(()=>r.value?P(!1):""),re=T(()=>r.value?P(!0):"");async function ue(){try{const k=await Y.post("/api/image-batch/sharpen-preset/params",{fields:A()});F.value=k.params,J.value=""}catch(k){F.value=void 0,J.value=k instanceof Error?k.message:String(k)}te.value=JSON.stringify(A())+_.value}function ee(){clearTimeout(x),x=setTimeout(()=>{ue()},300)}ye([S,h,m,_],ee,{deep:!0});function de(k){r.value=k[0]??"",$.value=!1;try{localStorage.setItem(Ue,r.value)}catch{r.value=k[0]??""}}async function X(k=!1){M.value=!0,K.value="";try{await Y.post("/api/image-batch/sharpen-preset/save",{name:I.value,scope:D.value,description:B.value,fields:A(),overwrite:k,...i.initial?{originalName:i.initial.name,originalScope:i.initial.scope}:{}}),f("saved",I.value)}catch(l){if(l instanceof Le&&l.status===409){if(window.confirm(`${l.message}. Replace it?`)){await X(!0);return}}else K.value=l instanceof Error?l.message:String(l)}finally{M.value=!1}}return pe(()=>{try{r.value=localStorage.getItem(Ue)??""}catch{r.value=""}ue()}),Ve(()=>clearTimeout(x)),(k,l)=>(t(),s("section",Kn,[e("div",Hn,[e("h2",null,a(U.value?"Edit preset":n.copyOf?"Copy of a built-in preset":"New sharpening preset"),1),e("button",{class:"btn btn--ghost",onClick:l[0]||(l[0]=u=>f("cancel"))},[q(Z,{name:"close",size:16}),l[11]||(l[11]=E(" Close ",-1))])]),e("div",Wn,[e("label",Gn,[l[12]||(l[12]=e("span",null,"Name",-1)),R(e("input",{"onUpdate:modelValue":l[1]||(l[1]=u=>I.value=u),class:"input",spellcheck:"false",placeholder:"web-crisp"},null,512),[[se,I.value]])]),e("label",Qn,[l[14]||(l[14]=e("span",null,"Where to keep it",-1)),R(e("select",{"onUpdate:modelValue":l[2]||(l[2]=u=>D.value=u),class:"select"},[e("option",Yn,"This project ("+a(Q(v).name)+")",1),l[13]||(l[13]=e("option",{value:"global"},"All projects (your home folder)",-1))],512),[[ve,D.value]])]),e("label",Zn,[l[15]||(l[15]=e("span",null,"Note (optional)",-1)),R(e("input",{"onUpdate:modelValue":l[3]||(l[3]=u=>B.value=u),class:"input",placeholder:"What it is for"},null,512),[[se,B.value]])])]),e("div",Xn,[e("label",eo,[l[17]||(l[17]=e("span",null,"Sharpen for",-1)),R(e("select",{"onUpdate:modelValue":l[4]||(l[4]=u=>S.value=u),class:"select"},[l[16]||(l[16]=e("option",{value:""},"Default (screen)",-1)),(t(!0),s(V,null,L(n.options.sharpen.targets,u=>(t(),s("option",{key:u,value:u},a(u),9,to))),128))],512),[[ve,S.value]])]),e("label",so,[l[19]||(l[19]=e("span",null,"Amount",-1)),R(e("select",{"onUpdate:modelValue":l[5]||(l[5]=u=>h.value=u),class:"select"},[l[18]||(l[18]=e("option",{value:""},"Default (standard)",-1)),(t(!0),s(V,null,L(n.options.sharpen.amounts,u=>(t(),s("option",{key:u,value:u},a(u),9,lo))),128))],512),[[ve,h.value]])])]),e("div",ao,[(t(),s(V,null,L(N,u=>e("label",{key:u,class:"field"},[e("span",null,[E(a(u)+" ",1),e("small",no,"("+a(C.value[u]?.[0])+"–"+a(C.value[u]?.[1])+")",1)]),R(e("input",{"onUpdate:modelValue":H=>m.value[u]=H,class:"input",inputmode:"decimal",placeholder:"value from the table"},null,8,oo),[[se,m.value[u]]])])),64))]),J.value?(t(),s("p",io,a(J.value),1)):F.value?(t(),s("p",uo,[E(" sharp receives: sigma "+a(F.value.sigma)+", flat "+a(F.value.m1)+", jagged "+a(F.value.m2),1),F.value.x1!==void 0?(t(),s(V,{key:0},[E(", threshold "+a(F.value.x1),1)],64)):d("",!0)])):d("",!0),e("div",ro,[e("div",co,[l[21]||(l[21]=e("h3",null,"Preview",-1)),e("div",vo,[e("label",po,[l[20]||(l[20]=e("span",null,"Width",-1)),R(e("input",{"onUpdate:modelValue":l[6]||(l[6]=u=>_.value=u),class:"input narrow",inputmode:"numeric","aria-label":"Preview width"},null,512),[[se,_.value]])]),e("button",{class:"btn btn--small",onClick:l[7]||(l[7]=u=>$.value=!0)},[q(Z,{name:"image",size:15}),E(" "+a(r.value?"Change image":"Choose an image"),1)])])]),r.value?J.value?d("",!0):(t(),ae(Jn,{key:r.value,before:le.value,after:re.value,"before-label":"Without sharpening","after-label":"Sharpened"},null,8,["before","after"])):(t(),s("p",mo," Pick any photo to see the preset applied at the output size. Drag the slider to compare. ")),r.value?(t(),s("p",fo,a(r.value)+" — resized to "+a(_.value||800)+" px wide, then sharpened, as it would be at the output size. ",1)):d("",!0)]),K.value?(t(),s("p",go,a(K.value),1)):d("",!0),e("div",ho,[e("button",{class:"btn btn--primary",disabled:M.value||I.value.trim()==="",onClick:l[8]||(l[8]=u=>X(!1))},[q(Z,{name:"save",size:15}),l[22]||(l[22]=E(" Save preset ",-1))],8,bo),e("button",{class:"btn",onClick:l[9]||(l[9]=u=>f("cancel"))},"Cancel")]),$.value?(t(),ae(xe,{key:3,start:r.value?r.value.replace(/[\\/][^\\/]*$/,""):n.cwd,mode:"image",onClose:l[10]||(l[10]=u=>$.value=!1),onConfirm:de},null,8,["start"])):d("",!0)]))}}),ko=ie(yo,[["__scopeId","data-v-6a65364c"]]),$o={class:"view"},_o={key:1,class:"card block"},wo={class:"head"},Co={key:0,class:"notice"},So={key:1,class:"notice notice--error"},Vo={class:"list"},xo={class:"item__main"},Eo={class:"item__title"},zo={key:0,class:"item__error"},Ao={key:0,class:"muted"},No={class:"item__facts"},Bo={key:0,class:"muted"},Oo={class:"item__actions"},jo=["onClick"],To=["onClick"],Uo=["onClick"],Io=oe({__name:"SharpenView",props:{options:{},cwd:{}},setup(n){const b=c([]),i=c(""),f=c(""),v=c("list"),N=c(),y=c();async function z(){try{b.value=await Y.get("/api/image-batch/sharpen-presets")}catch(m){i.value=m instanceof Error?m.message:String(m)}}function U(){N.value=void 0,y.value=void 0,v.value="edit"}function I(m){N.value=m,y.value=void 0,v.value="edit"}function D(m){N.value=void 0,y.value=m,v.value="edit"}async function B(m){if(window.confirm(`Delete the preset “${m.name}”? A .bak copy is kept next to it.`))try{await Y.post("/api/image-batch/sharpen-preset/delete",{name:m.name,scope:m.scope}),f.value=`Deleted ${m.name}.`,await z()}catch(r){i.value=r instanceof Error?r.message:String(r)}}async function S(m){v.value="list",f.value=`Saved ${m}.`,await z()}function h(m){return Object.entries(m.fields??{}).map(([r,_])=>`${r} ${_}`).join(" · ")}return pe(z),(m,r)=>(t(),s("div",$o,[v.value==="edit"?(t(),ae(ko,{key:0,options:n.options,cwd:n.cwd,initial:N.value,"copy-of":y.value,onSaved:S,onCancel:r[0]||(r[0]=_=>v.value="list")},null,8,["options","cwd","initial","copy-of"])):(t(),s("section",_o,[e("div",wo,[r[2]||(r[2]=e("div",null,[e("h2",null,"Sharpening presets"),e("p",{class:"muted"}," Save a set of sharpening values once and use it by name in any config or run. ")],-1)),e("button",{class:"btn btn--primary",onClick:U},[q(Z,{name:"plus",size:16}),r[1]||(r[1]=E(" New preset ",-1))])]),f.value?(t(),s("p",Co,a(f.value),1)):d("",!0),i.value?(t(),s("p",So,a(i.value),1)):d("",!0),e("ul",Vo,[(t(!0),s(V,null,L(b.value,_=>(t(),s("li",{key:_.scope+_.name,class:"item"},[e("div",xo,[e("div",Eo,[e("strong",null,a(_.name),1),e("span",{class:ne(["tag",{"tag--builtin":_.scope==="built-in"}])},a(_.scope),3)]),_.error?(t(),s("p",zo,a(_.error),1)):(t(),s(V,{key:1},[_.description?(t(),s("p",Ao,a(_.description),1)):d("",!0),e("p",No,[E(a(h(_))+" ",1),_.params?(t(),s("span",Bo," — sigma "+a(_.params.sigma)+", flat "+a(_.params.m1)+", jagged "+a(_.params.m2),1)):d("",!0)])],64))]),e("div",Oo,[_.scope!=="built-in"&&!_.error?(t(),s("button",{key:0,class:"btn btn--small",onClick:$=>I(_)}," Edit ",8,jo)):d("",!0),_.error?d("",!0):(t(),s("button",{key:1,class:"btn btn--small",onClick:$=>D(_)},a(_.scope==="built-in"?"Copy to my presets":"Duplicate"),9,To)),_.scope!=="built-in"?(t(),s("button",{key:2,class:"btn btn--small btn--danger",onClick:$=>B(_)}," Delete ",8,Uo)):d("",!0)])]))),128))])]))]))}}),Do=ie(Io,[["__scopeId","data-v-519b182c"]]),Mo={class:"page"},Lo={class:"page__head"},Ro={class:"muted"},Fo=["title"],Po={key:0,class:"notice notice--error"},Ie="mediatoolz-ui.image-batch-tab",qo=oe({__name:"ImageBatchPage",props:{module:{},status:{}},setup(n){const b=n;function i(){try{const z=localStorage.getItem(Ie);if(z==="convert"||z==="configs"||z==="sharpen"||z==="backups")return z}catch{return"convert"}return"convert"}const f=c(i()),v=c(),N=c("");function y(z){f.value=z;try{localStorage.setItem(Ie,z)}catch{f.value=z}}return Ae(ke,y),Ae(Ee,T(()=>Pt(b.status?.cwd??"",b.status?.home??"")).value),pe(async()=>{try{v.value=await Y.get("/api/image-batch/options")}catch(z){N.value=z instanceof Error?z.message:String(z)}}),(z,U)=>(t(),s("div",Mo,[e("header",Lo,[e("h1",null,a(n.module.title),1),e("p",Ro,a(n.module.description),1),n.status?.cwd?(t(),s("p",{key:0,class:"context",title:`Everything marked “This project” lives in ${n.status.cwd}`},[q(Z,{name:"folder",size:15}),U[0]||(U[0]=e("span",{class:"muted"},"Project folder",-1)),e("code",null,a(n.status.cwd),1)],8,Fo)):d("",!0)]),q(ge,{"model-value":f.value,label:"Image Batch sections",options:[{value:"convert",label:"Convert"},{value:"configs",label:"Configs"},{value:"sharpen",label:"Sharpening"},{value:"backups",label:"Backups"}],"onUpdate:modelValue":y},null,8,["model-value"]),N.value?(t(),s("p",Po,a(N.value),1)):v.value?(t(),s(V,{key:1},[f.value==="convert"?(t(),ae(Un,{key:0,cwd:b.status?.cwd??"",options:v.value},null,8,["cwd","options"])):f.value==="configs"?(t(),ae(da,{key:1,options:v.value},null,8,["options"])):f.value==="sharpen"?(t(),ae(Do,{key:2,options:v.value,cwd:b.status?.cwd??""},null,8,["options","cwd"])):(t(),ae(Ft,{key:3,cwd:b.status?.cwd??""},null,8,["cwd"]))],64)):d("",!0)]))}}),Ho=ie(qo,[["__scopeId","data-v-91e8f81e"]]);export{Ho as default};
@@ -0,0 +1 @@
1
+ .seg[data-v-56d7f4ee]{display:inline-flex;align-self:flex-start;flex-wrap:wrap;padding:3px;border:1px solid var(--border);border-radius:9px;background:var(--surface-2)}.seg__btn[data-v-56d7f4ee]{padding:5px 12px;border:0;border-radius:6px;background:transparent;color:var(--muted);cursor:pointer}.seg__btn[data-v-56d7f4ee]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.seg__btn--on[data-v-56d7f4ee]{background:var(--surface);color:var(--text);font-weight:600;box-shadow:var(--shadow)}.view[data-v-1e694fea]{display:flex;flex-direction:column;gap:16px}.block[data-v-1e694fea]{display:flex;flex-direction:column;gap:12px;padding:18px}.head[data-v-1e694fea]{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:12px}.small[data-v-1e694fea]{font-size:12px}.list[data-v-1e694fea]{display:flex;flex-direction:column;gap:8px;margin:0;padding:0;list-style:none}.item[data-v-1e694fea]{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px;padding:12px;border:1px solid var(--border);border-radius:9px}.item__main[data-v-1e694fea]{display:flex;flex-direction:column;gap:2px;flex:1;min-width:260px}.item__path[data-v-1e694fea]{color:var(--muted);font-size:11px;word-break:break-all}.item__actions[data-v-1e694fea]{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.custom[data-v-1e694fea]{display:flex;flex-direction:column;gap:8px;padding-top:12px;border-top:1px solid var(--border)}.custom h3[data-v-1e694fea]{font-size:12px;font-weight:600;letter-spacing:.02em;color:var(--muted);text-transform:uppercase}.pathrow[data-v-1e694fea]{display:flex;flex-wrap:wrap;gap:8px}.pathrow .input[data-v-1e694fea]{flex:1;min-width:220px;font-family:ui-monospace,Cascadia Code,SF Mono,Menlo,Consolas,monospace;font-size:12px}.actions[data-v-1e694fea]{display:flex;flex-wrap:wrap;align-items:center;gap:12px}.chips[data-v-1e694fea]{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.chip[data-v-1e694fea]{padding:2px 10px;border-radius:999px;background:var(--surface-2);font-size:12px;white-space:nowrap}.chip--good[data-v-1e694fea]{background:color-mix(in srgb,var(--ok) 18%,transparent);color:var(--ok)}.chip--warn[data-v-1e694fea]{background:var(--warn-soft);color:var(--warn)}.chip--bad[data-v-1e694fea]{background:var(--danger-soft);color:var(--danger)}.rows[data-v-1e694fea]{display:flex;flex-direction:column;gap:8px;margin:0;padding:0;list-style:none}.row[data-v-1e694fea]{display:flex;align-items:center;gap:12px;padding:8px;border:1px solid var(--border);border-radius:9px}.row__thumb[data-v-1e694fea]{flex:none;width:64px;height:64px;border-radius:6px;background:var(--surface-2);object-fit:cover}.row__text[data-v-1e694fea]{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.row__name[data-v-1e694fea]{font-family:ui-monospace,Cascadia Code,SF Mono,Menlo,Consolas,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row__message[data-v-1e694fea]{color:var(--danger)}.sharpen[data-v-1d132e3d]{display:flex;flex-direction:column;gap:12px}.grid[data-v-1d132e3d]{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px}.hint[data-v-1d132e3d]{align-self:end;font-size:12px}.link[data-v-1d132e3d]{align-self:flex-start;padding:0;border:0;background:transparent;color:var(--accent);cursor:pointer}.link[data-v-1d132e3d]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.recipe[data-v-6d67983e]{display:flex;flex-direction:column;gap:16px}.group[data-v-6d67983e]{display:flex;flex-direction:column;gap:10px}.group h4[data-v-6d67983e]{font-size:12px;font-weight:600;letter-spacing:.02em;color:var(--muted);text-transform:uppercase;margin:0}.grid[data-v-6d67983e]{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:14px}.small[data-v-6d67983e]{font-size:12px}.input--bad[data-v-6d67983e]{border-color:var(--danger)}.pills[data-v-6d67983e]{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.pill[data-v-6d67983e]{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border:1px solid var(--border);border-radius:999px;background:var(--surface);font-size:12px;cursor:pointer}.pill input[data-v-6d67983e]{display:none}.pill--on[data-v-6d67983e]{border-color:var(--accent);background:var(--accent-soft);color:var(--accent);font-weight:600}.pill--button[data-v-6d67983e]{font-family:ui-monospace,Cascadia Code,SF Mono,Menlo,Consolas,monospace}.pill--button[data-v-6d67983e]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.link[data-v-6d67983e]{align-self:flex-start;padding:0;border:0;background:transparent;color:var(--accent);cursor:pointer}.link[data-v-6d67983e]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.block[data-v-5992429e]{display:flex;flex-direction:column;gap:16px;padding:18px}.head[data-v-5992429e]{display:flex;align-items:center;justify-content:space-between;gap:12px}.grid[data-v-5992429e]{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:14px}.small[data-v-5992429e]{font-size:12px}.recipes[data-v-5992429e]{display:flex;flex-direction:column;gap:8px}.panel[data-v-5992429e]{border:1px solid var(--border);border-radius:9px}.panel--open[data-v-5992429e]{border-color:var(--accent)}.panel__head[data-v-5992429e]{display:flex;align-items:center;gap:8px;padding:4px 8px 4px 12px}.panel__title[data-v-5992429e]{display:flex;flex-direction:column;gap:0;flex:1;min-width:0;padding:8px 0;border:0;background:transparent;text-align:left;cursor:pointer}.panel__title[data-v-5992429e]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.panel__title .small[data-v-5992429e]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.panel__actions[data-v-5992429e]{display:flex;flex-wrap:wrap;align-items:center;gap:2px;flex:none}.panel__body[data-v-5992429e]{padding:12px 16px 16px;border-top:1px solid var(--border)}.panel summary[data-v-5992429e]{padding:12px;cursor:pointer;font-weight:600}details.panel>.panel__body[data-v-5992429e]{border-top:1px solid var(--border)}.json[data-v-5992429e]{min-height:420px;padding:12px;border:1px solid var(--border);border-radius:9px;background:var(--surface-2);font-family:ui-monospace,Cascadia Code,SF Mono,Menlo,Consolas,monospace;font-size:12px;resize:vertical;tab-size:2}.json[data-v-5992429e]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.check-panel[data-v-5992429e]{padding:10px 14px;border-radius:9px}.check-panel--ok[data-v-5992429e]{background:color-mix(in srgb,var(--ok) 14%,transparent)}.check-panel--bad[data-v-5992429e]{background:var(--danger-soft);color:var(--danger)}.lines[data-v-5992429e]{margin:8px 0 0;font-size:12px;white-space:pre-wrap}.actions[data-v-5992429e]{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.view[data-v-5b011a56]{display:flex;flex-direction:column;gap:16px}.block[data-v-5b011a56]{display:flex;flex-direction:column;gap:12px;padding:18px}.head[data-v-5b011a56]{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:12px}.list[data-v-5b011a56]{display:flex;flex-direction:column;gap:8px;margin:0;padding:0;list-style:none}.item[data-v-5b011a56]{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px;padding:12px;border:1px solid var(--border);border-radius:9px}.item__main[data-v-5b011a56]{display:flex;flex-direction:column;gap:2px;flex:1;min-width:260px}.item__title[data-v-5b011a56]{display:flex;align-items:center;gap:8px}.item__path[data-v-5b011a56]{color:var(--muted);font-size:11px;word-break:break-all}.item__error[data-v-5b011a56]{color:var(--danger);font-size:12px}.item__actions[data-v-5b011a56]{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.tag[data-v-5b011a56]{padding:1px 8px;border-radius:999px;background:var(--surface-2);color:var(--muted);font-size:11px}.block[data-v-56eb5236]{display:flex;flex-direction:column;gap:12px;padding:18px}.head[data-v-56eb5236]{display:flex;align-items:center;justify-content:space-between;gap:12px}.small[data-v-56eb5236]{font-size:12px}.chips[data-v-56eb5236]{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.chip[data-v-56eb5236]{padding:2px 10px;border-radius:999px;background:var(--surface-2);font-size:12px;white-space:nowrap}.chip--good[data-v-56eb5236]{background:color-mix(in srgb,var(--ok) 18%,transparent);color:var(--ok)}.chip--bad[data-v-56eb5236]{background:var(--danger-soft);color:var(--danger)}.errors[data-v-56eb5236]{margin:0;padding:10px 14px 10px 28px;border-radius:10px;background:var(--danger-soft);color:var(--danger)}.rows[data-v-56eb5236]{display:flex;flex-direction:column;gap:8px;margin:0;padding:0;list-style:none}.row[data-v-56eb5236]{display:flex;align-items:center;gap:12px;padding:8px;border:1px solid var(--border);border-radius:9px}.row__thumb[data-v-56eb5236]{flex:none;width:64px;height:64px;border-radius:6px;background:var(--surface-2);object-fit:cover}.row__thumb--empty[data-v-56eb5236]{display:grid;place-items:center;font-size:11px}.row__text[data-v-56eb5236]{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.row__name[data-v-56eb5236]{font-family:ui-monospace,Cascadia Code,SF Mono,Menlo,Consolas,monospace;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row__message[data-v-56eb5236]{color:var(--danger)}.row__sizes[data-v-56eb5236]{display:flex;align-items:baseline;gap:6px;flex:none;font-size:12px}.row__delta[data-v-56eb5236]{color:var(--muted)}.row__delta--down[data-v-56eb5236]{color:var(--ok)}@media(max-width:760px){.row[data-v-56eb5236]{flex-wrap:wrap}.row__text[data-v-56eb5236]{flex-basis:60%}}.link[data-v-56eb5236]{padding:0;border:0;background:transparent;color:var(--accent);cursor:pointer;text-decoration:underline}.more[data-v-56eb5236]{display:flex;flex-wrap:wrap;align-items:center;gap:8px;justify-content:center}.view[data-v-017cd3fb]{display:flex;flex-direction:column;gap:16px}.block[data-v-017cd3fb]{display:flex;flex-direction:column;gap:14px;padding:18px}.head[data-v-017cd3fb]{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px}.group[data-v-017cd3fb]{display:flex;flex-direction:column;gap:10px}.group h3[data-v-017cd3fb]{font-size:12px;font-weight:600;letter-spacing:.02em;color:var(--muted);text-transform:uppercase}.grid[data-v-017cd3fb]{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:14px}.narrow[data-v-017cd3fb]{max-width:320px}.small[data-v-017cd3fb]{font-size:12px}.pathrow[data-v-017cd3fb]{display:flex;gap:8px}.pathrow .input[data-v-017cd3fb]{flex:1;font-family:ui-monospace,Cascadia Code,SF Mono,Menlo,Consolas,monospace;font-size:12px}.pills[data-v-017cd3fb]{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.pill[data-v-017cd3fb]{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border:1px solid var(--border);border-radius:999px;background:var(--surface);font-size:12px;cursor:pointer}.pill input[data-v-017cd3fb]{display:none}.pill--on[data-v-017cd3fb]{border-color:var(--accent);background:var(--accent-soft);color:var(--accent);font-weight:600}.pill--button[data-v-017cd3fb]{font-family:ui-monospace,Cascadia Code,SF Mono,Menlo,Consolas,monospace}.pill--button[data-v-017cd3fb]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.runbar[data-v-017cd3fb]{display:flex;flex-wrap:wrap;align-items:center;gap:14px}.link[data-v-017cd3fb]{align-self:flex-start;padding:0;border:0;background:transparent;color:var(--accent);cursor:pointer}.link[data-v-017cd3fb]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.progress[data-v-017cd3fb]{height:8px;overflow:hidden;border-radius:999px;background:var(--surface-2)}.progress__bar[data-v-017cd3fb]{height:100%;background:var(--accent);transition:width .15s}.compare[data-v-0ee1b3e3]{display:flex;flex-direction:column;gap:8px}.compare__scroll[data-v-0ee1b3e3]{overflow:auto;border-radius:9px;background:var(--surface-2);max-height:70vh}.compare__stage[data-v-0ee1b3e3]{position:relative;width:fit-content;margin:0 auto;line-height:0}.compare__img[data-v-0ee1b3e3]{display:block;max-width:none}.compare__img--top[data-v-0ee1b3e3]{position:absolute;inset:0}.compare__line[data-v-0ee1b3e3]{position:absolute;top:0;bottom:0;width:2px;background:var(--accent);pointer-events:none}.compare__tag[data-v-0ee1b3e3]{position:absolute;top:8px;padding:2px 8px;border-radius:6px;background:#0009;color:#fff;font-size:11px;line-height:1.4}.compare__tag--left[data-v-0ee1b3e3]{left:8px}.compare__tag--right[data-v-0ee1b3e3]{right:8px}.compare__range[data-v-0ee1b3e3]{width:100%}.block[data-v-6a65364c]{display:flex;flex-direction:column;gap:16px;padding:18px}.head[data-v-6a65364c]{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px}.grid[data-v-6a65364c]{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:14px}.wide[data-v-6a65364c]{grid-column:1/-1}.small[data-v-6a65364c]{font-size:12px}.preview[data-v-6a65364c]{display:flex;flex-direction:column;gap:12px}.previewbar[data-v-6a65364c]{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.inline[data-v-6a65364c]{flex-direction:row;align-items:center}.narrow[data-v-6a65364c]{width:90px}.actions[data-v-6a65364c]{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.view[data-v-519b182c]{display:flex;flex-direction:column;gap:16px}.block[data-v-519b182c]{display:flex;flex-direction:column;gap:12px;padding:18px}.head[data-v-519b182c]{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:12px}.list[data-v-519b182c]{display:flex;flex-direction:column;gap:8px;margin:0;padding:0;list-style:none}.item[data-v-519b182c]{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px;padding:12px;border:1px solid var(--border);border-radius:9px}.item__main[data-v-519b182c]{display:flex;flex-direction:column;gap:2px;flex:1;min-width:260px}.item__title[data-v-519b182c]{display:flex;align-items:center;gap:8px}.item__facts[data-v-519b182c]{font-family:ui-monospace,Cascadia Code,SF Mono,Menlo,Consolas,monospace;font-size:12px}.item__error[data-v-519b182c]{color:var(--danger);font-size:12px}.item__actions[data-v-519b182c]{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.tag[data-v-519b182c]{padding:1px 8px;border-radius:999px;background:var(--surface-2);color:var(--muted);font-size:11px}.tag--builtin[data-v-519b182c]{background:var(--accent-soft);color:var(--accent)}.page[data-v-91e8f81e]{display:flex;flex-direction:column;gap:16px;padding:28px 28px 60px}@media(max-width:760px){.page[data-v-91e8f81e]{padding:18px 14px 40px}}.page__head p[data-v-91e8f81e]{margin-top:4px}.context[data-v-91e8f81e]{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:8px;font-size:12px}.context code[data-v-91e8f81e]{padding:2px 8px;border-radius:6px;background:var(--surface-2);word-break:break-all}
@@ -0,0 +1 @@
1
+ .canvas[data-v-af1c304e]{display:block;width:100%;height:100%;object-fit:cover;background:var(--surface-2)}.canvas--bad[data-v-af1c304e]{display:grid;place-items:center;color:var(--danger)}.item[data-v-ede0ed45]{display:flex;flex-direction:column;gap:10px;padding:10px;min-width:0}.item__open[data-v-ede0ed45]{display:flex;flex-direction:column;gap:10px;padding:0;border:0;background:transparent;text-align:left;cursor:zoom-in}.item__open[data-v-ede0ed45]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.shots[data-v-ede0ed45]{display:block;overflow:hidden;border-radius:8px;background:var(--surface-2)}.shot[data-v-ede0ed45]{display:block;width:100%;height:100%;object-fit:cover;transition:transform .15s}.item__open:hover .shot[data-v-ede0ed45]{transform:scale(1.02)}.strip[data-v-ede0ed45]{display:flex;overflow:hidden;border-radius:8px}.strip__cell[data-v-ede0ed45]{position:relative;flex:1}.strip__label[data-v-ede0ed45]{position:absolute;left:4px;bottom:3px;padding:0 5px;border-radius:4px;background:#0000008c;color:#fff;font-size:10px}.preview[data-v-ede0ed45]{width:100%;border-radius:8px}.meta[data-v-ede0ed45]{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.meta__name[data-v-ede0ed45]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hashes[data-v-ede0ed45]{display:flex;flex-direction:column;gap:2px;margin:0;padding:0;list-style:none}.hashes li[data-v-ede0ed45]{display:flex;align-items:center;gap:8px}.hashes__key[data-v-ede0ed45]{display:flex;align-items:center;gap:6px;flex:none;width:84px;color:var(--muted);font-size:12px}.hashes__value[data-v-ede0ed45]{flex:1;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.swatch[data-v-ede0ed45]{display:inline-block;width:12px;height:12px;border:1px solid var(--border);border-radius:4px}.counter[data-v-4cbae639]{font-size:12px}.scroll[data-v-4cbae639]{display:flex;flex-direction:column;gap:16px;padding:16px;min-height:0;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--border) transparent}.stage[data-v-4cbae639]{display:flex;align-items:center;justify-content:center;padding:12px;border-radius:9px;background:repeating-conic-gradient(var(--surface-2) 0% 25%,transparent 0% 50%) 50%/20px 20px}.stage__img[data-v-4cbae639]{display:block;max-width:100%;max-height:56vh;border-radius:6px;object-fit:contain}.compare[data-v-4cbae639]{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px}.compare__item[data-v-4cbae639]{display:flex;flex-direction:column;gap:6px;margin:0}.compare__item figcaption[data-v-4cbae639]{font-size:12px;font-weight:600;letter-spacing:.02em;color:var(--muted);text-transform:uppercase}.compare__frame[data-v-4cbae639]{overflow:hidden;border-radius:9px;background:var(--surface-2)}.compare__preview[data-v-4cbae639]{display:block;width:100%;height:100%;object-fit:cover}.facts[data-v-4cbae639]{display:flex;flex-direction:column;gap:4px;margin:0}.facts__row[data-v-4cbae639]{display:flex;align-items:center;gap:12px;min-height:32px}.facts dt[data-v-4cbae639]{display:flex;align-items:center;gap:6px;flex:none;width:96px;color:var(--muted);font-size:12px}.facts dd[data-v-4cbae639]{flex:1;min-width:0;margin:0}.facts__mono[data-v-4cbae639]{font-family:ui-monospace,Cascadia Code,SF Mono,Menlo,Consolas,monospace;font-size:12px;word-break:break-all}.swatch[data-v-4cbae639]{display:inline-block;width:12px;height:12px;border:1px solid var(--border);border-radius:4px}.page[data-v-8acfc1cb]{display:flex;flex-direction:column;gap:16px;padding:28px 28px 60px}@media(max-width:760px){.page[data-v-8acfc1cb]{padding:18px 14px 40px}}.page__head p[data-v-8acfc1cb]{margin-top:4px}.block[data-v-8acfc1cb]{display:flex;flex-direction:column;gap:14px;padding:18px}.block__head[data-v-8acfc1cb]{display:flex;align-items:center;justify-content:space-between;gap:12px}.types[data-v-8acfc1cb]{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:10px}.type[data-v-8acfc1cb]{display:grid;grid-template-columns:auto 1fr;column-gap:10px;row-gap:2px;padding:12px;border:1px solid var(--border);border-radius:10px;cursor:pointer}.type--on[data-v-8acfc1cb]{border-color:var(--accent);background:var(--accent-soft)}.type input[data-v-8acfc1cb]{grid-row:1/3;margin-top:3px}.type__title[data-v-8acfc1cb]{font-weight:600}.type__text[data-v-8acfc1cb]{font-size:12px}.grid[data-v-8acfc1cb]{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:14px}.runbar[data-v-8acfc1cb]{display:flex;align-items:center;gap:14px}.progress[data-v-8acfc1cb]{height:8px;overflow:hidden;border-radius:999px;background:var(--surface-2)}.progress__bar[data-v-8acfc1cb]{height:100%;background:var(--accent);transition:width .15s}.errors[data-v-8acfc1cb]{margin:0;padding:10px 14px 10px 28px;border-radius:10px;background:var(--danger-soft);color:var(--danger)}.tabs[data-v-8acfc1cb]{display:flex;gap:4px;border-bottom:1px solid var(--border)}.tab[data-v-8acfc1cb]{padding:8px 14px;border:0;border-bottom:2px solid transparent;background:transparent;color:var(--muted);cursor:pointer}.tab[data-v-8acfc1cb]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.tab--on[data-v-8acfc1cb]{border-bottom-color:var(--accent);color:var(--text);font-weight:600}.gallery[data-v-8acfc1cb]{width:100%}.more[data-v-8acfc1cb]{display:flex;flex-wrap:wrap;align-items:center;gap:8px;justify-content:center}.outbar[data-v-8acfc1cb]{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.outbar__path[data-v-8acfc1cb]{flex:1;min-width:220px;height:28px;font-family:ui-monospace,Cascadia Code,SF Mono,Menlo,Consolas,monospace;font-size:12px}.output[data-v-8acfc1cb]{max-height:420px;margin:0;padding:14px;border-radius:10px;background:var(--surface-2);font-size:12px;white-space:pre-wrap;word-break:break-all;overflow:auto}