chenbhao Claude Opus 4.6 commited on
Commit
02a4f30
·
1 Parent(s): 6d1207a

refactor: extract UI from ImageShowTool into separate component

Browse files

Split ImageShowTool.ts into utility functions (ImageShowTool.ts) and
React component (UI.tsx), enabling reuse of load/calculate logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

src/tools/ImageShowTool/ImageShowTool.ts CHANGED
@@ -1,108 +1,48 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * Visual test: display a local image or URL using ink-picture + Ink.
4
- *
5
- * Usage:
6
- * bun run src/ink-picture/__tests__/LocalPicture.test.tsx [path|url]
7
- *
8
- * Examples:
9
- * bun run src/ink-picture/__tests__/LocalPicture.test.tsx ~/Pictures/IMAGE/image.png
10
- * bun run src/ink-picture/__tests__/LocalPicture.test.tsx https://example.com/image.jpg
11
- */
12
-
13
  import { Jimp } from "jimp";
14
- import { useEffect, useState } from "react";
15
- import { render, Box, Text, useApp } from "ink";
16
- import Image, { InkPictureProvider } from "../index.ts";
17
- import { loadImageFromUrl } from "../utils/jimpURL.ts";
18
 
19
- // 终端字符尺寸(像素)
20
- const CELL_WIDTH = 8;
21
- const CELL_HEIGHT = 16;
 
 
 
22
 
23
- // 获取命令行参数
24
- const args = process.argv.slice(2);
25
- const IMAGE_PATH = args[0] || "/home/yuki/Pictures/Wallpapers/3god.jpg";
26
 
27
- // 判断是 URL 还是本地路径
28
- const isUrl = IMAGE_PATH.startsWith("http://") || IMAGE_PATH.startsWith("https://");
 
29
 
30
- // 加载图片(支持本地和 URL)
31
- async function loadImage(path: string) {
32
- if (isUrl) {
33
  return loadImageFromUrl(path);
34
  } else {
35
  return Jimp.read(path);
36
  }
37
  }
38
 
39
- function App() {
40
- const { exit } = useApp();
41
- const [dimensions, setDimensions] = useState<{
42
- width: number;
43
- height: number;
44
- pixelWidth: number;
45
- pixelHeight: number;
46
- } | null>(null);
47
- const [err, setErr] = useState(false);
48
-
49
- useEffect(() => {
50
- (async () => {
51
- try {
52
- const image = await loadImage(IMAGE_PATH);
53
- const origW = image.bitmap.width;
54
- const origH = image.bitmap.height;
55
- const cols = process.stdout.columns ?? 80;
56
-
57
- const targetW_chars = Math.floor(cols * 0.1618);
58
- const targetW_pixels = targetW_chars * CELL_WIDTH;
59
- const targetH_pixels = Math.floor(targetW_pixels * (origH / origW));
60
- const minH_pixels = 3 * CELL_HEIGHT;
61
- const finalH_pixels = Math.max(targetH_pixels, minH_pixels);
62
- const targetH_chars = Math.ceil(finalH_pixels / CELL_HEIGHT);
63
-
64
- setDimensions({
65
- width: targetW_chars,
66
- height: targetH_chars,
67
- pixelWidth: targetW_pixels,
68
- pixelHeight: finalH_pixels,
69
- });
70
- } catch (e) {
71
- console.error(e);
72
- setErr(true);
73
- exit();
74
- }
75
- })();
76
- }, []);
77
-
78
- useEffect(() => {
79
- const handleSigint = () => exit();
80
- process.on("SIGINT", handleSigint);
81
- }, [exit]);
82
-
83
- if (err) {
84
- return <Text color="red" > Failed to fetch: { IMAGE_PATH } </Text>;
85
- }
86
-
87
- if (!dimensions) {
88
- return <Text>Loading...</Text>;
89
- }
90
-
91
- return (
92
- <Box flexDirection= "column" >
93
- <InkPictureProvider>
94
- <Image
95
- src={ IMAGE_PATH }
96
- width = { dimensions.width }
97
- height = { dimensions.height }
98
- pixelWidth = { dimensions.pixelWidth }
99
- pixelHeight = { dimensions.pixelHeight }
100
- alt = { isUrl? "url-image": "local-image" }
101
- />
102
- </InkPictureProvider>
103
- </Box>
104
- );
105
  }
106
-
107
- const { waitUntilExit } = render(<App />);
108
- await waitUntilExit();
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import { Jimp } from "jimp";
2
+ import { loadImageFromUrl } from "../../ink-picture/utils/jimpURL.ts";
3
+
4
+ export const CELL_WIDTH = 8;
5
+ export const CELL_HEIGHT = 16;
6
 
7
+ export interface ImageDimensions {
8
+ width: number; // 字符宽度
9
+ height: number; // 字符高度
10
+ pixelWidth: number; // 像素宽度
11
+ pixelHeight: number; // 像素高度
12
+ }
13
 
14
+ export function getImagePath(args: string[]): string {
15
+ return args[0] || "/home/yuki/Pictures/Wallpapers/3god.jpg";
16
+ }
17
 
18
+ export function isUrl(path: string): boolean {
19
+ return path.startsWith("http://") || path.startsWith("https://");
20
+ }
21
 
22
+ export async function loadImage(path: string) {
23
+ if (isUrl(path)) {
 
24
  return loadImageFromUrl(path);
25
  } else {
26
  return Jimp.read(path);
27
  }
28
  }
29
 
30
+ export function calculateDimensions(
31
+ imageWidth: number,
32
+ imageHeight: number,
33
+ terminalCols: number
34
+ ): ImageDimensions {
35
+ const targetW_chars = Math.floor(terminalCols * 0.1618);
36
+ const targetW_pixels = targetW_chars * CELL_WIDTH;
37
+ const targetH_pixels = Math.floor(targetW_pixels * (imageHeight / imageWidth));
38
+ const minH_pixels = 3 * CELL_HEIGHT;
39
+ const finalH_pixels = Math.max(targetH_pixels, minH_pixels);
40
+ const targetH_chars = Math.ceil(finalH_pixels / CELL_HEIGHT);
41
+
42
+ return {
43
+ width: targetW_chars,
44
+ height: targetH_chars,
45
+ pixelWidth: targetW_pixels,
46
+ pixelHeight: finalH_pixels,
47
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }