ThisIsAnAnonymousUser commited on
Commit
1c5bcde
·
verified ·
1 Parent(s): 9e1e613

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +405 -18
index.html CHANGED
@@ -1,19 +1,406 @@
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="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <meta name="color-scheme" content="light" />
7
+ <meta name="description" content="Estimate the minimalism–maximalism stylistic position of a visual design." />
8
+ <title>Commitment over Compromise — Stylistic Commitment Research</title>
9
+ <style>
10
+ :root {
11
+ --navy: #193B63;
12
+ --navy-dark: #112C4B;
13
+ --ink: #202B3B;
14
+ --muted: #617085;
15
+ --line: #DDE4EB;
16
+ --background: #F8FAFC;
17
+ --white: #FFFFFF;
18
+ --blue: #9AB4CC;
19
+ --gold: #B99A63;
20
+ }
21
+ *, *::before, *::after { box-sizing: border-box; }
22
+ html { font-size: 16px; }
23
+ body {
24
+ margin: 0;
25
+ background: var(--background);
26
+ color: var(--ink);
27
+ font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont,
28
+ "Segoe UI", Arial, sans-serif;
29
+ line-height: 1.55;
30
+ }
31
+ button, input { font: inherit; }
32
+ button:focus-visible, .dropzone:focus-visible {
33
+ outline: 3px solid #7AA8D0; outline-offset: 3px;
34
+ }
35
+ .container { width: min(100% - 40px, 1200px); margin: 0 auto; }
36
+ header { text-align: center; padding: 9px 0 11px; }
37
+ h1 {
38
+ max-width: 1060px;
39
+ margin: 0 auto 7px;
40
+ color: var(--navy-dark);
41
+ font-size: clamp(1.85rem, 2.55vw, 2.19rem);
42
+ line-height: 1.18;
43
+ letter-spacing: -.032em;
44
+ font-weight: 750;
45
+ }
46
+ .intro {
47
+ max-width: 920px;
48
+ margin: 6px auto 0;
49
+ color: #455366;
50
+ font-size: 1.02rem;
51
+ }
52
+ main { padding-bottom: 16px; }
53
+ .workspace { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: stretch; }
54
+ .panel {
55
+ min-width: 0;
56
+ padding: 16px 19px;
57
+ background: var(--white);
58
+ border: 1px solid var(--line);
59
+ border-radius: 14px;
60
+ }
61
+ h2 { margin: 0 0 9px; font-size: 1.16rem; color: var(--navy-dark); font-weight: 750; letter-spacing: -.013em; }
62
+ #upload-heading, #results-heading { font-size: 1.42rem; line-height: 1.3; }
63
+ .dropzone {
64
+ position: relative;
65
+ display: flex;
66
+ min-height: 219px;
67
+ align-items: center;
68
+ justify-content: center;
69
+ padding: 22px;
70
+ border: 1.5px dashed #AEBBC8;
71
+ border-radius: 10px;
72
+ background: #FCFDFE;
73
+ cursor: pointer;
74
+ overflow: hidden;
75
+ transition: border-color 120ms ease, background 120ms ease;
76
+ }
77
+ .dropzone:hover, .dropzone.dragging { border-color: var(--navy); background: #F1F6FA; }
78
+ .dropzone input { display: none; }
79
+ .drop-prompt { text-align: center; }
80
+ .upload-icon { color: var(--navy); display: block; margin: 0 auto 14px; }
81
+ .upload-title { margin: 0; color: var(--navy-dark); font-weight: 650; }
82
+ .upload-detail { margin: 5px 0 0; color: var(--muted); font-size: .9rem; }
83
+ .preview {
84
+ display: none;
85
+ max-width: 100%;
86
+ width: 100%;
87
+ height: 205px;
88
+ object-fit: contain;
89
+ }
90
+ .dropzone.has-image { background: #FFFFFF; }
91
+ .dropzone.has-image .drop-prompt { display: none; }
92
+ .dropzone.has-image .preview { display: block; }
93
+ .filename { min-height: 21px; margin: 7px 1px 9px; font-size: .81rem; color: var(--muted); overflow-wrap: anywhere; }
94
+ .primary {
95
+ display: block;
96
+ width: 100%;
97
+ padding: 10px 16px;
98
+ border: 1px solid var(--navy);
99
+ border-radius: 9px;
100
+ color: white;
101
+ background: var(--navy);
102
+ font-weight: 650;
103
+ cursor: pointer;
104
+ transition: background 120ms ease;
105
+ }
106
+ .primary:hover:not(:disabled) { background: var(--navy-dark); }
107
+ .primary:disabled { opacity: .48; cursor: not-allowed; }
108
+ .message { min-height: 18px; margin: 7px 0 0; color: var(--muted); font-size: .84rem; }
109
+ .message.error { color: #A43636; }
110
+ .empty-result {
111
+ display: flex;
112
+ align-items: center;
113
+ justify-content: center;
114
+ min-height: 299px;
115
+ border: 1px dashed var(--line);
116
+ border-radius: 10px;
117
+ text-align: center;
118
+ color: var(--muted);
119
+ padding: 28px;
120
+ }
121
+ .results { display: none; }
122
+ .score-label { margin: 0 0 3px; font-size: .9rem; color: var(--muted); font-weight: 650; }
123
+ .score {
124
+ color: var(--navy);
125
+ font-size: clamp(2.08rem, 2.7vw, 2.35rem);
126
+ font-variant-numeric: tabular-nums;
127
+ font-weight: 760;
128
+ line-height: 1.17;
129
+ letter-spacing: -.04em;
130
+ }
131
+ .continuum { margin-top: 11px; }
132
+ .continuum-label { font-size: .85rem; font-weight: 650; color: var(--navy-dark); margin-bottom: 9px; }
133
+ .scale {
134
+ position: relative;
135
+ height: 13px;
136
+ border: 1px solid #C7D2DC;
137
+ border-radius: 20px;
138
+ background: linear-gradient(to right, #8FAEC9 0%, #B9CBD8 29%, #DBE0E4 50%, #D9C49D 73%, #B99A63 100%);
139
+ }
140
+ .scale-center {
141
+ position: absolute;
142
+ left: 50%;
143
+ top: -4px;
144
+ height: 21px;
145
+ border-left: 1px solid rgba(44, 58, 70, .52);
146
+ }
147
+ .marker {
148
+ position: absolute;
149
+ top: -9px;
150
+ left: 50%;
151
+ width: 7px;
152
+ height: 29px;
153
+ transform: translateX(-50%);
154
+ background: var(--navy-dark);
155
+ border: 1px solid white;
156
+ border-radius: 4px;
157
+ box-shadow: 0 0 0 1px rgba(18, 45, 75, .38);
158
+ transition: left 180ms ease;
159
+ }
160
+ .scale-ticks {
161
+ display: grid;
162
+ grid-template-columns: repeat(3, minmax(0, 1fr));
163
+ gap: 8px;
164
+ margin-top: 7px;
165
+ color: #445368;
166
+ font-size: .77rem;
167
+ font-weight: 650;
168
+ line-height: 1.38;
169
+ }
170
+ .scale-ticks span { min-width: 0; }
171
+ .scale-ticks span:nth-child(2) { text-align: center; }
172
+ .scale-ticks span:nth-child(3) { text-align: right; }
173
+ .scale-ticks small { display: block; font-size: .72rem; font-weight: 500; color: var(--muted); }
174
+ .scale-caption { margin: 7px 0 0; color: #67768A; font-size: .72rem; line-height: 1.4; }
175
+ .outside { display: none; color: #805D25; font-weight: 650; margin-top: 9px; font-size: .78rem; }
176
+ .divider { border: 0; border-top: 1px solid var(--line); margin: 10px 0 8px; }
177
+ .secondary-label { font-size: .86rem; color: var(--muted); font-weight: 650; }
178
+ .secondary-value { color: var(--navy-dark); font-size: 1.35rem; font-weight: 700; font-variant-numeric: tabular-nums; margin-top: 2px; }
179
+ .interpretation { margin: 8px 0 0; font-size: .85rem; line-height: 1.43; color: #526174; }
180
+ .model-info {
181
+ margin-top: 12px;
182
+ padding: 15px 20px 16px;
183
+ background: var(--white);
184
+ border: 1px solid var(--line);
185
+ border-radius: 14px;
186
+ }
187
+ .model-info h2 { margin: 0 0 9px; }
188
+ .model-info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; }
189
+ .model-info h3 { margin: 0 0 5px; color: var(--navy-dark); font-size: .98rem; }
190
+ .model-info p { margin: 0; color: #526174; font-size: .85rem; line-height: 1.45; }
191
+ @media (max-width: 790px) {
192
+ .container { width: min(100% - 30px, 1200px); }
193
+ header { padding: 30px 0 23px; }
194
+ .workspace { grid-template-columns: 1fr; gap: 15px; }
195
+ .panel { padding: 22px; }
196
+ .dropzone { min-height: 258px; }
197
+ .empty-result { min-height: 185px; }
198
+ .preview { height: 242px; }
199
+ .model-info { padding: 20px; }
200
+ .model-info-grid { grid-template-columns: 1fr; gap: 25px; }
201
+ .scale-ticks { gap: 5px; font-size: .7rem; }
202
+ .scale-ticks small { font-size: .68rem; }
203
+ }
204
+ /* On shorter desktop screens, prioritize keeping both explanatory sections visible.
205
+ Very small screens still scroll naturally rather than reducing type to unreadable sizes. */
206
+ @media (min-width: 791px) and (max-height: 780px) {
207
+ header { padding-top: 14px; padding-bottom: 15px; }
208
+ h1 { font-size: clamp(1.85rem, 2.6vw, 2.23rem); }
209
+ .intro { font-size: 1.02rem; margin-top: 7px; }
210
+ .dropzone { min-height: 195px; }
211
+ .preview { height: 182px; }
212
+ .empty-result { min-height: 278px; }
213
+ .model-info { margin-top: 12px; padding-top: 14px; padding-bottom: 16px; }
214
+ .model-info p { font-size: .85rem; line-height: 1.45; }
215
+ }
216
+ @media (prefers-reduced-motion: reduce) {
217
+ .marker, .dropzone, .primary { transition: none; }
218
+ }
219
+ </style>
220
+ </head>
221
+ <body>
222
+ <div class="container">
223
+ <header>
224
+ <h1>Commitment over Compromise: The Unique Aesthetic Rewards of Stylistic Commitment</h1>
225
+ <p class="intro">Upload a design image to estimate its position along the minimalism–maximalism stylistic continuum using a trained computer-vision model.</p>
226
+ </header>
227
+
228
+ <main>
229
+ <div class="workspace">
230
+ <section class="panel" aria-labelledby="upload-heading">
231
+ <h2 id="upload-heading">Upload a Design Image</h2>
232
+ <div id="dropzone" class="dropzone" tabindex="0" role="button" aria-label="Select or drop a design image">
233
+ <input id="image-file" type="file" accept="image/png,image/jpeg,image/webp,image/bmp,image/gif" />
234
+ <div class="drop-prompt">
235
+ <svg class="upload-icon" aria-hidden="true" width="39" height="39" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
236
+ <path d="M12 16V3m0 0L7 8m5-5 5 5"/><path d="M4 16v3a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-3"/>
237
+ </svg>
238
+ <p class="upload-title">Drag and drop an image here</p>
239
+ <p class="upload-detail">or click to browse</p>
240
+ </div>
241
+ <img id="image-preview" class="preview" alt="Selected design image preview" />
242
+ </div>
243
+ <p id="filename" class="filename">PNG, JPG, WebP, BMP, or GIF</p>
244
+ <button id="analyze-button" class="primary" type="button" disabled>Analyze Design</button>
245
+ <p id="status" class="message" role="status" aria-live="polite"></p>
246
+ </section>
247
+
248
+ <section class="panel" aria-labelledby="results-heading">
249
+ <h2 id="results-heading">Style Analysis</h2>
250
+ <div id="empty-result" class="empty-result"></div>
251
+ <div id="results" class="results" aria-live="polite">
252
+ <p class="score-label">Predicted Style Score</p>
253
+ <div id="style-score" class="score">—</div>
254
+ <div class="continuum">
255
+ <div class="continuum-label">Minimalism–Maximalism Continuum</div>
256
+ <div class="scale" role="img" aria-label="Stylistic continuum, minimalism on the left, moderate in the center, maximalism on the right">
257
+ <div class="scale-center" aria-hidden="true"></div>
258
+ <div id="marker" class="marker" aria-hidden="true"></div>
259
+ </div>
260
+ <div class="scale-ticks"><span>Stylistic Commitment<small>(Minimal)</small></span><span>Stylistically Compromised<small>(Moderate)</small></span><span>Stylistic Commitment<small>(Maximal)</small></span></div>
261
+ <p id="outside" class="outside">The prediction is beyond the visual window; its marker is shown at the boundary. The numerical score is unchanged.</p>
262
+ </div>
263
+ <hr class="divider" />
264
+ <div class="secondary-label">Estimated Expert Rating (1–7)</div>
265
+ <div id="expert-rating" class="secondary-value">—</div>
266
+ </div>
267
+ </section>
268
+ </div>
269
+
270
+ <section class="model-info" aria-labelledby="model-heading">
271
+ <h2 id="model-heading">About the Model</h2>
272
+ <div class="model-info-grid">
273
+ <div>
274
+ <h3>Model Overview</h3>
275
+ <p>This tool accompanies the research manuscript <em>Commitment over Compromise: The Unique Aesthetic Rewards of Stylistic Commitment</em>, which examines how clearly expressing a design style, rather than occupying a stylistic middle ground, shapes aesthetic appeal. Using minimalism and maximalism as two distinct design styles, the tool employs a trained computer-vision model (ResNet-18) to estimate where a design falls along the minimalism–maximalism stylistic continuum.</p>
276
+ </div>
277
+ <div>
278
+ <h3>Interpreting the Style Score</h3>
279
+ <p>The predicted Style Score indicates a design’s position along the minimalism–maximalism stylistic continuum. Increasingly negative scores indicate stronger stylistic commitment to minimalism, whereas increasingly positive scores indicate stronger stylistic commitment to maximalism. Scores closer to zero reflect a more moderate position along the continuum, corresponding to stylistically compromised designs that do not clearly express either style. Because the scores are mean-centered, zero represents the mean of the expert ratings used to train the model rather than an absolute boundary between stylistic categories.</p>
280
+ </div>
281
+ </div>
282
+ </section>
283
+ </main>
284
+ </div>
285
+
286
+ <script type="module">
287
+ // The original Space hosts the trained model. This page changes only the interface.
288
+ import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
289
+
290
+ const MODEL_SPACE = "ThisIsAnAnonymousUser/Minimal-Maximal.7.0";
291
+ const API_ENDPOINT = "/predict";
292
+ // This is a visualization window, not a bound on the model's possible outputs.
293
+ const DISPLAY_HALF_RANGE = 3.454;
294
+
295
+ const dropzone = document.getElementById("dropzone");
296
+ const input = document.getElementById("image-file");
297
+ const preview = document.getElementById("image-preview");
298
+ const filename = document.getElementById("filename");
299
+ const button = document.getElementById("analyze-button");
300
+ const status = document.getElementById("status");
301
+ const empty = document.getElementById("empty-result");
302
+ const results = document.getElementById("results");
303
+ const styleScore = document.getElementById("style-score");
304
+ const expertRating = document.getElementById("expert-rating");
305
+ const marker = document.getElementById("marker");
306
+ const outside = document.getElementById("outside");
307
+
308
+ let selectedFile = null;
309
+ let previewUrl = null;
310
+ let clientPromise = null;
311
+ let isAnalyzing = false;
312
+
313
+ function clearPrediction() {
314
+ results.style.display = "none";
315
+ empty.style.display = "flex";
316
+ styleScore.textContent = "—";
317
+ expertRating.textContent = "—";
318
+ }
319
+
320
+ function selectImage(file) {
321
+ if (!file || isAnalyzing) return;
322
+ const allowedTypes = new Set(["image/png", "image/jpeg", "image/webp", "image/bmp", "image/gif"]);
323
+ if (!allowedTypes.has(file.type)) {
324
+ status.textContent = "Please select a PNG, JPG, WebP, BMP, or GIF image.";
325
+ status.classList.add("error");
326
+ return;
327
+ }
328
+ selectedFile = file;
329
+ if (previewUrl) URL.revokeObjectURL(previewUrl);
330
+ previewUrl = URL.createObjectURL(file);
331
+ preview.src = previewUrl;
332
+ filename.textContent = file.name;
333
+ dropzone.classList.add("has-image");
334
+ status.textContent = "";
335
+ status.classList.remove("error");
336
+ clearPrediction();
337
+ button.disabled = isAnalyzing;
338
+ }
339
+
340
+ dropzone.addEventListener("click", (event) => {
341
+ // Ignore the click generated by input.click() so it does not bubble back.
342
+ if (event.target !== input && !isAnalyzing) input.click();
343
+ });
344
+ dropzone.addEventListener("keydown", (event) => {
345
+ if (event.key === "Enter" || event.key === " ") {
346
+ event.preventDefault();
347
+ if (!isAnalyzing) input.click();
348
+ }
349
+ });
350
+ input.addEventListener("change", () => selectImage(input.files?.[0]));
351
+ dropzone.addEventListener("dragover", (event) => { event.preventDefault(); dropzone.classList.add("dragging"); });
352
+ dropzone.addEventListener("dragleave", () => dropzone.classList.remove("dragging"));
353
+ dropzone.addEventListener("drop", (event) => {
354
+ event.preventDefault();
355
+ dropzone.classList.remove("dragging");
356
+ selectImage(event.dataTransfer.files?.[0]);
357
+ });
358
+
359
+ button.addEventListener("click", async () => {
360
+ if (!selectedFile || isAnalyzing) return;
361
+ const submittedFile = selectedFile;
362
+ isAnalyzing = true;
363
+ button.disabled = true;
364
+ button.textContent = "Analyzing…";
365
+ status.textContent = "Connecting to the model and analyzing your image…";
366
+ status.classList.remove("error");
367
+ try {
368
+ if (!clientPromise) clientPromise = Client.connect(MODEL_SPACE);
369
+ const client = await clientPromise;
370
+ // The API expects an image blob in the parameter named "img".
371
+ const response = await client.predict(API_ENDPOINT, { img: submittedFile });
372
+ // From the documented /predict API:
373
+ // [0] estimated expert rating (1–7), [1] mean-centered style score,
374
+ // [2] original HTML visualization (unused by the redesigned UI).
375
+ const values = response.data;
376
+ if (!Array.isArray(values) || values.length < 2 || values[0] == null || values[1] == null) {
377
+ throw new Error("The model returned an unexpected response.");
378
+ }
379
+ const expert = Number(values[0]);
380
+ const centered = Number(values[1]);
381
+ if (!Number.isFinite(expert) || !Number.isFinite(centered)) {
382
+ throw new Error("The model returned a non-numeric prediction.");
383
+ }
384
+ // Do not change or recompute model outputs; round only the visible labels.
385
+ styleScore.textContent = centered.toFixed(2);
386
+ expertRating.textContent = expert.toFixed(2);
387
+ const pct = Math.max(0, Math.min(100, ((centered + DISPLAY_HALF_RANGE) / (2 * DISPLAY_HALF_RANGE)) * 100));
388
+ marker.style.left = `${pct}%`;
389
+ outside.style.display = Math.abs(centered) > DISPLAY_HALF_RANGE ? "block" : "none";
390
+ empty.style.display = "none";
391
+ results.style.display = "block";
392
+ status.textContent = "Analysis complete.";
393
+ } catch (error) {
394
+ clientPromise = null; // allow a fresh connection on retry
395
+ status.textContent = "Prediction unavailable. Please retry; the model may be restarting or temporarily offline.";
396
+ status.classList.add("error");
397
+ console.error("Model prediction failed:", error);
398
+ } finally {
399
+ isAnalyzing = false;
400
+ button.disabled = !selectedFile;
401
+ button.textContent = "Analyze Design";
402
+ }
403
+ });
404
+ </script>
405
+ </body>
406
  </html>