@digital-ai/dot-illustrations 2.0.32 → 2.0.35
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/.github/workflows/publish.yml +21 -10
- package/demo/demo.css +27 -0
- package/demo/github-upload.js +289 -0
- package/demo/index.html +158 -19
- package/demo/script.js +396 -120
- package/illustrations/dark/global/delete.svg +17 -0
- package/illustrations/dark/global/remote-cloud.svg +37 -0
- package/illustrations/light/global/delete.svg +17 -0
- package/illustrations/light/global/remote-cloud.svg +37 -0
- package/index.css +16 -0
- package/package.json +1 -1
package/demo/script.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Make currentTab and root globally accessible
|
|
2
2
|
const root = document.documentElement;
|
|
3
|
-
let currentTab
|
|
3
|
+
let currentTab = 'all';
|
|
4
|
+
let alphabetFilter = ''; // '' = show all letters
|
|
4
5
|
|
|
5
6
|
function myFunction() {
|
|
6
7
|
var input, filter, divs, i, div, txtValue;
|
|
@@ -69,20 +70,18 @@ window.copyCode = function(button) {
|
|
|
69
70
|
const origCopyText = window.copyText;
|
|
70
71
|
window.copyText = function(button) {
|
|
71
72
|
var parentDiv = button.closest('.copy-container');
|
|
72
|
-
var
|
|
73
|
-
var
|
|
74
|
-
var
|
|
73
|
+
var idEl = parentDiv.querySelector('.iconID');
|
|
74
|
+
var itemId = idEl.textContent;
|
|
75
|
+
var isInteg = integrationsList && integrationsList.includes(itemId) && !globalList.concat(dashboardsList).includes(itemId);
|
|
76
|
+
var dotCode = isInteg
|
|
77
|
+
? `<span class="dot-integration"><img class="${itemId}"/></span>`
|
|
78
|
+
: `<DotIllustration illustrationId="${itemId}" />`;
|
|
75
79
|
navigator.clipboard.writeText(dotCode).then(function() {
|
|
76
80
|
var tooltip = button.querySelector('.tooltiptext');
|
|
77
81
|
tooltip.innerHTML = "Copied Dot Code";
|
|
78
82
|
tooltip.style.visibility = 'visible';
|
|
79
|
-
setTimeout(function() {
|
|
80
|
-
tooltip.innerHTML = "Copy Dot Code";
|
|
81
|
-
tooltip.style.visibility = 'hidden';
|
|
82
|
-
}, 2000);
|
|
83
|
+
setTimeout(function() { tooltip.innerHTML = "Copy Dot Code"; tooltip.style.visibility = 'hidden'; }, 2000);
|
|
83
84
|
showToast('Copied Dot Code: ' + dotCode);
|
|
84
|
-
}, function(err) {
|
|
85
|
-
console.error('Failed to copy dot code: ', err);
|
|
86
85
|
});
|
|
87
86
|
}
|
|
88
87
|
|
|
@@ -117,43 +116,54 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
117
116
|
const mainTabs = document.querySelectorAll('.tab-btn');
|
|
118
117
|
const floatingTabs = document.querySelectorAll('.floating-tab');
|
|
119
118
|
|
|
119
|
+
function updateTabUI(tab) {
|
|
120
|
+
[...mainTabs, ...floatingTabs].forEach(t => {
|
|
121
|
+
const isActive = t.dataset.tab === tab;
|
|
122
|
+
t.classList.toggle('text-blue-600', isActive);
|
|
123
|
+
t.classList.toggle('dark:text-blue-400', isActive);
|
|
124
|
+
t.classList.toggle('bg-blue-100', isActive);
|
|
125
|
+
t.classList.toggle('dark:bg-blue-900', isActive);
|
|
126
|
+
t.classList.toggle('text-gray-700', !isActive && t.dataset.tab !== 'upload');
|
|
127
|
+
t.classList.toggle('dark:text-gray-300', !isActive && t.dataset.tab !== 'upload');
|
|
128
|
+
t.setAttribute('aria-selected', isActive ? 'true' : 'false');
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
120
132
|
function setTab(tab) {
|
|
121
133
|
currentTab = tab;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
134
|
+
alphabetFilter = '';
|
|
135
|
+
updateTabUI(tab);
|
|
136
|
+
|
|
137
|
+
const uploadPanel = document.getElementById('upload-panel');
|
|
138
|
+
const grid = document.getElementById('illustration-list');
|
|
139
|
+
const alphaStrip = document.getElementById('alphabet-strip');
|
|
140
|
+
const integNote = document.getElementById('integrations-note');
|
|
141
|
+
|
|
142
|
+
if (tab === 'upload') {
|
|
143
|
+
uploadPanel.classList.remove('hidden');
|
|
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');
|
|
151
|
+
grid.classList.remove('hidden');
|
|
152
|
+
// Show alphabet strip only for tabs that have many items
|
|
153
|
+
const showStrip = ['all','global','dashboards','integrations','favourites'].includes(tab);
|
|
154
|
+
alphaStrip.classList.toggle('hidden', !showStrip);
|
|
155
|
+
integNote.classList.toggle('hidden', tab !== 'integrations');
|
|
156
|
+
renderAlphabetStrip();
|
|
157
|
+
renderIllustrations();
|
|
158
|
+
}
|
|
145
159
|
}
|
|
146
160
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
setTab(this.dataset.tab);
|
|
161
|
+
function wireTabs() {
|
|
162
|
+
document.querySelectorAll('.tab-btn').forEach(tab => {
|
|
163
|
+
tab.addEventListener('click', function() { setTab(this.dataset.tab); });
|
|
150
164
|
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
tab.addEventListener('click', function() {
|
|
154
|
-
setTab(this.dataset.tab);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
165
|
+
}
|
|
166
|
+
wireTabs();
|
|
157
167
|
|
|
158
168
|
// --- ILLUSTRATION DATA ---
|
|
159
169
|
const globalList = [
|
|
@@ -168,6 +178,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
168
178
|
"commitrepo",
|
|
169
179
|
"chart",
|
|
170
180
|
"custom-dashboards",
|
|
181
|
+
"delete",
|
|
171
182
|
"dependency-down",
|
|
172
183
|
"dependency-up",
|
|
173
184
|
"disconnected",
|
|
@@ -190,6 +201,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
190
201
|
"nothing-defined",
|
|
191
202
|
"password-token",
|
|
192
203
|
"protection-in-progress",
|
|
204
|
+
"remote-cloud",
|
|
193
205
|
"reports","scan-document",
|
|
194
206
|
"releases",
|
|
195
207
|
"survey",
|
|
@@ -227,7 +239,28 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
227
239
|
"data-extraction",
|
|
228
240
|
"workflow"
|
|
229
241
|
];
|
|
230
|
-
const illustrations = [...globalList, ...dashboardsList
|
|
242
|
+
const illustrations = [...globalList, ...dashboardsList];
|
|
243
|
+
|
|
244
|
+
const integrationsList = [
|
|
245
|
+
"agility","amazon-eks","ansible","apache-tomcat","app-aware","app-dynamics",
|
|
246
|
+
"app-management","application-protection","aras","arc-sight","argocd","atlassian",
|
|
247
|
+
"avaya","aws","azure","azure-aks-2","azure-devops-tfs","azure-functions",
|
|
248
|
+
"bamboo","bit-bucket","biztalk","black-duck","blazemeter","bmc-helix",
|
|
249
|
+
"checkmarx","chef","chrome","cisco","citrix","clarity-ppm","cloud-foundry",
|
|
250
|
+
"continuostesting","cyberark","dai-logo","delphix","deploy","docker","docker-compose",
|
|
251
|
+
"dynatrace","edge","essential-app-protection","explorer","f5","firefox",
|
|
252
|
+
"flux-cd","fortify","fortify-on-demand","free-marker","git-ops","github",
|
|
253
|
+
"github-actions","gitlab","google","google-cloud","google-cloud-functions",
|
|
254
|
+
"hashi-corp-vault","helm","ibm","ingress","inteligence","jboss","jenkins",
|
|
255
|
+
"jfrog","jira","key","kubernetes","linux","mailhog","microsoft-iis",
|
|
256
|
+
"microsoft-teams","mysql","netscaler","new-relic","octopus-deploy",
|
|
257
|
+
"open-policy-agent","openid","openshift","opsgenie","oracle","parasoft",
|
|
258
|
+
"pendo","planview","podman","powershell","progress-chef","puppet","python",
|
|
259
|
+
"rabbitmq","release","safari","salesforce","servicenow","skytab","slack",
|
|
260
|
+
"sonarcloud","sonarqube","sonatype","svn","team-forge","teamcity","terraform",
|
|
261
|
+
"testlink","topaz","travis-ci","urbancode-deploy","vmware","webhook",
|
|
262
|
+
"windows","xray","zendesk",
|
|
263
|
+
];
|
|
231
264
|
|
|
232
265
|
// --- FAVOURITES LOGIC ---
|
|
233
266
|
function getFavourites() {
|
|
@@ -269,43 +302,44 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
269
302
|
const modalClose = document.getElementById('modal-close');
|
|
270
303
|
const modalCloseBottom = document.getElementById('modal-close-bottom');
|
|
271
304
|
|
|
272
|
-
function openModal(
|
|
305
|
+
function openModal(item, type) {
|
|
273
306
|
if (modalDescription) modalDescription.innerHTML = '';
|
|
274
307
|
if (!descriptionsLoaded) return;
|
|
308
|
+
|
|
275
309
|
const themeClass = root.classList.contains('dark') ? 'dark' : 'light';
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
310
|
+
const isInteg = type === 'integration' || integrationsList.includes(item) && !illustrations.includes(item);
|
|
311
|
+
|
|
312
|
+
let imgHtml, dotCodeString, codeString;
|
|
313
|
+
if (isInteg) {
|
|
314
|
+
imgHtml = `<span class="dot-integration" style="display:inline-flex;align-items:center;justify-content:center;width:200px;height:200px;"><img class="${item}" style="width:100%;height:100%;object-fit:contain;"/></span>`;
|
|
315
|
+
dotCodeString = `<span class="dot-integration"><img class="${item}"/></span>`;
|
|
316
|
+
codeString = dotCodeString;
|
|
317
|
+
} else {
|
|
318
|
+
const cat = globalList.includes(item) ? 'global' : 'dashboards';
|
|
319
|
+
const svgPath = `illustrations/${themeClass}/${cat}/${item}.svg`;
|
|
320
|
+
imgHtml = `<span class="dot-illustration"><img src="${svgPath}" class="${item} ${themeClass}" style="width:286px;height:286px;object-fit:contain;"/></span>`;
|
|
321
|
+
dotCodeString = `<DotIllustration illustrationId="${item}" />`;
|
|
322
|
+
codeString = `<span class="dot-illustration"><img src="${svgPath}" class="${item} ${themeClass}"/></span>`;
|
|
281
323
|
}
|
|
282
|
-
|
|
283
|
-
modalImage.innerHTML =
|
|
284
|
-
// Set the illustration name
|
|
324
|
+
|
|
325
|
+
modalImage.innerHTML = imgHtml;
|
|
285
326
|
const modalIllustrationName = document.getElementById('modal-illustration-name');
|
|
286
|
-
if (modalIllustrationName) modalIllustrationName.textContent =
|
|
287
|
-
// Set the Dot Code textarea value
|
|
288
|
-
const dotCodeString = `<DotIllustration illustrationId=\"${illustration}\" />`;
|
|
327
|
+
if (modalIllustrationName) modalIllustrationName.textContent = item;
|
|
289
328
|
const modalDotCode = document.getElementById('modal-dot-code');
|
|
290
329
|
if (modalDotCode) modalDotCode.value = dotCodeString;
|
|
291
|
-
// Show the HTML code snippet in the modal
|
|
292
|
-
const codeString = `<span class=\"dot-illustration\"><img src=\"${svgPath}\" class=\"${illustration} ${themeClass}\"/></span>`;
|
|
293
330
|
modalCode.value = codeString;
|
|
331
|
+
|
|
294
332
|
modal.classList.add('active');
|
|
295
333
|
modal.classList.remove('hidden');
|
|
296
|
-
|
|
334
|
+
|
|
297
335
|
const modalCopyDotCode = document.getElementById('modal-copy-dot-code');
|
|
298
336
|
if (modalCopyDotCode) {
|
|
299
|
-
modalCopyDotCode.onclick =
|
|
337
|
+
modalCopyDotCode.onclick = () => {
|
|
300
338
|
if (modalDotCode) {
|
|
301
|
-
navigator.clipboard.writeText(modalDotCode.value).then(
|
|
339
|
+
navigator.clipboard.writeText(modalDotCode.value).then(() => {
|
|
302
340
|
modalCopyDotCode.textContent = 'Copied!';
|
|
303
341
|
showToast('Copied Dot Code: ' + modalDotCode.value);
|
|
304
|
-
setTimeout(() => {
|
|
305
|
-
modalCopyDotCode.textContent = 'Copy Dot Code';
|
|
306
|
-
}, 2000);
|
|
307
|
-
}, function(err) {
|
|
308
|
-
console.error('Failed to copy dot code: ', err);
|
|
342
|
+
setTimeout(() => { modalCopyDotCode.textContent = 'Copy Dot Code'; }, 2000);
|
|
309
343
|
});
|
|
310
344
|
}
|
|
311
345
|
};
|
|
@@ -324,75 +358,139 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
324
358
|
showToast('Copied code: ' + modalCode.value);
|
|
325
359
|
});
|
|
326
360
|
|
|
327
|
-
// ---
|
|
361
|
+
// --- ALPHABET STRIP ---
|
|
362
|
+
function renderAlphabetStrip() {
|
|
363
|
+
const strip = document.getElementById('alphabet-strip');
|
|
364
|
+
if (!strip) return;
|
|
365
|
+
|
|
366
|
+
const pool = getCurrentPool('');
|
|
367
|
+
const available = new Set(pool.map(id => id[0].toUpperCase()).filter(c => /[A-Z]/.test(c)));
|
|
368
|
+
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
|
|
369
|
+
|
|
370
|
+
strip.innerHTML = `
|
|
371
|
+
<button class="alpha-btn px-2 py-0.5 text-xs rounded font-mono ${alphabetFilter === '' ? 'bg-blue-600 text-white' : 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-600'} transition" data-letter="">All</button>
|
|
372
|
+
${letters.map(l => `
|
|
373
|
+
<button class="alpha-btn px-2 py-0.5 text-xs rounded font-mono ${
|
|
374
|
+
available.has(l)
|
|
375
|
+
? alphabetFilter === l
|
|
376
|
+
? 'bg-blue-600 text-white'
|
|
377
|
+
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-blue-100 dark:hover:bg-blue-800 transition'
|
|
378
|
+
: 'opacity-30 cursor-default bg-gray-100 dark:bg-gray-800 text-gray-400'
|
|
379
|
+
}" data-letter="${l}" ${available.has(l) ? '' : 'disabled'}>${l}</button>
|
|
380
|
+
`).join('')}
|
|
381
|
+
`;
|
|
382
|
+
strip.querySelectorAll('.alpha-btn:not([disabled])').forEach(btn => {
|
|
383
|
+
btn.addEventListener('click', () => {
|
|
384
|
+
alphabetFilter = btn.dataset.letter;
|
|
385
|
+
renderAlphabetStrip();
|
|
386
|
+
renderIllustrations();
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function getCurrentPool(filterOverride) {
|
|
392
|
+
const f = filterOverride !== undefined ? filterOverride : alphabetFilter;
|
|
393
|
+
let base = [];
|
|
394
|
+
if (currentTab === 'all') base = illustrations;
|
|
395
|
+
else if (currentTab === 'global') base = globalList;
|
|
396
|
+
else if (currentTab === 'dashboards') base = dashboardsList;
|
|
397
|
+
else if (currentTab === 'integrations') base = integrationsList;
|
|
398
|
+
else if (currentTab === 'favourites') base = [...illustrations, ...integrationsList].filter(id => getFavourites().includes(id));
|
|
399
|
+
if (f) base = base.filter(id => id[0].toUpperCase() === f);
|
|
400
|
+
return base;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// --- ILLUSTRATION + INTEGRATION RENDERING ---
|
|
328
404
|
function renderIllustrations() {
|
|
329
405
|
const list = document.getElementById("illustration-list");
|
|
330
406
|
list.innerHTML = "";
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
toShow = dashboardsList;
|
|
338
|
-
} else if (currentTab === 'favourites') {
|
|
339
|
-
const favs = getFavourites();
|
|
340
|
-
toShow = illustrations.filter(id => favs.includes(id));
|
|
341
|
-
}
|
|
342
|
-
const filter = (document.getElementById('myInput')?.value || '').toUpperCase();
|
|
343
|
-
toShow = toShow.filter(id => id.toUpperCase().includes(filter));
|
|
407
|
+
const searchFilter = (document.getElementById('myInput')?.value || '').toUpperCase();
|
|
408
|
+
let toShow = getCurrentPool().filter(id => id.toUpperCase().includes(searchFilter));
|
|
409
|
+
|
|
410
|
+
const countEl = document.getElementById('result-count');
|
|
411
|
+
if (countEl) countEl.textContent = toShow.length > 0 ? `${toShow.length} item${toShow.length === 1 ? '' : 's'}` : '';
|
|
412
|
+
|
|
344
413
|
if (toShow.length === 0) {
|
|
345
|
-
list.innerHTML =
|
|
414
|
+
list.innerHTML = `<div class="col-span-full text-center text-gray-500 dark:text-gray-400 py-8">${
|
|
415
|
+
currentTab === 'favourites' ? 'No favourites yet.' : 'No items found.'
|
|
416
|
+
}</div>`;
|
|
346
417
|
return;
|
|
347
418
|
}
|
|
419
|
+
|
|
420
|
+
const isIntegrations = currentTab === 'integrations' ||
|
|
421
|
+
(currentTab === 'favourites' && toShow.every(id => integrationsList.includes(id)));
|
|
348
422
|
const favs = getFavourites();
|
|
349
423
|
const themeClass = root.classList.contains('dark') ? 'dark' : 'light';
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="${isFav ? 'gold' : 'none'}" viewBox="0 0 24 24" stroke="gold">
|
|
366
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 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"/>
|
|
367
|
-
</svg>
|
|
368
|
-
</button>
|
|
369
|
-
<div class="flex-1 flex flex-col justify-center items-center">
|
|
370
|
-
<span class="dot-illustration">
|
|
371
|
-
<img src="${svgPath}" class="${illustration} ${themeClass}"/>
|
|
372
|
-
</span>
|
|
373
|
-
<span class="iconID block text-center text-base font-medium text-gray-800 dark:text-gray-200 my-2">${illustration}</span>
|
|
374
|
-
</div>
|
|
375
|
-
<div class="button-group flex flex-row items-center justify-center gap-2 mt-2">
|
|
376
|
-
<button id="CopyID" 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="copyText(this)">
|
|
377
|
-
Copy Dot Code
|
|
378
|
-
<span class="tooltiptext left-1/2 -translate-x-1/2 bottom-10 shadow-md">Copy Dot Code</span>
|
|
424
|
+
|
|
425
|
+
toShow.forEach(item => {
|
|
426
|
+
const isInteg = integrationsList.includes(item) && !illustrations.includes(item);
|
|
427
|
+
const isFav = favs.includes(item);
|
|
428
|
+
|
|
429
|
+
if (isInteg) {
|
|
430
|
+
// Integration card (single SVG, dot-integration wrapper)
|
|
431
|
+
const codeString = `<span class="dot-integration"><img class="${item}"/></span>`;
|
|
432
|
+
const div = document.createElement('div');
|
|
433
|
+
div.setAttribute('class', 'copy-container relative group flex flex-col items-center justify-between bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl shadow-lg p-2 md:p-3 transition-transform duration-200 hover:scale-105 hover:shadow-2xl h-[302px] w-full');
|
|
434
|
+
div.dataset.itemId = item;
|
|
435
|
+
div.dataset.itemType = 'integration';
|
|
436
|
+
div.innerHTML = `
|
|
437
|
+
<button class="absolute top-2 right-2 z-10 p-1 rounded-full bg-white/80 dark:bg-gray-800/80 hover:bg-yellow-100 dark:hover:bg-yellow-300 transition" title="Toggle favourite" onclick="event.stopPropagation();window.toggleFavourite('${item}')">
|
|
438
|
+
<svg class="h-5 w-5" fill="${isFav ? 'gold' : 'none'}" viewBox="0 0 24 24" stroke="gold"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 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>
|
|
379
439
|
</button>
|
|
380
|
-
<
|
|
381
|
-
|
|
382
|
-
<span class="
|
|
440
|
+
<div class="flex-1 flex flex-col justify-center items-center">
|
|
441
|
+
<span class="dot-integration flex items-center justify-center" style="width:160px;height:160px;"><img class="${item}" style="width:100%;height:100%;object-fit:contain;"/></span>
|
|
442
|
+
<span class="iconID block text-center text-base font-medium text-gray-800 dark:text-gray-200 my-2">${item}</span>
|
|
443
|
+
</div>
|
|
444
|
+
<div class="button-group flex flex-row items-center justify-center gap-2 mt-2">
|
|
445
|
+
<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="copyText(this)">
|
|
446
|
+
Copy Dot Code<span class="tooltiptext left-1/2 -translate-x-1/2 bottom-10 shadow-md">Copy Dot Code</span>
|
|
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>
|
|
451
|
+
</div>
|
|
452
|
+
<pre class="codeBlock"><code class="codeBlock">${codeString}</code></pre>
|
|
453
|
+
`;
|
|
454
|
+
list.appendChild(div);
|
|
455
|
+
} else {
|
|
456
|
+
// Illustration card (light/dark, dot-illustration wrapper)
|
|
457
|
+
let categoryFolder = globalList.includes(item) ? 'global' : 'dashboards';
|
|
458
|
+
const svgPath = `illustrations/${themeClass}/${categoryFolder}/${item}.svg`;
|
|
459
|
+
const codeString = `<span class="dot-illustration"><img src="${svgPath}" class="${item} ${themeClass}"/></span>`;
|
|
460
|
+
const div = document.createElement('div');
|
|
461
|
+
div.setAttribute('class', 'copy-container relative group flex flex-col items-center justify-between bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl shadow-lg p-2 md:p-3 transition-transform duration-200 hover:scale-105 hover:shadow-2xl h-[302px] w-full');
|
|
462
|
+
div.dataset.itemId = item;
|
|
463
|
+
div.dataset.itemType = 'illustration';
|
|
464
|
+
div.innerHTML = `
|
|
465
|
+
<button class="absolute top-2 right-2 z-10 p-1 rounded-full bg-white/80 dark:bg-gray-800/80 hover:bg-yellow-100 dark:hover:bg-yellow-300 transition" title="Toggle favourite" onclick="event.stopPropagation();window.toggleFavourite('${item}')">
|
|
466
|
+
<svg class="h-5 w-5" fill="${isFav ? 'gold' : 'none'}" viewBox="0 0 24 24" stroke="gold"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 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>
|
|
383
467
|
</button>
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
468
|
+
<div class="flex-1 flex flex-col justify-center items-center">
|
|
469
|
+
<span class="dot-illustration"><img src="${svgPath}" class="${item} ${themeClass}"/></span>
|
|
470
|
+
<span class="iconID block text-center text-base font-medium text-gray-800 dark:text-gray-200 my-2">${item}</span>
|
|
471
|
+
</div>
|
|
472
|
+
<div class="button-group flex flex-row items-center justify-center gap-2 mt-2">
|
|
473
|
+
<button id="CopyID" 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="copyText(this)">
|
|
474
|
+
Copy Dot Code<span class="tooltiptext left-1/2 -translate-x-1/2 bottom-10 shadow-md">Copy Dot Code</span>
|
|
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>
|
|
479
|
+
</div>
|
|
480
|
+
<pre class="codeBlock"><code class="codeBlock">${codeString}</code></pre>
|
|
481
|
+
`;
|
|
482
|
+
list.appendChild(div);
|
|
483
|
+
}
|
|
388
484
|
});
|
|
389
|
-
|
|
485
|
+
|
|
486
|
+
// Modal click on each card
|
|
390
487
|
document.querySelectorAll('.copy-container').forEach(card => {
|
|
391
488
|
card.addEventListener('click', function(e) {
|
|
392
489
|
if (e.target.closest('button')) return;
|
|
393
|
-
if (!descriptionsLoaded) return;
|
|
394
|
-
const id
|
|
395
|
-
|
|
490
|
+
if (!descriptionsLoaded) return;
|
|
491
|
+
const id = this.dataset.itemId || this.querySelector('.iconID')?.textContent;
|
|
492
|
+
const type = this.dataset.itemType;
|
|
493
|
+
if (id) openModal(id, type);
|
|
396
494
|
});
|
|
397
495
|
});
|
|
398
496
|
}
|
|
@@ -408,6 +506,183 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
408
506
|
document.getElementById('myInput').value = '';
|
|
409
507
|
document.getElementById('floatingInput').value = '';
|
|
410
508
|
|
|
509
|
+
// ── GitHub auth ──────────────────────────────────────────────────────────
|
|
510
|
+
function updateAuthBadge(user) {
|
|
511
|
+
const btn = document.getElementById('gh-connect-btn');
|
|
512
|
+
const badge = document.getElementById('gh-user-badge');
|
|
513
|
+
const name = document.getElementById('gh-user-name');
|
|
514
|
+
if (user) {
|
|
515
|
+
btn.classList.add('hidden');
|
|
516
|
+
badge.classList.remove('hidden');
|
|
517
|
+
badge.style.display = 'flex';
|
|
518
|
+
name.textContent = `@${user.login}`;
|
|
519
|
+
localStorage.setItem('gh_illus_user', JSON.stringify(user));
|
|
520
|
+
} else {
|
|
521
|
+
btn.classList.remove('hidden');
|
|
522
|
+
badge.style.display = 'none';
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function updateUploadAuthUI() {
|
|
527
|
+
const token = window.GitHubUpload?.getToken?.();
|
|
528
|
+
['upload-illus-auth-gate','upload-integ-auth-gate'].forEach(id => {
|
|
529
|
+
document.getElementById(id)?.classList.toggle('hidden', !!token);
|
|
530
|
+
});
|
|
531
|
+
['upload-illus-form','upload-integ-form'].forEach(id => {
|
|
532
|
+
document.getElementById(id)?.classList.toggle('hidden', !token);
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async function tryDeviceFlow() {
|
|
537
|
+
const modal = document.getElementById('gh-device-modal');
|
|
538
|
+
modal.classList.remove('hidden');
|
|
539
|
+
document.getElementById('gh-device-step-code').classList.remove('hidden');
|
|
540
|
+
document.getElementById('gh-device-step-pat').classList.add('hidden');
|
|
541
|
+
document.getElementById('gh-device-error').classList.add('hidden');
|
|
542
|
+
try {
|
|
543
|
+
const { user_code, verification_uri, device_code, expires_in, interval } =
|
|
544
|
+
await window.GitHubUpload.startDeviceFlow();
|
|
545
|
+
document.getElementById('gh-device-uri').href = verification_uri;
|
|
546
|
+
document.getElementById('gh-device-uri').textContent = verification_uri;
|
|
547
|
+
document.getElementById('gh-device-code').textContent = user_code;
|
|
548
|
+
document.getElementById('gh-copy-code-btn').onclick = () =>
|
|
549
|
+
navigator.clipboard.writeText(user_code).then(() => showToast('Code copied!'));
|
|
550
|
+
const token = await window.GitHubUpload.pollForToken(device_code, interval || 5);
|
|
551
|
+
window.GitHubUpload.setToken(token);
|
|
552
|
+
const user = await window.GitHubUpload.getUser();
|
|
553
|
+
updateAuthBadge(user);
|
|
554
|
+
modal.classList.add('hidden');
|
|
555
|
+
updateUploadAuthUI();
|
|
556
|
+
showToast(`Connected as @${user.login}`);
|
|
557
|
+
} catch (err) {
|
|
558
|
+
if (err.message === 'NO_CLIENT_ID' || err.name === 'TypeError') {
|
|
559
|
+
document.getElementById('gh-device-step-code').classList.add('hidden');
|
|
560
|
+
document.getElementById('gh-device-step-pat').classList.remove('hidden');
|
|
561
|
+
} else {
|
|
562
|
+
document.getElementById('gh-device-error').textContent = err.message;
|
|
563
|
+
document.getElementById('gh-device-error').classList.remove('hidden');
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
document.getElementById('gh-connect-btn')?.addEventListener('click', tryDeviceFlow);
|
|
569
|
+
document.getElementById('gh-logout-btn')?.addEventListener('click', () => {
|
|
570
|
+
window.GitHubUpload?.clearToken?.();
|
|
571
|
+
updateAuthBadge(null);
|
|
572
|
+
updateUploadAuthUI();
|
|
573
|
+
showToast('Disconnected from GitHub');
|
|
574
|
+
});
|
|
575
|
+
document.getElementById('gh-device-close')?.addEventListener('click', () => {
|
|
576
|
+
document.getElementById('gh-device-modal').classList.add('hidden');
|
|
577
|
+
});
|
|
578
|
+
document.getElementById('gh-pat-submit')?.addEventListener('click', async () => {
|
|
579
|
+
const pat = document.getElementById('gh-pat-input').value.trim();
|
|
580
|
+
if (!pat) return;
|
|
581
|
+
window.GitHubUpload.setToken(pat);
|
|
582
|
+
try {
|
|
583
|
+
const user = await window.GitHubUpload.getUser();
|
|
584
|
+
updateAuthBadge(user);
|
|
585
|
+
document.getElementById('gh-device-modal').classList.add('hidden');
|
|
586
|
+
updateUploadAuthUI();
|
|
587
|
+
showToast(`Connected as @${user.login}`);
|
|
588
|
+
} catch {
|
|
589
|
+
document.getElementById('gh-device-error').textContent = 'Invalid token.';
|
|
590
|
+
document.getElementById('gh-device-error').classList.remove('hidden');
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// Restore session
|
|
595
|
+
const savedUser = localStorage.getItem('gh_illus_user');
|
|
596
|
+
if (window.GitHubUpload?.getToken?.() && savedUser) {
|
|
597
|
+
try { updateAuthBadge(JSON.parse(savedUser)); } catch {}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// ── Upload panel ─────────────────────────────────────────────────────────
|
|
601
|
+
const uploadBufs = { illusLight: null, illusDark: null, integSvg: null };
|
|
602
|
+
|
|
603
|
+
function bindFileDrop(dropId, inputId, nameId, bufKey) {
|
|
604
|
+
const drop = document.getElementById(dropId);
|
|
605
|
+
const input = document.getElementById(inputId);
|
|
606
|
+
const nameEl = document.getElementById(nameId);
|
|
607
|
+
if (!drop || !input) return;
|
|
608
|
+
drop.addEventListener('click', () => input.click());
|
|
609
|
+
drop.addEventListener('dragover', e => { e.preventDefault(); drop.classList.add('border-blue-400'); });
|
|
610
|
+
drop.addEventListener('dragleave', () => drop.classList.remove('border-blue-400'));
|
|
611
|
+
drop.addEventListener('drop', e => { e.preventDefault(); drop.classList.remove('border-blue-400'); handleFile(e.dataTransfer.files[0], bufKey, nameEl, drop); });
|
|
612
|
+
input.addEventListener('change', () => handleFile(input.files[0], bufKey, nameEl, drop));
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function handleFile(file, bufKey, nameEl, drop) {
|
|
616
|
+
if (!file || !file.name.endsWith('.svg')) { showToast('Please select an SVG file'); return; }
|
|
617
|
+
const reader = new FileReader();
|
|
618
|
+
reader.onload = e => {
|
|
619
|
+
uploadBufs[bufKey] = e.target.result;
|
|
620
|
+
if (nameEl) { nameEl.textContent = `✓ ${file.name}`; nameEl.classList.remove('hidden'); }
|
|
621
|
+
drop.classList.add('border-green-400', 'dark:border-green-600');
|
|
622
|
+
drop.classList.remove('border-gray-300', 'dark:border-gray-600');
|
|
623
|
+
};
|
|
624
|
+
reader.readAsArrayBuffer(file);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
bindFileDrop('illus-light-drop', 'illus-light-input', 'illus-light-name', 'illusLight');
|
|
628
|
+
bindFileDrop('illus-dark-drop', 'illus-dark-input', 'illus-dark-name', 'illusDark');
|
|
629
|
+
bindFileDrop('integ-svg-drop', 'integ-svg-input', 'integ-svg-name', 'integSvg');
|
|
630
|
+
|
|
631
|
+
async function handleIllusUpload() {
|
|
632
|
+
const id = document.getElementById('upload-illus-id')?.value.trim();
|
|
633
|
+
const cat = document.getElementById('upload-illus-category')?.value;
|
|
634
|
+
const btn = document.getElementById('upload-illus-btn');
|
|
635
|
+
const status = document.getElementById('upload-illus-status');
|
|
636
|
+
const prLink = document.getElementById('upload-illus-pr-link');
|
|
637
|
+
if (!id) { showToast('Enter illustration ID'); return; }
|
|
638
|
+
if (!uploadBufs.illusLight) { showToast('Select the light SVG'); return; }
|
|
639
|
+
if (!uploadBufs.illusDark) { showToast('Select the dark SVG'); return; }
|
|
640
|
+
if (!window.GitHubUpload?.getToken?.()) { showToast('Connect GitHub first'); return; }
|
|
641
|
+
btn.disabled = true; btn.textContent = 'Creating PR…';
|
|
642
|
+
status.classList.add('hidden'); prLink.classList.add('hidden');
|
|
643
|
+
try {
|
|
644
|
+
const url = await window.GitHubUpload.createIllustrationPR(id, cat, uploadBufs.illusLight, uploadBufs.illusDark);
|
|
645
|
+
status.textContent = 'PR created!';
|
|
646
|
+
status.className = 'text-sm text-center text-green-600 dark:text-green-400';
|
|
647
|
+
status.classList.remove('hidden');
|
|
648
|
+
document.getElementById('upload-illus-pr-anchor').href = url;
|
|
649
|
+
prLink.classList.remove('hidden');
|
|
650
|
+
showToast('PR created!');
|
|
651
|
+
} catch (err) {
|
|
652
|
+
status.textContent = `Error: ${err.message}`;
|
|
653
|
+
status.className = 'text-sm text-center text-red-600 dark:text-red-400';
|
|
654
|
+
status.classList.remove('hidden');
|
|
655
|
+
} finally { btn.disabled = false; btn.textContent = 'Create PR'; }
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
async function handleIntegUpload() {
|
|
659
|
+
const id = document.getElementById('upload-integ-id')?.value.trim();
|
|
660
|
+
const btn = document.getElementById('upload-integ-btn');
|
|
661
|
+
const status = document.getElementById('upload-integ-status');
|
|
662
|
+
const prLink = document.getElementById('upload-integ-pr-link');
|
|
663
|
+
if (!id) { showToast('Enter integration ID'); return; }
|
|
664
|
+
if (!uploadBufs.integSvg) { showToast('Select an SVG file'); return; }
|
|
665
|
+
if (!window.GitHubUpload?.getToken?.()) { showToast('Connect GitHub first'); return; }
|
|
666
|
+
btn.disabled = true; btn.textContent = 'Creating PR…';
|
|
667
|
+
status.classList.add('hidden'); prLink.classList.add('hidden');
|
|
668
|
+
try {
|
|
669
|
+
const url = await window.GitHubUpload.createIntegrationPR(id, uploadBufs.integSvg);
|
|
670
|
+
status.textContent = 'PR created!';
|
|
671
|
+
status.className = 'text-sm text-center text-green-600 dark:text-green-400';
|
|
672
|
+
status.classList.remove('hidden');
|
|
673
|
+
document.getElementById('upload-integ-pr-anchor').href = url;
|
|
674
|
+
prLink.classList.remove('hidden');
|
|
675
|
+
showToast('PR created!');
|
|
676
|
+
} catch (err) {
|
|
677
|
+
status.textContent = `Error: ${err.message}`;
|
|
678
|
+
status.className = 'text-sm text-center text-red-600 dark:text-red-400';
|
|
679
|
+
status.classList.remove('hidden');
|
|
680
|
+
} finally { btn.disabled = false; btn.textContent = 'Create PR'; }
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
document.getElementById('upload-illus-btn')?.addEventListener('click', handleIllusUpload);
|
|
684
|
+
document.getElementById('upload-integ-btn')?.addEventListener('click', handleIntegUpload);
|
|
685
|
+
|
|
411
686
|
// Set initial tab state to 'all' and update UI (this will also render illustrations)
|
|
412
687
|
setTab('all');
|
|
413
688
|
|
|
@@ -444,6 +719,7 @@ function getSuggestions(query) {
|
|
|
444
719
|
if (currentTab === 'all') pool = illustrations;
|
|
445
720
|
else if (currentTab === 'global') pool = globalList;
|
|
446
721
|
else if (currentTab === 'dashboards') pool = dashboardsList;
|
|
722
|
+
else if (currentTab === 'integrations') pool = integrationsList;
|
|
447
723
|
else if (currentTab === 'favourites') pool = getFavourites();
|
|
448
724
|
// Fuzzy match, sort by closeness (shorter index of first match, then length)
|
|
449
725
|
let scored = pool
|