@wasao/kagemusha 0.1.1 → 0.3.4

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 (155) hide show
  1. package/README.md +168 -79
  2. package/dist/commands/add.d.ts +6 -0
  3. package/dist/commands/add.d.ts.map +1 -0
  4. package/dist/commands/add.js +26 -0
  5. package/dist/commands/add.js.map +1 -0
  6. package/dist/commands/capture.d.ts +3 -2
  7. package/dist/commands/capture.d.ts.map +1 -1
  8. package/dist/commands/capture.js +256 -20
  9. package/dist/commands/capture.js.map +1 -1
  10. package/dist/commands/discover.d.ts +2 -0
  11. package/dist/commands/discover.d.ts.map +1 -0
  12. package/dist/commands/discover.js +62 -0
  13. package/dist/commands/discover.js.map +1 -0
  14. package/dist/commands/edit.d.ts +1 -1
  15. package/dist/commands/edit.d.ts.map +1 -1
  16. package/dist/commands/edit.js +82 -26
  17. package/dist/commands/edit.js.map +1 -1
  18. package/dist/commands/init.d.ts +1 -1
  19. package/dist/commands/init.d.ts.map +1 -1
  20. package/dist/commands/init.js +240 -105
  21. package/dist/commands/init.js.map +1 -1
  22. package/dist/commands/list.d.ts +2 -0
  23. package/dist/commands/list.d.ts.map +1 -0
  24. package/dist/commands/list.js +33 -0
  25. package/dist/commands/list.js.map +1 -0
  26. package/dist/commands/login.d.ts +6 -0
  27. package/dist/commands/login.d.ts.map +1 -0
  28. package/dist/commands/login.js +131 -0
  29. package/dist/commands/login.js.map +1 -0
  30. package/dist/commands/validate.js +1 -1
  31. package/dist/commands/validate.js.map +1 -1
  32. package/dist/editor/inject-script/annotations.d.ts +11 -0
  33. package/dist/editor/inject-script/annotations.d.ts.map +1 -0
  34. package/dist/editor/inject-script/annotations.js +409 -0
  35. package/dist/editor/inject-script/annotations.js.map +1 -0
  36. package/dist/editor/inject-script/bridge.d.ts +13 -0
  37. package/dist/editor/inject-script/bridge.d.ts.map +1 -0
  38. package/dist/editor/inject-script/bridge.js +33 -0
  39. package/dist/editor/inject-script/bridge.js.map +1 -0
  40. package/dist/editor/inject-script/crop.d.ts +9 -0
  41. package/dist/editor/inject-script/crop.d.ts.map +1 -0
  42. package/dist/editor/inject-script/crop.js +236 -0
  43. package/dist/editor/inject-script/crop.js.map +1 -0
  44. package/dist/editor/inject-script/dom.d.ts +7 -0
  45. package/dist/editor/inject-script/dom.d.ts.map +1 -0
  46. package/dist/editor/inject-script/dom.js +32 -0
  47. package/dist/editor/inject-script/dom.js.map +1 -0
  48. package/dist/editor/inject-script/index.d.ts +2 -0
  49. package/dist/editor/inject-script/index.d.ts.map +1 -0
  50. package/dist/editor/inject-script/index.js +56 -0
  51. package/dist/editor/inject-script/index.js.map +1 -0
  52. package/dist/editor/inject-script/record.d.ts +5 -0
  53. package/dist/editor/inject-script/record.d.ts.map +1 -0
  54. package/dist/editor/inject-script/record.js +398 -0
  55. package/dist/editor/inject-script/record.js.map +1 -0
  56. package/dist/editor/inject-script/selector.d.ts +6 -0
  57. package/dist/editor/inject-script/selector.d.ts.map +1 -0
  58. package/dist/editor/inject-script/selector.js +112 -0
  59. package/dist/editor/inject-script/selector.js.map +1 -0
  60. package/dist/editor/inject-script/state.d.ts +27 -0
  61. package/dist/editor/inject-script/state.d.ts.map +1 -0
  62. package/dist/editor/inject-script/state.js +26 -0
  63. package/dist/editor/inject-script/state.js.map +1 -0
  64. package/dist/editor/inject-script/svg.d.ts +7 -0
  65. package/dist/editor/inject-script/svg.d.ts.map +1 -0
  66. package/dist/editor/inject-script/svg.js +39 -0
  67. package/dist/editor/inject-script/svg.js.map +1 -0
  68. package/dist/editor/inject-script/toolbar.d.ts +14 -0
  69. package/dist/editor/inject-script/toolbar.d.ts.map +1 -0
  70. package/dist/editor/inject-script/toolbar.js +240 -0
  71. package/dist/editor/inject-script/toolbar.js.map +1 -0
  72. package/dist/editor/inject-script/types.d.ts +102 -0
  73. package/dist/editor/inject-script/types.d.ts.map +1 -0
  74. package/dist/editor/inject-script/types.js +5 -0
  75. package/dist/editor/inject-script/types.js.map +1 -0
  76. package/dist/editor/inject-script.js +1276 -353
  77. package/dist/index.js +34 -16
  78. package/dist/index.js.map +1 -1
  79. package/dist/lib/annotate.d.ts +2 -2
  80. package/dist/lib/annotate.d.ts.map +1 -1
  81. package/dist/lib/annotate.js +35 -43
  82. package/dist/lib/annotate.js.map +1 -1
  83. package/dist/lib/auth.d.ts +18 -0
  84. package/dist/lib/auth.d.ts.map +1 -0
  85. package/dist/lib/auth.js +45 -0
  86. package/dist/lib/auth.js.map +1 -0
  87. package/dist/lib/aws-error.d.ts +7 -0
  88. package/dist/lib/aws-error.d.ts.map +1 -0
  89. package/dist/lib/aws-error.js +74 -0
  90. package/dist/lib/aws-error.js.map +1 -0
  91. package/dist/lib/canonical.d.ts +54 -0
  92. package/dist/lib/canonical.d.ts.map +1 -0
  93. package/dist/lib/canonical.js +152 -0
  94. package/dist/lib/canonical.js.map +1 -0
  95. package/dist/lib/config.d.ts +2 -0
  96. package/dist/lib/config.d.ts.map +1 -1
  97. package/dist/lib/config.js +23 -13
  98. package/dist/lib/config.js.map +1 -1
  99. package/dist/lib/crawl.d.ts +1 -1
  100. package/dist/lib/crawl.d.ts.map +1 -1
  101. package/dist/lib/crawl.js +213 -20
  102. package/dist/lib/crawl.js.map +1 -1
  103. package/dist/lib/definition.d.ts +2 -0
  104. package/dist/lib/definition.d.ts.map +1 -0
  105. package/dist/lib/definition.js +6 -0
  106. package/dist/lib/definition.js.map +1 -0
  107. package/dist/lib/diff.d.ts +71 -0
  108. package/dist/lib/diff.d.ts.map +1 -0
  109. package/dist/lib/diff.js +40 -0
  110. package/dist/lib/diff.js.map +1 -0
  111. package/dist/lib/login-error.d.ts +10 -0
  112. package/dist/lib/login-error.d.ts.map +1 -0
  113. package/dist/lib/login-error.js +13 -0
  114. package/dist/lib/login-error.js.map +1 -0
  115. package/dist/lib/page-ready.d.ts +18 -0
  116. package/dist/lib/page-ready.d.ts.map +1 -0
  117. package/dist/lib/page-ready.js +19 -0
  118. package/dist/lib/page-ready.js.map +1 -0
  119. package/dist/lib/screenshot.d.ts +11 -2
  120. package/dist/lib/screenshot.d.ts.map +1 -1
  121. package/dist/lib/screenshot.js +73 -64
  122. package/dist/lib/screenshot.js.map +1 -1
  123. package/dist/lib/staging.d.ts +7 -0
  124. package/dist/lib/staging.d.ts.map +1 -0
  125. package/dist/lib/staging.js +21 -0
  126. package/dist/lib/staging.js.map +1 -0
  127. package/dist/types.d.ts +10 -23
  128. package/dist/types.d.ts.map +1 -1
  129. package/package.json +20 -3
  130. package/dist/commands/preview.d.ts +0 -6
  131. package/dist/commands/preview.d.ts.map +0 -1
  132. package/dist/commands/preview.js +0 -33
  133. package/dist/commands/preview.js.map +0 -1
  134. package/dist/commands/run.d.ts +0 -6
  135. package/dist/commands/run.d.ts.map +0 -1
  136. package/dist/commands/run.js +0 -39
  137. package/dist/commands/run.js.map +0 -1
  138. package/dist/editor/editor/editor.html +0 -313
  139. package/dist/editor/editor/inject.ts +0 -385
  140. package/dist/editor/editor.html +0 -338
  141. package/dist/editor/inject-script.cjs +0 -398
  142. package/dist/editor/inject-script.cjs.map +0 -1
  143. package/dist/editor/inject-script.d.cts +0 -2
  144. package/dist/editor/inject-script.d.cts.map +0 -1
  145. package/dist/editor/inject-script.d.ts +0 -2
  146. package/dist/editor/inject-script.d.ts.map +0 -1
  147. package/dist/editor/inject-script.js.map +0 -1
  148. package/dist/editor/inject.d.ts +0 -2
  149. package/dist/editor/inject.d.ts.map +0 -1
  150. package/dist/editor/inject.js +0 -385
  151. package/dist/editor/inject.js.map +0 -1
  152. package/dist/lib/upload.d.ts +0 -9
  153. package/dist/lib/upload.d.ts.map +0 -1
  154. package/dist/lib/upload.js +0 -43
  155. package/dist/lib/upload.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wasao/kagemusha",
3
- "version": "0.1.1",
3
+ "version": "0.3.4",
4
4
  "description": "Auto-update help center screenshots when your code changes. The shadow warrior for your documentation.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -12,15 +12,21 @@
12
12
  "files": [
13
13
  "dist"
14
14
  ],
15
+ "publishConfig": {
16
+ "registry": "https://registry.npmjs.org/",
17
+ "access": "public"
18
+ },
15
19
  "scripts": {
16
- "build": "tsc",
20
+ "build": "tsc && pnpm build:inject",
21
+ "build:inject": "esbuild src/editor/inject-script/index.ts --bundle --format=iife --outfile=dist/editor/inject-script.js --platform=browser --target=es2022",
22
+ "build:clean": "rm -rf dist && tsc && pnpm build:inject",
17
23
  "dev": "bun --watch src/index.ts",
18
24
  "typecheck": "tsc --noEmit",
19
25
  "lint": "bunx biome check .",
20
26
  "format": "bunx biome format --write .",
21
27
  "test": "bun test",
22
28
  "prepare": "husky",
23
- "prepublishOnly": "bun run build"
29
+ "prepublishOnly": "pnpm run build"
24
30
  },
25
31
  "keywords": [
26
32
  "screenshot",
@@ -47,7 +53,9 @@
47
53
  "commander": "^13.0.0",
48
54
  "glob": "^11.0.0",
49
55
  "inquirer": "^12.0.0",
56
+ "pixelmatch": "^7.0.0",
50
57
  "playwright-chromium": "^1.50.0",
58
+ "pngjs": "^7.0.0",
51
59
  "sharp": "^0.34.5",
52
60
  "yaml": "^2.7.0"
53
61
  },
@@ -56,8 +64,17 @@
56
64
  "@types/bun": "latest",
57
65
  "@types/inquirer": "^9.0.0",
58
66
  "@types/node": "^22.0.0",
67
+ "@types/pixelmatch": "^5.2.6",
68
+ "@types/pngjs": "^6.0.5",
59
69
  "@types/sharp": "^0.32.0",
70
+ "esbuild": "^0.28.0",
60
71
  "husky": "^9.1.7",
61
72
  "typescript": "^5.7.0"
73
+ },
74
+ "pnpm": {
75
+ "onlyBuiltDependencies": [
76
+ "playwright-chromium",
77
+ "sharp"
78
+ ]
62
79
  }
63
80
  }
@@ -1,6 +0,0 @@
1
- interface PreviewOptions {
2
- id?: string;
3
- }
4
- export declare function previewCommand(options: PreviewOptions): Promise<void>;
5
- export {};
6
- //# sourceMappingURL=preview.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/commands/preview.ts"],"names":[],"mappings":"AAMA,UAAU,cAAc;IACvB,EAAE,CAAC,EAAE,MAAM,CAAC;CACZ;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAwC3E"}
@@ -1,33 +0,0 @@
1
- import chalk from "chalk";
2
- import { chromium } from "playwright-chromium";
3
- import { annotateScreenshots } from "../lib/annotate.js";
4
- import { findProjectRoot, loadConfig, loadDefinitions } from "../lib/config.js";
5
- import { captureScreenshots } from "../lib/screenshot.js";
6
- export async function previewCommand(options) {
7
- console.log(chalk.bold("\n🥷 Kagemusha — Preview\n"));
8
- const projectRoot = findProjectRoot();
9
- const config = loadConfig(projectRoot);
10
- let definitions = loadDefinitions(projectRoot);
11
- if (options.id) {
12
- definitions = definitions.filter((d) => d.id === options.id);
13
- if (definitions.length === 0) {
14
- console.log(chalk.red(`Definition not found: ${options.id}`));
15
- return;
16
- }
17
- }
18
- console.log(chalk.blue(`📸 Capturing ${definitions.length} screenshot(s)...`));
19
- const results = await captureScreenshots(config, definitions, projectRoot);
20
- const annotated = await annotateScreenshots(definitions, results, projectRoot);
21
- console.log(chalk.green("\n✅ Screenshots captured. Opening preview...\n"));
22
- // Open annotated screenshots in browser
23
- const browser = await chromium.launch({ headless: false });
24
- const context = await browser.newContext();
25
- for (const result of annotated) {
26
- const page = await context.newPage();
27
- await page.goto(`file://${result.annotatedPath}`);
28
- }
29
- console.log(chalk.gray("Press Ctrl+C to close preview.\n"));
30
- // Keep process alive until user closes
31
- await new Promise(() => { });
32
- }
33
- //# sourceMappingURL=preview.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/commands/preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAM1D,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAuB;IAC3D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,CAAC;IAEtD,MAAM,WAAW,GAAG,eAAe,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IAEvC,IAAI,WAAW,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAC/C,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;QAChB,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC,CAAC;QAC7D,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,yBAAyB,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC9D,OAAO;QACR,CAAC;IACF,CAAC;IAED,OAAO,CAAC,GAAG,CACV,KAAK,CAAC,IAAI,CAAC,gBAAgB,WAAW,CAAC,MAAM,mBAAmB,CAAC,CACjE,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;IAC3E,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAC1C,WAAW,EACX,OAAO,EACP,WAAW,CACX,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC,CAAC;IAE3E,wCAAwC;IACxC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;IAE3C,KAAK,MAAM,MAAM,IAAI,SAAS,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC,CAAC;IAE5D,uCAAuC;IACvC,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAC7B,CAAC"}
@@ -1,6 +0,0 @@
1
- interface RunOptions {
2
- ids?: string;
3
- }
4
- export declare function runCommand(options: RunOptions): Promise<void>;
5
- export {};
6
- //# sourceMappingURL=run.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAMA,UAAU,UAAU;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DnE"}
@@ -1,39 +0,0 @@
1
- import chalk from "chalk";
2
- import { annotateScreenshots } from "../lib/annotate.js";
3
- import { findProjectRoot, loadConfig, loadDefinitions } from "../lib/config.js";
4
- import { captureScreenshots } from "../lib/screenshot.js";
5
- import { uploadToS3 } from "../lib/upload.js";
6
- export async function runCommand(options) {
7
- console.log(chalk.bold("\n🥷 Kagemusha — Running pipeline\n"));
8
- const projectRoot = findProjectRoot();
9
- const config = loadConfig(projectRoot);
10
- let definitions = loadDefinitions(projectRoot);
11
- if (options.ids) {
12
- const ids = options.ids.split(",").map((s) => s.trim());
13
- definitions = definitions.filter((d) => ids.includes(d.id));
14
- }
15
- if (definitions.length === 0) {
16
- console.log(chalk.yellow("No screenshot definitions to process. Skipping."));
17
- return;
18
- }
19
- console.log(chalk.gray(` Found ${definitions.length} definition(s) to process\n`));
20
- // Step 1: Capture
21
- console.log(chalk.blue("📸 Capturing screenshots..."));
22
- const captureResults = await captureScreenshots(config, definitions, projectRoot);
23
- console.log(chalk.green(` ✓ Captured ${captureResults.length} screenshot(s)\n`));
24
- // Step 2: Annotate
25
- console.log(chalk.blue("🎨 Drawing annotations..."));
26
- const annotatedResults = await annotateScreenshots(definitions, captureResults, projectRoot);
27
- console.log(chalk.green(` ✓ Annotated ${annotatedResults.length} screenshot(s)\n`));
28
- // Step 3: Publish
29
- if (config.publish?.destination === "s3") {
30
- console.log(chalk.blue("☁️ Uploading to S3..."));
31
- const uploadResults = await uploadToS3(config, annotatedResults, projectRoot);
32
- console.log(chalk.green(` ✓ Uploaded ${uploadResults.length} screenshot(s)\n`));
33
- }
34
- else {
35
- console.log(chalk.green(` Screenshots saved locally\n`));
36
- }
37
- console.log(chalk.bold.green("✅ Pipeline complete!\n"));
38
- }
39
- //# sourceMappingURL=run.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAM9C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAmB;IACnD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC,CAAC;IAE/D,MAAM,WAAW,GAAG,eAAe,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,WAAW,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAE/C,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACjB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACxD,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CACV,KAAK,CAAC,MAAM,CAAC,iDAAiD,CAAC,CAC/D,CAAC;QACF,OAAO;IACR,CAAC;IAED,OAAO,CAAC,GAAG,CACV,KAAK,CAAC,IAAI,CAAC,WAAW,WAAW,CAAC,MAAM,6BAA6B,CAAC,CACtE,CAAC;IAEF,kBAAkB;IAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC;IACvD,MAAM,cAAc,GAAG,MAAM,kBAAkB,CAC9C,MAAM,EACN,WAAW,EACX,WAAW,CACX,CAAC;IACF,OAAO,CAAC,GAAG,CACV,KAAK,CAAC,KAAK,CAAC,gBAAgB,cAAc,CAAC,MAAM,kBAAkB,CAAC,CACpE,CAAC;IAEF,mBAAmB;IACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,CAAC;IACrD,MAAM,gBAAgB,GAAG,MAAM,mBAAmB,CACjD,WAAW,EACX,cAAc,EACd,WAAW,CACX,CAAC;IACF,OAAO,CAAC,GAAG,CACV,KAAK,CAAC,KAAK,CAAC,iBAAiB,gBAAgB,CAAC,MAAM,kBAAkB,CAAC,CACvE,CAAC;IAEF,kBAAkB;IAClB,IAAI,MAAM,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC;QAClD,MAAM,aAAa,GAAG,MAAM,UAAU,CACrC,MAAM,EACN,gBAAgB,EAChB,WAAW,CACX,CAAC;QACF,OAAO,CAAC,GAAG,CACV,KAAK,CAAC,KAAK,CAAC,gBAAgB,aAAa,CAAC,MAAM,kBAAkB,CAAC,CACnE,CAAC;IACH,CAAC;SAAM,CAAC;QACP,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;AACzD,CAAC"}
@@ -1,313 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <title>Kagemusha Annotation Editor</title>
6
- <style>
7
- * { margin: 0; padding: 0; box-sizing: border-box; }
8
- body { background: #1a1a2e; font-family: -apple-system, BlinkMacSystemFont, sans-serif; overflow: hidden; }
9
-
10
- .toolbar {
11
- position: fixed; top: 0; left: 0; right: 0; z-index: 100;
12
- background: #16213e; padding: 8px 16px; display: flex; align-items: center; gap: 12px;
13
- box-shadow: 0 2px 8px rgba(0,0,0,0.3);
14
- }
15
- .toolbar button {
16
- padding: 6px 14px; border: 1px solid #444; border-radius: 6px;
17
- background: #1a1a2e; color: #fff; font-size: 13px; cursor: pointer;
18
- }
19
- .toolbar button:hover { background: #2a2a4e; }
20
- .toolbar button.active { background: #6366f1; border-color: #6366f1; }
21
- .toolbar .separator { width: 1px; height: 24px; background: #444; }
22
- .toolbar .title { color: #888; font-size: 13px; margin-right: 8px; }
23
- .toolbar .save-btn { background: #22c55e; border-color: #22c55e; font-weight: 600; margin-left: auto; }
24
- .toolbar .save-btn:hover { background: #16a34a; }
25
-
26
- .canvas-area {
27
- margin-top: 48px; display: flex; justify-content: center; align-items: flex-start;
28
- padding: 20px; height: calc(100vh - 48px); overflow: auto;
29
- }
30
-
31
- .editor-container {
32
- position: relative; cursor: crosshair;
33
- }
34
- .editor-container img { display: block; width: 100%; height: auto; }
35
-
36
- .annotation { position: absolute; }
37
- .annotation.selected { outline: 2px dashed #6366f1; outline-offset: 2px; }
38
-
39
- .label-input {
40
- background: none; border: none; color: inherit; font: inherit;
41
- width: 100%; outline: none;
42
- }
43
-
44
- .hint {
45
- position: fixed; bottom: 16px; left: 50%; transform: translateX(-50%);
46
- color: #666; font-size: 12px;
47
- }
48
- </style>
49
- </head>
50
- <body>
51
-
52
- <div class="toolbar">
53
- <span class="title">🥷 Annotation Editor</span>
54
- <button id="tool-rect" class="active" onclick="setTool('rect')">▭ Rectangle</button>
55
- <button id="tool-arrow" onclick="setTool('arrow')">→ Arrow</button>
56
- <button id="tool-label" onclick="setTool('label')">T Label</button>
57
- <div class="separator"></div>
58
- <button onclick="deleteSelected()">🗑 Delete</button>
59
- <button class="save-btn" onclick="save()">💾 Save</button>
60
- </div>
61
-
62
- <div class="canvas-area">
63
- <div class="editor-container" id="editor">
64
- <img id="screenshot" />
65
- </div>
66
- </div>
67
-
68
- <div class="hint">Click and drag to add annotations. Click to select. Press Delete to remove.</div>
69
-
70
- <script>
71
- let tool = 'rect';
72
- let annotations = [];
73
- let selectedId = null;
74
- let dragState = null;
75
- let nextId = 1;
76
-
77
- const editor = document.getElementById('editor');
78
- const img = document.getElementById('screenshot');
79
-
80
- // Mouse position relative to the editor container (in CSS px, matches display)
81
- function getPos(e) {
82
- const r = editor.getBoundingClientRect();
83
- return { x: e.clientX - r.left, y: e.clientY - r.top };
84
- }
85
-
86
- // Ratio to convert display px → image natural px (for saving)
87
- function getScale() {
88
- return img.naturalWidth / editor.getBoundingClientRect().width;
89
- }
90
-
91
- window.initEditor = (screenshotDataUrl, existingDecorations) => {
92
- img.src = screenshotDataUrl;
93
- img.onload = () => {
94
- // Set editor width to fit window
95
- const maxW = window.innerWidth - 80;
96
- const maxH = window.innerHeight - 108;
97
- const ratio = img.naturalWidth / img.naturalHeight;
98
- let w = maxW;
99
- if (w / ratio > maxH) w = maxH * ratio;
100
- editor.style.width = w + 'px';
101
-
102
- if (existingDecorations) {
103
- existingDecorations.forEach(d => addFromDecoration(d));
104
- }
105
- };
106
- };
107
-
108
- function setTool(t) {
109
- tool = t;
110
- document.querySelectorAll('.toolbar button').forEach(b => b.classList.remove('active'));
111
- document.getElementById('tool-' + t)?.classList.add('active');
112
- deselectAll();
113
- }
114
-
115
- function deselectAll() {
116
- selectedId = null;
117
- document.querySelectorAll('.annotation').forEach(el => el.classList.remove('selected'));
118
- }
119
-
120
- function selectEl(el) {
121
- deselectAll();
122
- selectedId = parseInt(el.dataset.id);
123
- el.classList.add('selected');
124
- }
125
-
126
- function deleteSelected() {
127
- if (!selectedId) return;
128
- const el = document.querySelector(`[data-id="${selectedId}"]`);
129
- if (el) el.remove();
130
- annotations = annotations.filter(a => a.id !== selectedId);
131
- selectedId = null;
132
- }
133
-
134
- document.addEventListener('keydown', e => {
135
- if (e.key === 'Delete' || e.key === 'Backspace') {
136
- if (document.activeElement.classList?.contains('label-input')) return;
137
- deleteSelected();
138
- }
139
- });
140
-
141
- // --- CREATE ---
142
- editor.addEventListener('mousedown', e => {
143
- if (e.target.closest('.annotation')) return;
144
- const p = getPos(e);
145
- deselectAll();
146
-
147
- if (tool === 'rect') {
148
- const id = nextId++;
149
- const el = makeRect(id, p.x, p.y, 0, 0);
150
- editor.appendChild(el);
151
- dragState = { type: 'create-rect', id, el, sx: p.x, sy: p.y };
152
- } else if (tool === 'arrow') {
153
- const id = nextId++;
154
- const el = makeArrow(id, p.x, p.y, p.x, p.y);
155
- editor.appendChild(el);
156
- dragState = { type: 'create-arrow', id, el, sx: p.x, sy: p.y };
157
- } else if (tool === 'label') {
158
- const id = nextId++;
159
- annotations.push({ id, type: 'label', x: p.x, y: p.y, text: 'Label' });
160
- const el = makeLabel(id, p.x, p.y, 'Label');
161
- editor.appendChild(el);
162
- selectEl(el);
163
- el.querySelector('.label-input').focus();
164
- el.querySelector('.label-input').select();
165
- }
166
- });
167
-
168
- document.addEventListener('mousemove', e => {
169
- if (!dragState) return;
170
- const p = getPos(e);
171
-
172
- if (dragState.type === 'create-rect') {
173
- const el = dragState.el;
174
- const w = p.x - dragState.sx;
175
- const h = p.y - dragState.sy;
176
- el.style.left = (w > 0 ? dragState.sx : p.x) + 'px';
177
- el.style.top = (h > 0 ? dragState.sy : p.y) + 'px';
178
- el.style.width = Math.abs(w) + 'px';
179
- el.style.height = Math.abs(h) + 'px';
180
- } else if (dragState.type === 'create-arrow') {
181
- updateArrow(dragState.el, dragState.sx, dragState.sy, p.x, p.y);
182
- } else if (dragState.type === 'move') {
183
- const a = annotations.find(a => a.id === dragState.id);
184
- if (!a) return;
185
- a.x = p.x - dragState.ox;
186
- a.y = p.y - dragState.oy;
187
- dragState.el.style.left = a.x + 'px';
188
- dragState.el.style.top = a.y + 'px';
189
- }
190
- });
191
-
192
- document.addEventListener('mouseup', e => {
193
- if (!dragState) return;
194
- const p = getPos(e);
195
-
196
- if (dragState.type === 'create-rect') {
197
- const w = Math.abs(p.x - dragState.sx);
198
- const h = Math.abs(p.y - dragState.sy);
199
- if (w < 5 && h < 5) { dragState.el.remove(); }
200
- else {
201
- const a = { id: dragState.id, type: 'rect', x: Math.min(dragState.sx, p.x), y: Math.min(dragState.sy, p.y), width: w, height: h };
202
- annotations.push(a);
203
- selectEl(dragState.el);
204
- }
205
- } else if (dragState.type === 'create-arrow') {
206
- const dist = Math.hypot(p.x - dragState.sx, p.y - dragState.sy);
207
- if (dist < 5) { dragState.el.remove(); }
208
- else {
209
- annotations.push({ id: dragState.id, type: 'arrow', fromX: dragState.sx, fromY: dragState.sy, toX: p.x, toY: p.y });
210
- selectEl(dragState.el);
211
- }
212
- }
213
- dragState = null;
214
- });
215
-
216
- // --- ELEMENTS ---
217
- function makeRect(id, x, y, w, h) {
218
- const el = document.createElement('div');
219
- el.className = 'annotation'; el.dataset.id = id;
220
- el.style.cssText = `left:${x}px;top:${y}px;width:${w}px;height:${h}px;border:2px solid #FF0000;border-radius:4px;`;
221
- el.addEventListener('mousedown', e => {
222
- e.stopPropagation();
223
- selectEl(el);
224
- const a = annotations.find(a => a.id === parseInt(el.dataset.id));
225
- if (a) {
226
- const pp = getPos(e);
227
- dragState = { type: 'move', id: a.id, el, ox: pp.x - a.x, oy: pp.y - a.y };
228
- }
229
- });
230
- return el;
231
- }
232
-
233
- function makeArrow(id, x1, y1, x2, y2) {
234
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
235
- svg.classList.add('annotation'); svg.dataset.id = id;
236
- svg.style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;';
237
- svg.innerHTML = `
238
- <defs><marker id="ah-${id}" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto" fill="#FF0000">
239
- <polygon points="0 0, 10 3.5, 0 7"/></marker></defs>
240
- <line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="#FF0000" stroke-width="2" marker-end="url(#ah-${id})" style="pointer-events:stroke;cursor:pointer;" />`;
241
- svg.querySelector('line').addEventListener('mousedown', e => {
242
- e.stopPropagation();
243
- selectEl(svg);
244
- });
245
- return svg;
246
- }
247
-
248
- function updateArrow(svg, x1, y1, x2, y2) {
249
- const line = svg.querySelector('line');
250
- line.setAttribute('x1', x1); line.setAttribute('y1', y1);
251
- line.setAttribute('x2', x2); line.setAttribute('y2', y2);
252
- }
253
-
254
- function makeLabel(id, x, y, text) {
255
- const el = document.createElement('div');
256
- el.className = 'annotation'; el.dataset.id = id;
257
- el.style.cssText = `left:${x}px;top:${y}px;padding:4px 8px;background:#fff;border:1px solid #FF0000;border-radius:4px;color:#FF0000;font-size:14px;min-width:40px;`;
258
- el.innerHTML = `<input class="label-input" value="${text}" style="color:#FF0000;font-size:14px;" />`;
259
- el.querySelector('.label-input').addEventListener('input', e => {
260
- const a = annotations.find(a => a.id === parseInt(el.dataset.id));
261
- if (a) a.text = e.target.value;
262
- });
263
- el.addEventListener('mousedown', e => {
264
- if (e.target.classList.contains('label-input')) return;
265
- e.stopPropagation();
266
- selectEl(el);
267
- const a = annotations.find(a => a.id === parseInt(el.dataset.id));
268
- if (a) {
269
- const pp = getPos(e);
270
- dragState = { type: 'move', id: a.id, el, ox: pp.x - a.x, oy: pp.y - a.y };
271
- }
272
- });
273
- return el;
274
- }
275
-
276
- // --- LOAD EXISTING ---
277
- function addFromDecoration(d) {
278
- const id = nextId++;
279
- const s = getScale();
280
- if (d.type === 'rect' && d.target && 'x' in d.target) {
281
- const a = { id, type: 'rect', x: d.target.x/s, y: d.target.y/s, width: d.target.width/s, height: d.target.height/s };
282
- annotations.push(a);
283
- editor.appendChild(makeRect(id, a.x, a.y, a.width, a.height));
284
- } else if (d.type === 'arrow' && 'x' in d.from && 'x' in d.to) {
285
- const a = { id, type: 'arrow', fromX: d.from.x/s, fromY: d.from.y/s, toX: d.to.x/s, toY: d.to.y/s };
286
- annotations.push(a);
287
- editor.appendChild(makeArrow(id, a.fromX, a.fromY, a.toX, a.toY));
288
- } else if (d.type === 'label' && 'x' in d.position) {
289
- const a = { id, type: 'label', x: d.position.x/s, y: d.position.y/s, text: d.text };
290
- annotations.push(a);
291
- editor.appendChild(makeLabel(id, a.x, a.y, a.text));
292
- }
293
- }
294
-
295
- // --- SAVE ---
296
- function save() {
297
- const s = getScale();
298
- const decorations = annotations.map(a => {
299
- if (a.type === 'rect') {
300
- return { type: 'rect', target: { x: Math.round(a.x*s), y: Math.round(a.y*s), width: Math.round(a.width*s), height: Math.round(a.height*s) }, style: { color: '#FF0000', strokeWidth: 2 } };
301
- } else if (a.type === 'arrow') {
302
- return { type: 'arrow', from: { x: Math.round(a.fromX*s), y: Math.round(a.fromY*s) }, to: { x: Math.round(a.toX*s), y: Math.round(a.toY*s) }, style: { color: '#FF0000', strokeWidth: 2 } };
303
- } else if (a.type === 'label') {
304
- return { type: 'label', text: a.text, position: { x: Math.round(a.x*s), y: Math.round(a.y*s) }, style: { fontSize: 14, color: '#FF0000', background: '#FFFFFF' } };
305
- }
306
- }).filter(Boolean);
307
-
308
- window.__SAVED_DECORATIONS__ = decorations;
309
- document.title = 'SAVED';
310
- }
311
- </script>
312
- </body>
313
- </html>