@digital-ai/dot-illustrations 2.0.36 → 2.0.38
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.
- package/demo/demo.css +37 -183
- package/demo/github-upload.js +39 -4
- package/demo/index.html +251 -171
- package/demo/local-proxy.js +90 -0
- package/demo/script.js +170 -89
- package/package.json +1 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* local-proxy.js — GitHub API proxy for local demo development
|
|
4
|
+
*
|
|
5
|
+
* Uses your existing `gh auth` credentials — no token input needed.
|
|
6
|
+
* Run from the repo root: node demo/local-proxy.js
|
|
7
|
+
*
|
|
8
|
+
* The demo page detects this proxy at http://localhost:3001 and
|
|
9
|
+
* authenticates automatically using your gh CLI identity.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const http = require('http');
|
|
15
|
+
const https = require('https');
|
|
16
|
+
const { execSync } = require('child_process');
|
|
17
|
+
|
|
18
|
+
const PROXY_PORT = 3001;
|
|
19
|
+
|
|
20
|
+
// Resolve the gh CLI token once at startup
|
|
21
|
+
let GH_TOKEN, GH_USER;
|
|
22
|
+
try {
|
|
23
|
+
GH_TOKEN = execSync('gh auth token', { stdio: ['pipe','pipe','pipe'] }).toString().trim();
|
|
24
|
+
GH_USER = execSync('gh api user --jq .login', { stdio: ['pipe','pipe','pipe'] }).toString().trim();
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error('❌ gh CLI is not authenticated. Run: gh auth login --web');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const server = http.createServer((req, res) => {
|
|
31
|
+
// Allow the demo page (any localhost origin) to call this proxy
|
|
32
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
33
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
34
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
35
|
+
|
|
36
|
+
if (req.method === 'OPTIONS') {
|
|
37
|
+
res.writeHead(204);
|
|
38
|
+
res.end();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Special: /whoami — tells the demo page who is logged in
|
|
43
|
+
if (req.url === '/whoami') {
|
|
44
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
45
|
+
res.end(JSON.stringify({ login: GH_USER, proxy: true }));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Proxy everything else to api.github.com
|
|
50
|
+
let body = '';
|
|
51
|
+
req.on('data', chunk => { body += chunk; });
|
|
52
|
+
req.on('end', () => {
|
|
53
|
+
const options = {
|
|
54
|
+
hostname: 'api.github.com',
|
|
55
|
+
path: req.url,
|
|
56
|
+
method: req.method,
|
|
57
|
+
headers: {
|
|
58
|
+
'Authorization': `Bearer ${GH_TOKEN}`,
|
|
59
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
60
|
+
'Content-Type': 'application/json',
|
|
61
|
+
'User-Agent': 'dot-illustrations-local-proxy',
|
|
62
|
+
...(body ? { 'Content-Length': Buffer.byteLength(body) } : {}),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const proxyReq = https.request(options, proxyRes => {
|
|
67
|
+
res.writeHead(proxyRes.statusCode, {
|
|
68
|
+
'Content-Type': proxyRes.headers['content-type'] || 'application/json',
|
|
69
|
+
'Access-Control-Allow-Origin': '*',
|
|
70
|
+
});
|
|
71
|
+
proxyRes.pipe(res);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
proxyReq.on('error', err => {
|
|
75
|
+
res.writeHead(502);
|
|
76
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (body) proxyReq.write(body);
|
|
80
|
+
proxyReq.end();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
server.listen(PROXY_PORT, '127.0.0.1', () => {
|
|
85
|
+
console.log(`✅ GitHub proxy running at http://localhost:${PROXY_PORT}`);
|
|
86
|
+
console.log(`👤 Authenticated as: @${GH_USER}`);
|
|
87
|
+
console.log(`\n Open the demo: http://localhost:5175/demo/`);
|
|
88
|
+
console.log(' The demo will connect automatically — no token input needed.\n');
|
|
89
|
+
console.log(' Press Ctrl+C to stop.\n');
|
|
90
|
+
});
|
package/demo/script.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Make currentTab and root globally accessible
|
|
2
2
|
const root = document.documentElement;
|
|
3
|
-
let
|
|
4
|
-
let
|
|
3
|
+
let currentSection = 'illustrations'; // 'illustrations' | 'integrations' | 'upload'
|
|
4
|
+
let currentTab = 'all';
|
|
5
|
+
let alphabetFilter = '';
|
|
5
6
|
|
|
6
7
|
function myFunction() {
|
|
7
8
|
var input, filter, divs, i, div, txtValue;
|
|
@@ -116,48 +117,117 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
116
117
|
const mainTabs = document.querySelectorAll('.tab-btn');
|
|
117
118
|
const floatingTabs = document.querySelectorAll('.floating-tab');
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
120
|
+
// ── Upload New button (in header) ────────────────────────────────────────
|
|
121
|
+
// ── Upload New: type picker → form ──────────────────────────────────────
|
|
122
|
+
function showUploadStep(step) {
|
|
123
|
+
// step: 'picker' | 'illustration' | 'integration'
|
|
124
|
+
document.getElementById('upload-type-picker').classList.toggle('hidden', step !== 'picker');
|
|
125
|
+
document.getElementById('upload-illus-panel').classList.toggle('hidden', step !== 'illustration');
|
|
126
|
+
document.getElementById('upload-integ-panel').classList.toggle('hidden', step !== 'integration');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function openUploadModal() {
|
|
130
|
+
const panel = document.getElementById('upload-panel');
|
|
131
|
+
panel.classList.remove('hidden');
|
|
132
|
+
showUploadStep('picker');
|
|
133
|
+
updateUploadAuthUI();
|
|
134
|
+
document.getElementById('upload-new-btn').classList.add('ring-2','ring-blue-400','bg-blue-700');
|
|
135
|
+
}
|
|
136
|
+
function closeUploadModal() {
|
|
137
|
+
document.getElementById('upload-panel').classList.add('hidden');
|
|
138
|
+
document.getElementById('upload-new-btn').classList.remove('ring-2','ring-blue-400','bg-blue-700');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
document.getElementById('upload-new-btn')?.addEventListener('click', openUploadModal);
|
|
142
|
+
|
|
143
|
+
// Backdrop click does NOT close — use X button or Escape
|
|
144
|
+
|
|
145
|
+
// Close on X button
|
|
146
|
+
document.getElementById('upload-modal-close')?.addEventListener('click', closeUploadModal);
|
|
147
|
+
|
|
148
|
+
// Close on Escape
|
|
149
|
+
document.addEventListener('keydown', e => {
|
|
150
|
+
if (e.key === 'Escape' && !document.getElementById('upload-panel').classList.contains('hidden')) closeUploadModal();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
document.getElementById('pick-illustration')?.addEventListener('click', () => { showUploadStep('illustration'); updateUploadAuthUI(); });
|
|
154
|
+
document.getElementById('pick-integration')?.addEventListener('click', () => { showUploadStep('integration'); updateUploadAuthUI(); });
|
|
155
|
+
document.getElementById('back-from-illus')?.addEventListener('click', () => showUploadStep('picker'));
|
|
156
|
+
document.getElementById('back-from-integ')?.addEventListener('click', () => showUploadStep('picker'));
|
|
157
|
+
|
|
158
|
+
// ── Section switcher (Illustrations | Integrations) — pill style ──────────
|
|
159
|
+
function updateSectionUI(section) {
|
|
160
|
+
document.querySelectorAll('.section-btn, .floating-section-btn').forEach(btn => {
|
|
161
|
+
const active = btn.dataset.section === section;
|
|
162
|
+
// Active pill: white card on gray background
|
|
163
|
+
btn.classList.toggle('bg-white', active);
|
|
164
|
+
btn.classList.toggle('dark:bg-gray-700', active);
|
|
165
|
+
btn.classList.toggle('text-gray-900', active);
|
|
166
|
+
btn.classList.toggle('dark:text-white', active);
|
|
167
|
+
btn.classList.toggle('shadow-sm', active);
|
|
168
|
+
// Inactive: transparent
|
|
169
|
+
btn.classList.toggle('text-gray-500', !active);
|
|
170
|
+
btn.classList.toggle('dark:text-gray-400', !active);
|
|
129
171
|
});
|
|
130
172
|
}
|
|
131
173
|
|
|
132
|
-
function
|
|
133
|
-
|
|
174
|
+
function setSection(section) {
|
|
175
|
+
currentSection = section;
|
|
134
176
|
alphabetFilter = '';
|
|
135
|
-
|
|
177
|
+
updateSectionUI(section);
|
|
136
178
|
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
grid.classList.add('hidden');
|
|
145
|
-
alphaStrip.classList.add('hidden');
|
|
146
|
-
integNote.classList.add('hidden');
|
|
147
|
-
document.getElementById('result-count').textContent = '';
|
|
148
|
-
updateUploadAuthUI();
|
|
149
|
-
} else {
|
|
150
|
-
uploadPanel.classList.add('hidden');
|
|
179
|
+
const grid = document.getElementById('illustration-list');
|
|
180
|
+
const alphaStrip = document.getElementById('alphabet-strip');
|
|
181
|
+
const illusSubTabs = document.getElementById('illus-sub-tabs');
|
|
182
|
+
const floatingTabs = document.getElementById('floating-tabs');
|
|
183
|
+
const resultCount = document.getElementById('result-count');
|
|
184
|
+
|
|
185
|
+
if (section === 'integrations') {
|
|
151
186
|
grid.classList.remove('hidden');
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
187
|
+
illusSubTabs.classList.add('hidden');
|
|
188
|
+
if (floatingTabs) floatingTabs.classList.add('hidden');
|
|
189
|
+
// Alphabet strip — show as flex for integrations (110 items benefit from A–Z jump)
|
|
190
|
+
alphaStrip.style.display = 'flex';
|
|
156
191
|
renderAlphabetStrip();
|
|
157
192
|
renderIllustrations();
|
|
193
|
+
} else {
|
|
194
|
+
// Illustrations — search is sufficient for 72 items; hide alphabet strip
|
|
195
|
+
grid.classList.remove('hidden');
|
|
196
|
+
illusSubTabs.classList.remove('hidden');
|
|
197
|
+
if (floatingTabs) floatingTabs.classList.remove('hidden');
|
|
198
|
+
alphaStrip.style.display = 'none';
|
|
199
|
+
alphabetFilter = '';
|
|
200
|
+
renderIllustrations();
|
|
158
201
|
}
|
|
159
202
|
}
|
|
160
203
|
|
|
204
|
+
document.querySelectorAll('.section-btn, .floating-section-btn').forEach(btn => {
|
|
205
|
+
btn.addEventListener('click', () => setSection(btn.dataset.section));
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// ── Illustration sub-tabs — pill style ───────────────────────────────────
|
|
209
|
+
function updateTabUI(tab) {
|
|
210
|
+
document.querySelectorAll('.tab-btn').forEach(t => {
|
|
211
|
+
const active = t.dataset.tab === tab;
|
|
212
|
+
t.classList.toggle('bg-white', active);
|
|
213
|
+
t.classList.toggle('dark:bg-gray-700', active);
|
|
214
|
+
t.classList.toggle('text-gray-900', active);
|
|
215
|
+
t.classList.toggle('dark:text-white', active);
|
|
216
|
+
t.classList.toggle('shadow-sm', active);
|
|
217
|
+
t.classList.toggle('text-gray-500', !active);
|
|
218
|
+
t.classList.toggle('dark:text-gray-400', !active);
|
|
219
|
+
t.setAttribute('aria-selected', active ? 'true' : 'false');
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function setTab(tab) {
|
|
224
|
+
currentTab = tab;
|
|
225
|
+
alphabetFilter = '';
|
|
226
|
+
updateTabUI(tab);
|
|
227
|
+
renderAlphabetStrip();
|
|
228
|
+
renderIllustrations();
|
|
229
|
+
}
|
|
230
|
+
|
|
161
231
|
function wireTabs() {
|
|
162
232
|
document.querySelectorAll('.tab-btn').forEach(tab => {
|
|
163
233
|
tab.addEventListener('click', function() { setTab(this.dataset.tab); });
|
|
@@ -311,7 +381,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
311
381
|
|
|
312
382
|
let imgHtml, dotCodeString, codeString;
|
|
313
383
|
if (isInteg) {
|
|
314
|
-
imgHtml = `<
|
|
384
|
+
imgHtml = `<img src="../integrations/${item}.svg" alt="${item}" style="width:160px;height:160px;object-fit:contain;" />`;
|
|
315
385
|
dotCodeString = `<span class="dot-integration"><img class="${item}"/></span>`;
|
|
316
386
|
codeString = dotCodeString;
|
|
317
387
|
} else {
|
|
@@ -368,14 +438,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
368
438
|
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
|
|
369
439
|
|
|
370
440
|
strip.innerHTML = `
|
|
371
|
-
<button class="alpha-btn px-
|
|
441
|
+
<button class="alpha-btn text-xs font-medium px-1.5 py-0.5 rounded-md transition-all ${
|
|
442
|
+
alphabetFilter === ''
|
|
443
|
+
? 'text-blue-600 dark:text-blue-400 font-semibold'
|
|
444
|
+
: 'text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'
|
|
445
|
+
}" data-letter="">All</button>
|
|
446
|
+
<span class="text-gray-200 dark:text-gray-700 text-xs select-none">|</span>
|
|
372
447
|
${letters.map(l => `
|
|
373
|
-
<button class="alpha-btn px-
|
|
448
|
+
<button class="alpha-btn text-xs font-medium px-1.5 py-0.5 rounded-md transition-all ${
|
|
374
449
|
available.has(l)
|
|
375
450
|
? alphabetFilter === l
|
|
376
|
-
? '
|
|
377
|
-
: '
|
|
378
|
-
: '
|
|
451
|
+
? 'text-blue-600 dark:text-blue-400 font-semibold bg-blue-50 dark:bg-blue-900/30'
|
|
452
|
+
: 'text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-800'
|
|
453
|
+
: 'text-gray-200 dark:text-gray-700 cursor-default'
|
|
379
454
|
}" data-letter="${l}" ${available.has(l) ? '' : 'disabled'}>${l}</button>
|
|
380
455
|
`).join('')}
|
|
381
456
|
`;
|
|
@@ -391,11 +466,15 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
391
466
|
function getCurrentPool(filterOverride) {
|
|
392
467
|
const f = filterOverride !== undefined ? filterOverride : alphabetFilter;
|
|
393
468
|
let base = [];
|
|
394
|
-
if (
|
|
395
|
-
|
|
396
|
-
else
|
|
397
|
-
|
|
398
|
-
|
|
469
|
+
if (currentSection === 'integrations') {
|
|
470
|
+
base = integrationsList;
|
|
471
|
+
} else {
|
|
472
|
+
// illustrations section
|
|
473
|
+
if (currentTab === 'all') base = illustrations;
|
|
474
|
+
else if (currentTab === 'global') base = globalList;
|
|
475
|
+
else if (currentTab === 'dashboards') base = dashboardsList;
|
|
476
|
+
else if (currentTab === 'favourites') base = illustrations.filter(id => getFavourites().includes(id));
|
|
477
|
+
}
|
|
399
478
|
if (f) base = base.filter(id => id[0].toUpperCase() === f);
|
|
400
479
|
return base;
|
|
401
480
|
}
|
|
@@ -417,8 +496,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
417
496
|
return;
|
|
418
497
|
}
|
|
419
498
|
|
|
420
|
-
const isIntegrations =
|
|
421
|
-
(currentTab === 'favourites' && toShow.every(id => integrationsList.includes(id)));
|
|
499
|
+
const isIntegrations = currentSection === 'integrations';
|
|
422
500
|
const favs = getFavourites();
|
|
423
501
|
const themeClass = root.classList.contains('dark') ? 'dark' : 'light';
|
|
424
502
|
|
|
@@ -427,57 +505,54 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
427
505
|
const isFav = favs.includes(item);
|
|
428
506
|
|
|
429
507
|
if (isInteg) {
|
|
430
|
-
// Integration card
|
|
431
|
-
|
|
508
|
+
// Integration card — use direct <img src> so the image always loads
|
|
509
|
+
// ── Integration card ─────────────────────────────────────────────
|
|
510
|
+
const htmlCode = `<span class="dot-integration"><img class="${item}"/></span>`;
|
|
432
511
|
const div = document.createElement('div');
|
|
433
|
-
div.
|
|
434
|
-
div.dataset.itemId
|
|
512
|
+
div.className = 'copy-container group relative bg-white dark:bg-gray-900 rounded-2xl overflow-hidden cursor-pointer shadow-sm transition-all duration-200 hover:shadow-md hover:ring-2 hover:ring-blue-400/30 dark:hover:ring-blue-500/30';
|
|
513
|
+
div.dataset.itemId = item;
|
|
435
514
|
div.dataset.itemType = 'integration';
|
|
436
515
|
div.innerHTML = `
|
|
437
|
-
<button class="absolute top-2 right-2 z-
|
|
438
|
-
<svg class="
|
|
516
|
+
<button class="fav-btn absolute top-2.5 right-2.5 z-20 opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded-lg bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm shadow-sm" onclick="event.stopPropagation();window.toggleFavourite('${item}')">
|
|
517
|
+
<svg class="w-3.5 h-3.5" fill="${isFav ? '#FBBF24' : 'none'}" viewBox="0 0 24 24" stroke="${isFav ? '#FBBF24' : '#9CA3AF'}" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l2.036 6.29a1 1 0 00.95.69h6.631c.969 0 1.371 1.24.588 1.81l-5.37 3.905a1 1 0 00-.364 1.118l2.036 6.29c.3.921-.755 1.688-1.54 1.118l-5.37-3.905a1 1 0 00-1.176 0l-5.37 3.905c-.784.57-1.838-.197-1.54-1.118l2.036-6.29a1 1 0 00-.364-1.118L2.342 11.717c-.783-.57-.38-1.81.588-1.81h6.631a1 1 0 00.95-.69l2.036-6.29z"/></svg>
|
|
439
518
|
</button>
|
|
440
|
-
<div class="flex
|
|
441
|
-
<
|
|
442
|
-
|
|
519
|
+
<div class="flex items-center justify-center h-36 px-4">
|
|
520
|
+
<img src="../integrations/${item}.svg" alt="${item}" class="w-[80px] h-[80px] object-contain" loading="lazy" />
|
|
521
|
+
</div>
|
|
522
|
+
<div class="px-3 py-3">
|
|
523
|
+
<p class="iconID text-[11px] text-center text-gray-500 dark:text-gray-400 font-medium leading-tight truncate">${item}</p>
|
|
443
524
|
</div>
|
|
444
|
-
<div class="
|
|
445
|
-
<button class="button flex
|
|
446
|
-
|
|
447
|
-
</button>
|
|
448
|
-
<button class="button flex items-center justify-center min-w-[80px] px-1.5 py-0.5 text-[11px] text-gray-400 border border-gray-300 dark:border-gray-700 rounded bg-white dark:bg-gray-800 font-normal hover:bg-blue-100 dark:hover:bg-blue-900 transition relative text-center" onmouseover="showTooltip(this)" onmouseout="hideTooltip(this)" onclick="copyCode(this)">
|
|
449
|
-
Copy Code<span class="tooltiptext left-1/2 -translate-x-1/2 bottom-10 shadow-md">Copy Code</span>
|
|
450
|
-
</button>
|
|
525
|
+
<div class="action-bar absolute bottom-0 inset-x-0 bg-white/97 dark:bg-gray-900/97 backdrop-blur-sm border-t border-gray-100 dark:border-gray-800 p-2 flex gap-1.5 translate-y-full group-hover:translate-y-0 transition-transform duration-150 ease-out">
|
|
526
|
+
<button class="button flex-1 py-1.5 text-[10px] font-semibold text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/30 rounded-lg hover:bg-blue-100 dark:hover:bg-blue-900/60 transition" onclick="event.stopPropagation();copyText(this)">Dot Code</button>
|
|
527
|
+
<button class="button flex-1 py-1.5 text-[10px] font-semibold text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-800 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition" onclick="event.stopPropagation();copyCode(this)">HTML</button>
|
|
451
528
|
</div>
|
|
452
|
-
<pre class="codeBlock"><code class="codeBlock">${
|
|
529
|
+
<pre class="codeBlock hidden"><code class="codeBlock">${htmlCode}</code></pre>
|
|
453
530
|
`;
|
|
454
531
|
list.appendChild(div);
|
|
455
532
|
} else {
|
|
456
|
-
// Illustration card
|
|
533
|
+
// ── Illustration card ─────────────────────────────────────────────
|
|
457
534
|
let categoryFolder = globalList.includes(item) ? 'global' : 'dashboards';
|
|
458
|
-
const svgPath
|
|
535
|
+
const svgPath = `illustrations/${themeClass}/${categoryFolder}/${item}.svg`;
|
|
459
536
|
const codeString = `<span class="dot-illustration"><img src="${svgPath}" class="${item} ${themeClass}"/></span>`;
|
|
460
537
|
const div = document.createElement('div');
|
|
461
|
-
div.
|
|
462
|
-
div.dataset.itemId
|
|
538
|
+
div.className = 'copy-container group relative bg-white dark:bg-gray-900 rounded-2xl overflow-hidden cursor-pointer shadow-sm transition-all duration-200 hover:shadow-md hover:ring-2 hover:ring-blue-400/30 dark:hover:ring-blue-500/30';
|
|
539
|
+
div.dataset.itemId = item;
|
|
463
540
|
div.dataset.itemType = 'illustration';
|
|
464
541
|
div.innerHTML = `
|
|
465
|
-
<button class="absolute top-2 right-2 z-
|
|
466
|
-
<svg class="
|
|
542
|
+
<button class="fav-btn absolute top-2.5 right-2.5 z-20 opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded-lg bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm shadow-sm" onclick="event.stopPropagation();window.toggleFavourite('${item}')">
|
|
543
|
+
<svg class="w-3.5 h-3.5" fill="${isFav ? '#FBBF24' : 'none'}" viewBox="0 0 24 24" stroke="${isFav ? '#FBBF24' : '#9CA3AF'}" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l2.036 6.29a1 1 0 00.95.69h6.631c.969 0 1.371 1.24.588 1.81l-5.37 3.905a1 1 0 00-.364 1.118l2.036 6.29c.3.921-.755 1.688-1.54 1.118l-5.37-3.905a1 1 0 00-1.176 0l-5.37 3.905c-.784.57-1.838-.197-1.54-1.118l2.036-6.29a1 1 0 00-.364-1.118L2.342 11.717c-.783-.57-.38-1.81.588-1.81h6.631a1 1 0 00.95-.69l2.036-6.29z"/></svg>
|
|
467
544
|
</button>
|
|
468
|
-
<div class="flex
|
|
469
|
-
<
|
|
470
|
-
|
|
545
|
+
<div class="flex items-center justify-center h-36 px-4 dot-illustration">
|
|
546
|
+
<img src="${svgPath}" class="${item} ${themeClass}" style="width:120px;height:120px;object-fit:contain;display:block;" />
|
|
547
|
+
</div>
|
|
548
|
+
<div class="px-3 py-3">
|
|
549
|
+
<p class="iconID text-[11px] text-center text-gray-500 dark:text-gray-400 font-medium leading-tight break-words">${item}</p>
|
|
471
550
|
</div>
|
|
472
|
-
<div class="
|
|
473
|
-
<button
|
|
474
|
-
|
|
475
|
-
</button>
|
|
476
|
-
<button class="button flex items-center justify-center min-w-[80px] px-1.5 py-0.5 text-[11px] text-gray-400 border border-gray-300 dark:border-gray-700 rounded bg-white dark:bg-gray-800 font-normal hover:bg-blue-100 dark:hover:bg-blue-900 transition relative text-center" onmouseover="showTooltip(this)" onmouseout="hideTooltip(this)" onclick="copyCode(this)">
|
|
477
|
-
Copy Code<span class="tooltiptext left-1/2 -translate-x-1/2 bottom-10 shadow-md">Copy Code</span>
|
|
478
|
-
</button>
|
|
551
|
+
<div class="action-bar absolute bottom-0 inset-x-0 bg-white/97 dark:bg-gray-900/97 backdrop-blur-sm border-t border-gray-100 dark:border-gray-800 p-2 flex gap-1.5 translate-y-full group-hover:translate-y-0 transition-transform duration-150 ease-out">
|
|
552
|
+
<button class="button flex-1 py-1.5 text-[10px] font-semibold text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/30 rounded-lg hover:bg-blue-100 dark:hover:bg-blue-900/60 transition" onclick="event.stopPropagation();copyText(this)">Dot Code</button>
|
|
553
|
+
<button class="button flex-1 py-1.5 text-[10px] font-semibold text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-800 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition" onclick="event.stopPropagation();copyCode(this)">HTML</button>
|
|
479
554
|
</div>
|
|
480
|
-
<pre class="codeBlock"><code class="codeBlock">${codeString}</code></pre>
|
|
555
|
+
<pre class="codeBlock hidden"><code class="codeBlock">${codeString}</code></pre>
|
|
481
556
|
`;
|
|
482
557
|
list.appendChild(div);
|
|
483
558
|
}
|
|
@@ -590,7 +665,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
590
665
|
}
|
|
591
666
|
});
|
|
592
667
|
|
|
593
|
-
//
|
|
668
|
+
// Auto-connect via local proxy (node demo/local-proxy.js) — no token input needed
|
|
669
|
+
window.GitHubUpload?.tryLocalProxy?.().then(user => {
|
|
670
|
+
if (user) { updateAuthBadge(user); updateUploadAuthUI(); }
|
|
671
|
+
});
|
|
594
672
|
|
|
595
673
|
// ── Upload panel ─────────────────────────────────────────────────────────
|
|
596
674
|
const uploadBufs = { illusLight: null, illusDark: null, integSvg: null };
|
|
@@ -678,8 +756,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
678
756
|
document.getElementById('upload-illus-btn')?.addEventListener('click', handleIllusUpload);
|
|
679
757
|
document.getElementById('upload-integ-btn')?.addEventListener('click', handleIntegUpload);
|
|
680
758
|
|
|
681
|
-
//
|
|
682
|
-
|
|
759
|
+
// Initial render — start on illustrations, alphabet hidden
|
|
760
|
+
const strip = document.getElementById('alphabet-strip');
|
|
761
|
+
if (strip) strip.style.display = 'none';
|
|
762
|
+
updateSectionUI('illustrations');
|
|
763
|
+
updateTabUI('all');
|
|
764
|
+
renderIllustrations();
|
|
683
765
|
|
|
684
766
|
// --- FLOATING BAR LOGIC ---
|
|
685
767
|
const floatingBar = document.getElementById('floating-bar');
|
|
@@ -709,13 +791,12 @@ function fuzzyMatch(str, query) {
|
|
|
709
791
|
|
|
710
792
|
function getSuggestions(query) {
|
|
711
793
|
if (!query) return [];
|
|
712
|
-
// Use current tab's list
|
|
713
794
|
let pool = [];
|
|
714
|
-
if (
|
|
715
|
-
else if (currentTab === '
|
|
716
|
-
else if (currentTab === '
|
|
717
|
-
else if (currentTab === '
|
|
718
|
-
else if (currentTab === 'favourites')
|
|
795
|
+
if (currentSection === 'integrations') pool = integrationsList;
|
|
796
|
+
else if (currentTab === 'all') pool = illustrations;
|
|
797
|
+
else if (currentTab === 'global') pool = globalList;
|
|
798
|
+
else if (currentTab === 'dashboards') pool = dashboardsList;
|
|
799
|
+
else if (currentTab === 'favourites') pool = illustrations.filter(id => getFavourites().includes(id));
|
|
719
800
|
// Fuzzy match, sort by closeness (shorter index of first match, then length)
|
|
720
801
|
let scored = pool
|
|
721
802
|
.map(id => {
|
package/package.json
CHANGED