aied-lab commited on
Commit
fe52fe7
·
verified ·
1 Parent(s): c83a914

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +153 -19
index.html CHANGED
@@ -1,19 +1,153 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-Hant">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Gemma 多模態影像分析助手</title>
7
+ <style>
8
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; max-width: 600px; margin: 40px auto; padding: 20px; background: #f9f9f9; color: #333; }
9
+ .card { background: #fff; padding: 24px; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); }
10
+ h2 { margin-top: 0; font-size: 1.5rem; color: #111; }
11
+ p { color: #666; font-size: 0.9rem; }
12
+ .form-group { margin-bottom: 16px; }
13
+ label { display: block; font-weight: 600; margin-bottom: 6px; font-size: 0.9rem; }
14
+ input[type="text"], input[type="file"], textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; box-sizing: border-box; }
15
+ button { background: #000; color: #fff; border: none; padding: 10px 20px; font-weight: 600; border-radius: 6px; cursor: pointer; width: 100%; }
16
+ button:hover { background: #333; }
17
+ button:disabled { background: #ccc; cursor: not-allowed; }
18
+ #result { margin-top: 20px; padding: 12px; background: #f1f1f1; border-radius: 6px; white-space: pre-wrap; min-height: 50px; font-size: 0.95rem; }
19
+ </style>
20
+ </head>
21
+ <body>
22
+
23
+ <div class="card">
24
+ <h2>Gemma 多模態影像分析助手</h2>
25
+ <p>透過 Hugging Face Router 呼叫 gemma-4-31B-it 模型進行影像辨識與分析。</p>
26
+
27
+ <div class="form-group">
28
+ <label for="apiKey">Hugging Face Token (HF_TOKEN)</label>
29
+ <input type="password" id="apiKey" placeholder="請輸入您的 HF_TOKEN">
30
+ </div>
31
+
32
+ <div class="form-group">
33
+ <label for="imageInput">上傳圖片</label>
34
+ <input type="file" id="imageInput" accept="image/*">
35
+ </div>
36
+
37
+ <div class="form-group">
38
+ <label for="promptInput">輸入提示詞 (Prompt)</label>
39
+ <input type="text" id="promptInput" value="Describe this image in one sentence in chinese">
40
+ </div>
41
+
42
+ <button id="submitBtn" onclick="analyzeImage()">送出分析</button>
43
+
44
+ <div class="form-group" style="margin-top: 20px;">
45
+ <label>模型回應</label>
46
+ <div id="result">尚未開始分析...</div>
47
+ </div>
48
+ </div>
49
+
50
+ <script>
51
+ // 壓縮並轉成 Base64 的前端實作
52
+ function compressImage(file, maxSize = 1024, quality = 0.85) {
53
+ return new Promise((resolve, reject) => {
54
+ const reader = new FileReader();
55
+ reader.onload = function (e) {
56
+ const img = new Image();
57
+ img.onload = function () {
58
+ let width = img.width;
59
+ let height = img.height;
60
+
61
+ if (width > height) {
62
+ if (width > maxSize) {
63
+ height *= maxSize / width;
64
+ width = maxSize;
65
+ }
66
+ } else {
67
+ if (height > maxSize) {
68
+ width *= maxSize / height;
69
+ height = maxSize;
70
+ }
71
+ }
72
+
73
+ const canvas = document.createElement('canvas');
74
+ canvas.width = width;
75
+ canvas.height = height;
76
+ const ctx = canvas.getContext('2d');
77
+ ctx.drawImage(img, 0, 0, width, height);
78
+
79
+ const dataUrl = canvas.toDataURL('image/jpeg', quality);
80
+ resolve(dataUrl);
81
+ };
82
+ img.src = e.target.result;
83
+ };
84
+ reader.onerror = reject;
85
+ reader.readAsDataURL(file);
86
+ });
87
+ }
88
+
89
+ async function analyzeImage() {
90
+ const apiKey = document.getElementById('apiKey').value.trim();
91
+ const imageFile = document.getElementById('imageInput').files[0];
92
+ let prompt = document.getElementById('promptInput').value.trim();
93
+ const resultDiv = document.getElementById('result');
94
+ const submitBtn = document.getElementById('submitBtn');
95
+
96
+ if (!apiKey) {
97
+ alert('請先輸入 Hugging Face Token!');
98
+ return;
99
+ }
100
+ if (!imageFile) {
101
+ alert('請先上傳一張圖片!');
102
+ return;
103
+ }
104
+ if (!prompt) {
105
+ prompt = "Describe this image in one sentence.";
106
+ }
107
+
108
+ submitBtn.disabled = true;
109
+ resultDiv.innerText = '處理中,請稍候...';
110
+
111
+ try {
112
+ // 壓縮轉 Base64
113
+ const base64Image = await compressImage(imageFile);
114
+
115
+ // 呼叫 Hugging Face Router API (OpenAI 相容格式)
116
+ const response = await fetch("https://router.huggingface.co/v1/chat/completions", {
117
+ method: "POST",
118
+ headers: {
119
+ "Authorization": `Bearer ${apiKey}`,
120
+ "Content-Type": "application/json"
121
+ },
122
+ body: JSON.stringify({
123
+ model: "google/gemma-4-31B-it:novita",
124
+ messages: [
125
+ {
126
+ role: "user",
127
+ content: [
128
+ { type: "text", text: prompt },
129
+ { type: "image_url", image_url: { url: base64Image } }
130
+ ]
131
+ }
132
+ ],
133
+ max_tokens: 300
134
+ })
135
+ });
136
+
137
+ const data = await response.json();
138
+
139
+ if (response.ok && data.choices && data.choices.length > 0) {
140
+ resultDiv.innerText = data.choices[0].message.content;
141
+ } else {
142
+ resultDiv.innerText = `發生錯誤:${data.error?.message || JSON.stringify(data)}`;
143
+ }
144
+ } catch (error) {
145
+ resultDiv.innerText = `發生錯誤:${error.message}`;
146
+ } finally {
147
+ submitBtn.disabled = false;
148
+ }
149
+ }
150
+ </script>
151
+
152
+ </body>
153
+ </html>