Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
805 lines (739 loc) · 60.9 KB
/
Copy pathindex.html
File metadata and controls
805 lines (739 loc) · 60.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>InfoArch — Free Information Architecture Diagram Builder</title>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-5QFPQ8T3');</script>
<!-- End Google Tag Manager -->
<!-- Google Analytics (GA4) — loads async, never blocks the app if offline/unavailable -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-2DX8RDCCXE"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-2DX8RDCCXE');
</script>
<!-- Primary SEO meta -->
<meta name="description" content="InfoArch is a free, browser-based information architecture diagram builder. Turn simple indented text syntax into interactive IA sitemaps, user-flow trees, and UX decision diagrams — with personas, tag filters, navigation links, and PNG export. No sign-up, runs entirely in your browser.">
<meta name="keywords" content="information architecture, IA diagram, information architecture tool, sitemap builder, UX flow diagram, user flow tool, decision flow diagram, text to diagram, diagram as code, IA sitemap generator, free diagramming tool, tree diagram builder, wireflow, UX design tool">
<meta name="author" content="InfoArch">
<meta name="robots" content="index, follow, max-image-preview:large">
<meta name="theme-color" content="#6366f1">
<meta name="application-name" content="InfoArch">
<link rel="canonical" href="https://infoarchdiagram.org/">
<!-- Open Graph (Facebook, LinkedIn, Slack, Discord, etc.) -->
<meta property="og:type" content="website">
<meta property="og:site_name" content="InfoArch">
<meta property="og:title" content="InfoArch — Free Information Architecture Diagram Builder">
<meta property="og:description" content="Turn simple indented text into interactive information architecture diagrams, UX flows, and decision trees. Personas, tag filters, navigation links, and PNG export — free and in-browser.">
<meta property="og:url" content="https://infoarchdiagram.org/">
<meta property="og:image" content="https://infoarchdiagram.org/image.png">
<meta property="og:image:secure_url" content="https://infoarchdiagram.org/image.png">
<meta property="og:image:type" content="image/jpeg">
<meta property="og:image:width" content="1024">
<meta property="og:image:height" content="541">
<meta property="og:image:alt" content="InfoArch — a tool for modeling UX information architecture diagrams, showing multiple interactive tree and flow diagrams.">
<meta property="og:locale" content="en_US">
<!-- Twitter / X Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="InfoArch — Free Information Architecture Diagram Builder">
<meta name="twitter:description" content="Turn simple indented text into interactive information architecture diagrams, UX flows, and decision trees. Free and in-browser — no sign-up.">
<meta name="twitter:image" content="https://infoarchdiagram.org/image.png">
<meta name="twitter:image:alt" content="InfoArch — a tool for modeling UX information architecture diagrams.">
<!-- Favicon (matches the header schema logo) -->
<link rel="icon" type="image/svg+xml" href="./favicon.svg">
<link rel="mask-icon" href="./favicon.svg" color="#6366f1">
<link rel="apple-touch-icon" href="./favicon.svg">
<!-- Structured data for search engines, LLMs, and reading agents -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "InfoArch",
"alternateName": "InfoArch Diagram Builder",
"url": "https://infoarchdiagram.org/",
"description": "A free, browser-based information architecture diagram builder that converts simple indented text syntax into interactive IA sitemaps, UX user-flow trees, and decision-flow diagrams. Supports personas, tag and status filters, navigation links, a glossary, and PNG export. Runs entirely client-side with no sign-up.",
"applicationCategory": "DesignApplication",
"applicationSubCategory": "Information Architecture & Diagramming",
"operatingSystem": "Any (web browser)",
"browserRequirements": "Requires JavaScript. Runs in any modern web browser.",
"image": "https://infoarchdiagram.org/image.png",
"screenshot": "https://infoarchdiagram.org/image.png",
"inLanguage": "en",
"isAccessibleForFree": true,
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"featureList": [
"Text-to-diagram syntax (diagram as code)",
"Interactive information architecture trees and sitemaps",
"UX user flows and conditional decision diagrams",
"User personas with role-based filtering",
"Tag, status, and flag filters",
"Navigation links (href) between cards",
"Glossary of elements and personas",
"Horizontal and vertical tree layouts",
"PNG export",
"Light and dark mode",
"Runs fully in-browser with local file load/save"
]
}
</script>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Google Material Symbols Outlined -->
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet" />
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
brand: {
50: '#eef2ff',
100: '#e0e7ff',
500: '#6366f1',
600: '#4f46e5',
700: '#4338ca',
}
}
}
}
}
</script>
<link rel="stylesheet" href="./styles.css">
</head>
<body class="h-full overflow-hidden bg-slate-100 text-slate-800 dark:bg-slate-900 dark:text-slate-100 transition-colors duration-200">
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5QFPQ8T3"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<div id="app" class="h-full flex flex-col">
<!-- Header -->
<header class="relative overflow-visible bg-white dark:bg-slate-800 border-b border-slate-200 dark:border-slate-700 px-6 py-3 shrink-0 shadow-sm z-10">
<div class="flex items-center justify-between gap-4">
<div class="flex items-center space-x-3 overflow-hidden">
<div class="bg-brand-500 text-white p-2 rounded-lg flex items-center justify-center shrink-0">
<span class="material-symbols-outlined text-2xl" style="transform: rotate(90deg);">schema</span>
</div>
<div class="truncate">
<!-- Dynamically Updated Diagram Title via 'title:' tag -->
<h1 id="header-diagram-title" class="text-lg font-bold tracking-tight text-slate-900 dark:text-white truncate max-w-[240px] sm:max-w-md">Untitled Diagram</h1>
<p class="text-xs text-slate-500 dark:text-slate-400">Interactive Syntax-to-Tree IA Diagrammer</p>
</div>
</div>
<!-- Action Buttons -->
<div class="flex items-center space-x-2 shrink-0">
<!-- Load Local File Trigger -->
<button onclick="triggerFileInput()" class="px-3 py-1.5 rounded-lg border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-700 hover:bg-slate-50 dark:hover:bg-slate-600 text-slate-700 dark:text-slate-300 text-xs font-semibold flex items-center space-x-1.5 transition-colors animate-none" title="Load text diagram from your device">
<span class="material-symbols-outlined text-base">upload_file</span>
<span class="hidden sm:inline">Load File</span>
</button>
<input type="file" id="fileInput" accept=".txt,.ia,text/*" class="hidden" onchange="handleFileUpload(event)">
<!-- Save Local File Trigger -->
<button onclick="saveToFile()" class="px-3 py-1.5 rounded-lg border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-700 hover:bg-slate-50 dark:hover:bg-slate-600 text-slate-700 dark:text-slate-300 text-xs font-semibold flex items-center space-x-1.5 transition-colors" title="Save current text diagram to your device">
<span class="material-symbols-outlined text-base">save</span>
<span class="hidden sm:inline">Save File</span>
</button>
<!-- Layout Controls -->
<div class="flex bg-slate-100 dark:bg-slate-700 p-1 rounded-lg border border-slate-200 dark:border-slate-600">
<button onclick="setOrientation('horizontal')" id="btn-orient-horizontal" class="px-2.5 py-1 rounded text-xs font-semibold flex items-center space-x-1 transition-all bg-white dark:bg-slate-600 shadow-sm text-brand-600 dark:text-white" title="Horizontal Tree Layout (Left-to-Right)">
<span class="material-symbols-outlined text-sm">keyboard_double_arrow_right</span>
<span class="hidden sm:inline">Horizontal</span>
</button>
<button onclick="setOrientation('vertical')" id="btn-orient-vertical" class="px-2.5 py-1 rounded text-xs font-semibold flex items-center space-x-1 transition-all text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white" title="Vertical Tree Layout (Top-to-Bottom)">
<span class="material-symbols-outlined text-sm">keyboard_double_arrow_down</span>
<span class="hidden sm:inline">Vertical</span>
</button>
</div>
<!-- Connection toggle -->
<button onclick="toggleConnections()" id="btn-toggle-connections" class="p-2 rounded-lg border border-slate-200 dark:border-slate-600 bg-indigo-50 dark:bg-slate-600 hover:bg-indigo-100 dark:hover:bg-slate-500 text-indigo-600 dark:text-white relative transition-colors" title="Toggle Field Instance Connecting Lines">
<span class="material-symbols-outlined text-base">link</span>
<span id="connections-active-dot" class="absolute top-1 right-1 w-2 h-2 bg-emerald-500 rounded-full"></span>
</button>
<!-- Top-bar quick filter toggles -->
<button onclick="toggleTopMenu('persona')" id="btn-toggle-persona-menu" class="hidden p-2 rounded-lg border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-700 hover:bg-slate-50 dark:hover:bg-slate-600 text-slate-600 dark:text-slate-300 transition-colors" title="Toggle Persona Filter">
<span class="material-symbols-outlined text-base">group</span>
</button>
<button onclick="toggleTopMenu('tag')" id="btn-toggle-tag-menu" class="hidden p-2 rounded-lg border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-700 hover:bg-slate-50 dark:hover:bg-slate-600 text-slate-600 dark:text-slate-300 transition-colors" title="Toggle Tag Filter">
<span class="material-symbols-outlined text-base">sell</span>
</button>
<button onclick="toggleTopMenu('glossary')" id="btn-toggle-glossary-menu" class="hidden p-2 rounded-lg border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-700 hover:bg-slate-50 dark:hover:bg-slate-600 text-slate-600 dark:text-slate-300 transition-colors" title="Toggle Glossary">
<span class="material-symbols-outlined text-base">menu_book</span>
</button>
<!-- Inspector Toggle Sidebar Button -->
<button onclick="toggleInspector()" id="btn-toggle-inspector" class="p-2 rounded-lg border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-700 hover:bg-slate-50 dark:hover:bg-slate-600 text-slate-600 dark:text-slate-300 transition-colors" title="Toggle Right Filter & Inspector Panel">
<span class="material-symbols-outlined text-base">filter_list</span>
</button>
<!-- Dark mode toggle -->
<button onclick="toggleTheme()" class="p-2 rounded-lg border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-700 hover:bg-slate-50 dark:hover:bg-slate-600" title="Toggle Theme">
<span id="theme-icon" class="material-symbols-outlined text-base">dark_mode</span>
</button>
<!-- Help Button -->
<button onclick="toggleHelpModal(true)" class="p-2 rounded-lg border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-700 hover:bg-slate-50 dark:hover:bg-slate-600 text-slate-500 dark:text-slate-400" title="Syntax Guide">
<span class="material-symbols-outlined text-base">help</span>
</button>
<!-- Donate Button -->
<button onclick="toggleDonateModal(true)" class="px-3 py-1.5 rounded-lg border border-amber-200 dark:border-amber-700/60 bg-amber-50 dark:bg-amber-950/40 hover:bg-amber-100 dark:hover:bg-amber-900/60 text-amber-700 dark:text-amber-300 text-xs font-semibold flex items-center space-x-1.5 transition-colors" title="Support this project with a Bitcoin donation">
<span class="material-symbols-outlined text-base">favorite</span>
<span class="hidden sm:inline">Donate</span>
</button>
</div>
</div>
<div id="top-filter-panels" class="hidden absolute right-6 top-full mt-3 z-30 flex justify-end pointer-events-none">
<div id="persona-filter-card" class="hidden w-72 max-w-[calc(100vw-3rem)] bg-white dark:bg-slate-800 shadow-lg border border-slate-200 dark:border-slate-700 rounded-xl p-3.5 text-xs transition-all duration-300 pointer-events-auto">
<div class="flex items-center justify-between mb-2">
<h3 class="font-bold text-slate-800 dark:text-white flex items-center">
<span class="material-symbols-outlined text-brand-500 text-sm mr-1.5">group</span>
Persona Filter
</h3>
</div>
<div class="relative">
<select id="personaSelect" onchange="setPersonaFilter(this.value)" class="w-full bg-slate-50 dark:bg-slate-700 border border-slate-200 dark:border-slate-600 rounded-lg px-3 py-2 text-xs font-semibold hover:bg-slate-100 dark:hover:bg-slate-600 focus:outline-none focus:ring-2 focus:ring-brand-500 cursor-pointer text-slate-800 dark:text-slate-100">
<!-- Dynamically populated -->
</select>
</div>
</div>
<div id="tag-filter-card" class="hidden w-72 max-w-[calc(100vw-3rem)] bg-white dark:bg-slate-800 shadow-lg border border-slate-200 dark:border-slate-700 rounded-xl p-3.5 text-xs transition-all duration-300 pointer-events-auto">
<div class="flex items-center justify-between mb-2">
<h3 class="font-bold text-slate-800 dark:text-white flex items-center">
<span class="material-symbols-outlined text-brand-500 text-sm mr-1.5">sell</span>
Tag Filter
</h3>
</div>
<div class="relative">
<select id="tagSelect" onchange="setTagFilter(this.value)" class="w-full bg-slate-50 dark:bg-slate-700 border border-slate-200 dark:border-slate-600 rounded-lg px-3 py-2 text-xs font-semibold hover:bg-slate-100 dark:hover:bg-slate-600 focus:outline-none focus:ring-2 focus:ring-brand-500 cursor-pointer text-slate-800 dark:text-slate-100">
<!-- Dynamically populated -->
</select>
</div>
<div class="mt-3 pt-3 border-t border-slate-100 dark:border-slate-700">
<p id="tag-results-summary" class="text-[10px] font-semibold uppercase tracking-wider text-slate-400 dark:text-slate-500">Select a tag to list matching cards</p>
<div id="tag-results-list" class="mt-2 max-h-48 overflow-y-auto space-y-1 text-xs font-sans">
<!-- Dynamically populated -->
</div>
</div>
</div>
<div id="glossary-card" class="hidden w-72 max-w-[calc(100vw-3rem)] bg-white dark:bg-slate-800 shadow-lg border border-slate-200 dark:border-slate-700 rounded-xl p-3.5 text-xs transition-all duration-300 pointer-events-auto">
<div class="flex items-center justify-between mb-2">
<h3 class="font-bold text-slate-800 dark:text-white flex items-center">
<span class="material-symbols-outlined text-brand-500 text-sm mr-1.5">menu_book</span>
Glossary
</h3>
</div>
<p class="text-[10px] font-semibold uppercase tracking-wider text-slate-400 dark:text-slate-500">Elements and personas in alphabetical order</p>
<div id="glossary-list" class="mt-3 max-h-64 overflow-y-auto space-y-2 text-xs font-sans">
<!-- Dynamically populated -->
</div>
</div>
</div>
</header>
<!-- App Body (Sidebar and main SVG area) -->
<div class="grow flex flex-col md:flex-row overflow-hidden relative">
<!-- Left Panel: Text Code Editor -->
<div id="editor-panel" class="w-full md:w-[420px] md:min-w-[320px] md:max-w-[70vw] shrink-0 border-r border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 flex flex-col h-1/3 md:h-full z-10 font-sans md:resize-x md:overflow-auto">
<!-- Editor Header -->
<div class="px-4 py-2 bg-slate-50 dark:bg-slate-800/50 border-b border-slate-200 dark:border-slate-700 flex items-center justify-between shrink-0">
<span class="text-xs font-semibold tracking-wider text-slate-500 dark:text-slate-400 uppercase flex items-center">
<span class="material-symbols-outlined text-sm mr-1.5 text-brand-500">code</span> Diagram Syntax
</span>
<span id="parsing-badge" class="px-2 py-0.5 rounded-full text-[10px] font-medium bg-emerald-100 text-emerald-800 dark:bg-emerald-950/50 dark:text-emerald-300 flex items-center">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-500 mr-1 animate-pulse"></span> Sync Active
</span>
</div>
<div id="editor-content" class="grow flex flex-col min-h-0">
<!-- Real Code Area -->
<div class="grow flex flex-col relative font-mono text-xs overflow-hidden">
<div class="grow min-h-0 flex overflow-hidden bg-slate-900">
<div class="w-16 shrink-0 border-r border-slate-800 bg-slate-950/80 text-slate-500 text-[11px] leading-relaxed select-none overflow-hidden">
<div id="lineNumbersContent" class="py-4"></div>
</div>
<!-- Textarea for input -->
<textarea id="syntaxInput" placeholder="# Type here or choose a snippet below..." spellcheck="false" class="w-full grow min-h-[220px] overflow-auto p-4 bg-slate-900 text-slate-100 border-none focus:outline-none focus:ring-0 resize-y font-mono leading-relaxed text-[11px]" oninput="handleInputChange()"></textarea>
</div>
<!-- Sync Error Popover (opened by clicking the inline error icon in the gutter) -->
<div id="error-popover" class="hidden fixed z-50 w-72 max-w-[calc(100vw-2rem)] bg-white dark:bg-slate-800 border border-rose-200 dark:border-rose-900 rounded-xl shadow-2xl p-3.5 text-xs">
<div class="flex items-start gap-2">
<span class="material-symbols-outlined text-rose-500 text-base shrink-0 mt-0.5">error</span>
<div class="grow">
<p class="font-bold text-slate-800 dark:text-white">Syntax Hint</p>
<p id="error-message" class="text-slate-600 dark:text-slate-300 mt-0.5 break-words"></p>
</div>
<button onclick="closeErrorPopover()" class="text-slate-400 hover:text-slate-600 dark:hover:text-white shrink-0 flex items-center" title="Dismiss">
<span class="material-symbols-outlined text-base">close</span>
</button>
</div>
</div>
</div>
<!-- Editor Toolbar Helper -->
<div class="p-3 bg-slate-50 dark:bg-slate-800/80 border-t border-slate-200 dark:border-slate-700 flex flex-wrap gap-1.5 shrink-0">
<button onclick="insertTemplate('title')" class="px-2 py-1 bg-white dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 border border-slate-200 dark:border-slate-600 rounded text-[10px] font-semibold flex items-center" title="Define the main map title header">
<span class="w-2 h-2 rounded bg-indigo-400 mr-1"></span> +Title Tag
</button>
<button onclick="insertTemplate('persona')" class="px-2 py-1 bg-white dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 border border-slate-200 dark:border-slate-600 rounded text-[10px] font-semibold flex items-center" title="Declare a system persona profile">
<span class="w-2 h-2 rounded bg-pink-400 mr-1"></span> +Persona Tag
</button>
<button onclick="insertTemplate('element')" class="px-2 py-1 bg-white dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 border border-slate-200 dark:border-slate-600 rounded text-[10px] font-semibold flex items-center" title="Define a generic shared component">
<span class="w-2 h-2 rounded bg-slate-400 mr-1"></span> +Decl element
</button>
<button onclick="insertTemplate('page')" class="px-2 py-1 bg-white dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 border border-slate-200 dark:border-slate-600 rounded text-[10px] font-semibold flex items-center">
<span class="w-2 h-2 rounded bg-indigo-500 mr-1"></span> +Page
</button>
<button onclick="insertTemplate('group')" class="px-2 py-1 bg-white dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 border border-slate-200 dark:border-slate-600 rounded text-[10px] font-semibold flex items-center">
<span class="w-2 h-2 rounded bg-amber-500 mr-1"></span> +Group
</button>
<button onclick="insertTemplate('field')" class="px-2 py-1 bg-white dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 border border-slate-200 dark:border-slate-600 rounded text-[10px] font-semibold flex items-center">
<span class="w-2 h-2 rounded bg-sky-500 mr-1"></span> +Field
</button>
<button onclick="insertTemplate('button')" class="px-2 py-1 bg-white dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 border border-slate-200 dark:border-slate-600 rounded text-[10px] font-semibold flex items-center">
<span class="w-2 h-2 rounded bg-purple-500 mr-1"></span> +Button
</button>
<button onclick="insertTemplate('decision')" class="px-2 py-1 bg-white dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 border border-slate-200 dark:border-slate-600 rounded text-[10px] font-semibold flex items-center" title="Insert a VisVocab decision point">
<span class="w-2 h-2 rotate-45 bg-rose-400 mr-1"></span> +Decision
</button>
<button onclick="insertTemplate('branch')" class="px-2 py-1 bg-white dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 border border-slate-200 dark:border-slate-600 rounded text-[10px] font-semibold flex items-center" title="Insert a conditional branch">
<span class="w-0 h-0 border-l-[5px] border-r-[5px] border-b-[8px] border-l-transparent border-r-transparent border-b-amber-400 mr-1"></span> +Branch
</button>
<button onclick="insertTemplate('reference')" class="px-2 py-1 bg-white dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 border border-slate-200 dark:border-slate-600 rounded text-[10px] font-semibold flex items-center" title="Insert a reusable flow reference">
<span class="w-2 h-2 bg-violet-400 mr-1" style="clip-path: polygon(10% 0%, 90% 0%, 100% 24%, 100% 76%, 90% 100%, 10% 100%, 0% 76%, 0% 24%);"></span> +Flow Ref
</button>
<span class="grow"></span>
<button onclick="clearEditor()" class="px-2 py-1 bg-rose-50 dark:bg-rose-950/30 text-rose-600 hover:bg-rose-100 dark:hover:bg-rose-900/50 rounded text-[10px] font-semibold" title="Clear Editor">
Clear
</button>
</div>
</div>
</div>
<!-- Center & Right Panels: Tree Canvas + Collapsible Inspector -->
<div class="grow flex h-2/3 md:h-full overflow-hidden relative font-sans">
<!-- Main Tree SVG Area -->
<div class="grow h-full relative overflow-hidden">
<!-- SVG Container Layer -->
<div id="canvas-container" class="w-full h-full relative overflow-hidden select-none cursor-grab active:cursor-grabbing grid-bg-light dark:grid-bg-dark">
<!-- Main Interactive SVG -->
<svg id="diagramSvg" class="w-full h-full absolute inset-0">
<!-- Definitions for markers and filters -->
<defs>
<filter id="shadow" x="-10%" y="-10%" width="120%" height="120%">
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#0f172a" flood-opacity="0.08" />
</filter>
<filter id="shadow-selected" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="#6366f1" flood-opacity="0.3" />
</filter>
<!-- Glowing line filter -->
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="3" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- Arrow Marker -->
<marker id="arrow" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 1 L 10 5 L 0 9 z" fill="#cbd5e1" />
</marker>
<marker id="arrow-dark" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 1 L 10 5 L 0 9 z" fill="#475569" />
</marker>
<!-- Nav Arrow Marker (href target pointers) -->
<marker id="nav-arrow" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 1 L 10 5 L 0 9 z" fill="#0ea5e9" />
</marker>
</defs>
<!-- Transformable group for panning and zooming -->
<g id="viewport">
<!-- Grid Line connections (The visual tree branches) -->
<g id="tree-branches"></g>
<!-- Connection paths overlaying duplicate items -->
<g id="cross-connections" class="opacity-80"></g>
<!-- Navigation link lines for href -->
<g id="nav-connections" class="opacity-80"></g>
<!-- Rendered Nodes of the Tree -->
<g id="tree-nodes"></g>
</g>
</svg>
<!-- Quick Format Guide shown when Canvas is empty -->
<div id="empty-state" class="absolute inset-0 flex items-center justify-center p-6 bg-slate-50/95 dark:bg-slate-900/98 overflow-y-auto pointer-events-auto">
<div class="max-w-xl w-full bg-white dark:bg-slate-800 rounded-2xl shadow-xl border border-slate-100 dark:border-slate-700 p-6 sm:p-8 space-y-6 overflow-hidden font-sans">
<div class="text-center space-y-2 font-sans">
<div class="mx-auto w-12 h-12 bg-indigo-50 dark:bg-indigo-950 text-indigo-500 rounded-xl flex items-center justify-center shadow-inner">
<span class="material-symbols-outlined text-3xl animate-spin-slow">explore</span>
</div>
<h2 class="text-xl font-bold text-slate-800 dark:text-white">Information Architecture Canvas</h2>
<p class="text-sm text-slate-500 dark:text-slate-400">Map out your application's structure. Click any card to navigate your code instantly.</p>
</div>
<!-- Quick start guides list -->
<div class="space-y-3 font-sans">
<h3 class="text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500">Quick Formatting Guide</h3>
<div class="grid grid-cols-1 gap-1.5 text-xs">
<div class="flex items-start space-x-2.5 p-2 rounded-lg bg-slate-50 dark:bg-slate-900 border border-slate-100 dark:border-slate-800">
<span class="font-mono text-indigo-500 font-bold bg-white dark:bg-slate-800 px-1.5 py-0.5 rounded shadow-sm shrink-0">title: Name</span>
<span class="text-slate-600 dark:text-slate-300">Set the dynamic diagram name displayed on the top header.</span>
</div>
<div class="flex items-start space-x-2.5 p-2 rounded-lg bg-slate-50 dark:bg-slate-900 border border-slate-100 dark:border-slate-800">
<span class="font-mono text-indigo-500 font-bold bg-white dark:bg-slate-800 px-1.5 py-0.5 rounded shadow-sm shrink-0">href: card_id</span>
<span class="text-slate-600 dark:text-slate-300">Connects fields with navigational links (e.g. {id: link_a, href: billing_page}).</span>
</div>
<div class="flex items-start space-x-2.5 p-2 rounded-lg bg-slate-50 dark:bg-slate-900 border border-slate-100 dark:border-slate-800">
<span class="font-mono text-indigo-500 font-bold bg-white dark:bg-slate-800 px-1.5 py-0.5 rounded shadow-sm shrink-0">comment: "Hint"</span>
<span class="text-slate-600 dark:text-slate-300">Displays a modern info-icon helper with interactive hover text.</span>
</div>
<div class="flex items-start space-x-2.5 p-2 rounded-lg bg-slate-50 dark:bg-slate-900 border border-slate-100 dark:border-slate-800">
<span class="font-mono text-indigo-500 font-bold bg-white dark:bg-slate-800 px-1.5 py-0.5 rounded shadow-sm shrink-0">persona: User</span>
<span class="text-slate-600 dark:text-slate-300">Declares a system user persona. Generates an interactive dropdown filter on the canvas.</span>
</div>
<div class="flex items-start space-x-2.5 p-2 rounded-lg bg-slate-50 dark:bg-slate-900 border border-slate-100 dark:border-slate-800">
<span class="font-mono text-indigo-500 font-bold bg-white dark:bg-slate-800 px-1.5 py-0.5 rounded shadow-sm shrink-0">decision: Valid?</span>
<span class="text-slate-600 dark:text-slate-300">Use <strong>decision</strong>, <strong>branch</strong>, <strong>selector</strong>, <strong>cluster</strong>, <strong>flow</strong>, and <strong>reference</strong> to model conditional flow and navigation.</span>
</div>
<div class="flex items-start space-x-2.5 p-2 rounded-lg bg-slate-50 dark:bg-slate-900 border border-slate-100 dark:border-slate-800">
<span class="font-mono text-indigo-500 font-bold bg-white dark:bg-slate-800 px-1.5 py-0.5 rounded shadow-sm shrink-0">condition: "yes"</span>
<span class="text-slate-600 dark:text-slate-300">Add <strong>condition</strong> or <strong>action</strong> inside a child node attributes block to label its incoming connector.</span>
</div>
<div class="flex items-start space-x-2.5 p-2 rounded-lg bg-slate-50 dark:bg-slate-900 border border-slate-100 dark:border-slate-800">
<span class="font-mono text-indigo-500 font-bold bg-white dark:bg-slate-800 px-1.5 py-0.5 rounded shadow-sm shrink-0">Tab Key</span>
<span class="text-slate-600 dark:text-slate-300">Press <strong>Tab</strong> inside the editor to cleanly indent child branches.</span>
</div>
</div>
</div>
<!-- Quick Demos / Templates -->
<div class="pt-2 border-t border-slate-100 dark:border-slate-700 font-sans">
<p class="text-xs font-bold text-slate-400 dark:text-slate-500 uppercase tracking-wider mb-2.5">Click to Load a Kickstart Template</p>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2">
<button onclick="loadTemplate('ecom')" class="p-2 bg-indigo-50 hover:bg-indigo-100 dark:bg-indigo-950/40 dark:hover:bg-indigo-950/80 border border-indigo-100 dark:border-indigo-900/50 rounded-xl text-left transition-colors">
<div class="font-bold text-xs text-indigo-700 dark:text-indigo-300">🛒 Checkout Flow</div>
<div class="text-[10px] text-indigo-600/70 dark:text-indigo-400/70 mt-0.5">Payment steps & persona filters</div>
</button>
<button onclick="loadTemplate('saas')" class="p-2 bg-amber-50 hover:bg-amber-100 dark:bg-amber-950/40 dark:hover:bg-amber-950/80 border border-amber-100 dark:border-amber-900/50 rounded-xl text-left transition-colors">
<div class="font-bold text-xs text-amber-700 dark:text-amber-300 font-bold">📊 SaaS Dashboard</div>
<div class="text-[10px] text-amber-600/70 dark:text-indigo-400/70 mt-0.5">Settings panels & shared buttons</div>
</button>
<button onclick="loadTemplate('simple')" class="p-2 bg-emerald-50 hover:bg-emerald-100 dark:bg-emerald-950/40 dark:hover:bg-emerald-950/80 border border-emerald-100 dark:border-emerald-900/50 rounded-xl text-left transition-colors">
<div class="font-bold text-xs text-emerald-700 dark:text-emerald-300">🌱 Simple Form</div>
<div class="text-[10px] text-emerald-600/70 dark:text-indigo-400/70 mt-0.5">Quick form with unique fields</div>
</button>
<button onclick="loadTemplate('conditional')" class="p-2 bg-rose-50 hover:bg-rose-100 dark:bg-rose-950/40 dark:hover:bg-rose-950/80 border border-rose-100 dark:border-rose-900/50 rounded-xl text-left transition-colors">
<div class="font-bold text-xs text-rose-700 dark:text-rose-300">🔀 Conditional Flow</div>
<div class="text-[10px] text-rose-600/70 dark:text-rose-400/70 mt-0.5">Decision points, selectors & reusable flows</div>
</button>
<button onclick="loadTemplate('selectorcluster')" class="p-2 bg-fuchsia-50 hover:bg-fuchsia-100 dark:bg-fuchsia-950/40 dark:hover:bg-fuchsia-950/80 border border-fuchsia-100 dark:border-fuchsia-900/50 rounded-xl text-left transition-colors">
<div class="font-bold text-xs text-fuchsia-700 dark:text-fuchsia-300">🔢 Selector & Cluster</div>
<div class="text-[10px] text-fuchsia-600/70 dark:text-fuchsia-400/70 mt-0.5">One-or-more paths & grouped sets</div>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Collapsible Right Panel: Filter & Inspect -->
<div id="inspector-panel" class="hidden w-[280px] shrink-0 border-l border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 flex flex-col h-full z-10 font-sans shadow-xl">
<!-- Panel Header -->
<div class="px-4 py-3 bg-slate-50 dark:bg-slate-800/50 border-b border-slate-200 dark:border-slate-700 flex items-center justify-between shrink-0">
<span class="text-xs font-bold tracking-wider text-slate-500 dark:text-slate-400 uppercase flex items-center">
<span class="material-symbols-outlined text-sm mr-1.5 text-brand-500">travel_explore</span> Filter & Inspect
</span>
<button onclick="toggleInspector()" class="text-slate-400 hover:text-slate-600 dark:hover:text-white flex items-center">
<span class="material-symbols-outlined text-lg">close</span>
</button>
</div>
<!-- Filter Options lists (Statuses, Flags, Personas) -->
<div class="grow overflow-y-auto p-4 space-y-5">
<!-- Active Status Category -->
<div id="inspector-section-status" class="space-y-2">
<h3 class="text-xs font-bold text-slate-400 uppercase tracking-wider">Statuses</h3>
<div id="inspector-status-list" class="space-y-1 text-xs font-sans">
<!-- Dynamically populated -->
</div>
</div>
<!-- Active Flags Category -->
<div id="inspector-section-flags" class="space-y-2 pt-3 border-t border-slate-100 dark:border-slate-700">
<h3 class="text-xs font-bold text-slate-400 uppercase tracking-wider">Active Flags</h3>
<div id="inspector-flags-list" class="space-y-1 text-xs font-sans">
<!-- Dynamically populated -->
</div>
</div>
<!-- Active Personas Category -->
<div id="inspector-section-personas" class="space-y-2 pt-3 border-t border-slate-100 dark:border-slate-700">
<h3 class="text-xs font-bold text-slate-400 uppercase tracking-wider">Persona Access</h3>
<div id="inspector-personas-list" class="space-y-1 text-xs font-sans">
<!-- Dynamically populated -->
</div>
</div>
</div>
</div>
<!-- Fully Restored Overlay Action Panel (Floating Canvas Controls) -->
<div class="absolute bottom-4 right-4 z-20 flex items-center space-x-1.5 bg-white dark:bg-slate-800 p-1.5 shadow-lg border border-slate-200 dark:border-slate-700 rounded-xl backdrop-blur-sm">
<button onclick="zoomOut()" class="p-2 rounded-lg bg-slate-50 dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 text-slate-600 dark:text-slate-300 transition-colors flex items-center justify-center" title="Zoom Out">
<span class="material-symbols-outlined text-sm">zoom_out</span>
</button>
<!-- Zoom Preset Select Menu -->
<div class="relative">
<select id="zoomSelect" onchange="setZoomFromMenu(this.value)" class="bg-slate-50 dark:bg-slate-700 text-slate-700 dark:text-slate-200 border border-slate-200 dark:border-slate-600 rounded-lg px-2 py-1 text-xs font-semibold hover:bg-slate-100 dark:hover:bg-slate-600 focus:outline-none focus:ring-1 focus:ring-brand-500 cursor-pointer appearance-none pr-6 text-center">
<option value="0.25">25%</option>
<option value="0.50">50%</option>
<option value="0.75">75%</option>
<option value="1.00">100%</option>
<option value="1.25">125%</option>
<option value="1.50">150%</option>
<option value="2.00">200%</option>
</select>
<span class="material-symbols-outlined absolute right-1 top-1/2 -translate-y-1/2 text-[12px] pointer-events-none text-slate-400">arrow_drop_down</span>
</div>
<button onclick="zoomIn()" class="p-2 rounded-lg bg-slate-50 dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 text-slate-600 dark:text-slate-300 transition-colors flex items-center justify-center" title="Zoom In">
<span class="material-symbols-outlined text-sm">zoom_in</span>
</button>
<div class="w-[1px] h-6 bg-slate-200 dark:bg-slate-600 mx-1"></div>
<!-- Focus on main card -->
<button onclick="focusMainCard()" class="p-2 rounded-lg bg-slate-50 dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 text-slate-600 dark:text-slate-300 transition-colors flex items-center justify-center" title="Focus on Main/Root Card">
<span class="material-symbols-outlined text-sm">center_focus_strong</span>
</button>
<div class="w-[1px] h-6 bg-slate-200 dark:bg-slate-600 mx-1"></div>
<!-- Expand / Collapse Contextual Actions (Google Material expand_content / collapse_content) -->
<button onclick="handleGlobalExpand()" id="btn-global-expand" class="p-2 rounded-lg bg-slate-50 dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 text-slate-600 dark:text-slate-300 transition-colors flex items-center justify-center" title="Expand All (Whole Model)">
<span class="material-symbols-outlined text-sm">expand_content</span>
</button>
<button onclick="handleGlobalCollapse()" id="btn-global-collapse" class="p-2 rounded-lg bg-slate-50 dark:bg-slate-700 hover:bg-slate-100 dark:hover:bg-slate-600 text-slate-600 dark:text-slate-300 transition-colors flex items-center justify-center" title="Collapse All (Whole Model)">
<span class="material-symbols-outlined text-sm">collapse_content</span>
</button>
<div class="w-[1px] h-6 bg-slate-200 dark:bg-slate-600 mx-1"></div>
<button onclick="exportToPNG()" class="px-2.5 py-1.5 rounded-lg bg-brand-500 hover:bg-brand-600 text-white text-xs font-semibold flex items-center space-x-1 transition-colors" title="Download PNG of whole diagram">
<span class="material-symbols-outlined text-sm">download</span>
<span>Export PNG</span>
</button>
</div>
</div>
</div>
<!-- Help Modal & Syntax Cheat Sheet -->
<div id="helpModal" class="hidden fixed inset-0 z-50 overflow-auto bg-slate-900/50 backdrop-blur-sm flex items-center justify-center p-4">
<div class="bg-white dark:bg-slate-800 rounded-2xl max-w-2xl w-full max-h-[85vh] overflow-hidden shadow-2xl flex flex-col animate-in fade-in zoom-in duration-200 font-sans">
<!-- Modal Header -->
<div class="px-6 py-4 border-b border-slate-200 dark:border-slate-700 flex items-center justify-between bg-slate-50 dark:bg-slate-800/50">
<div class="flex items-center space-x-2">
<span class="material-symbols-outlined text-indigo-500 text-lg mr-1">book</span>
<h3 class="text-md font-bold text-slate-900 dark:text-white font-sans">Diagram Syntax Reference Guide</h3>
</div>
<button onclick="toggleHelpModal(false)" class="text-slate-400 hover:text-slate-600 dark:hover:text-white transition-colors flex items-center">
<span class="material-symbols-outlined">close</span>
</button>
</div>
<!-- Modal Body (Scrollable) -->
<div class="p-6 overflow-y-auto space-y-6 text-sm leading-relaxed text-slate-600 dark:text-slate-300">
<div>
<h4 class="font-bold text-slate-900 dark:text-white mb-2 flex items-center font-sans">
<span class="bg-brand-100 text-brand-700 dark:bg-brand-950 dark:text-brand-300 w-5 h-5 rounded-full inline-flex items-center justify-center text-xs mr-2 font-bold">1</span>
Diagram Title
</h4>
<p class="text-xs mb-2">Use the <code class="text-indigo-500 font-semibold font-mono">title:</code> tag at the start of your file to set the header title at the top of the interface screen.</p>
<pre class="bg-slate-950 text-slate-100 p-3 rounded-lg text-xs leading-5">title: My Checkout Process Flow</pre>
</div>
<div>
<h4 class="font-bold text-slate-900 dark:text-white mb-2 flex items-center font-sans">
<span class="bg-brand-100 text-brand-700 dark:bg-brand-950 dark:text-brand-300 w-5 h-5 rounded-full inline-flex items-center justify-center text-xs mr-2 font-bold">2</span>
User Personas & Role Filtering
</h4>
<p class="text-xs mb-2">Declare roles to filter complex architectures for different users. Use <code class="text-indigo-500 font-semibold font-mono">persona: Name</code> globally, then restrict elements:</p>
<pre class="bg-slate-950 text-slate-100 p-3 rounded-lg text-xs leading-5">
persona: Customer
persona: Admin
page: Dashboard
button: Standard Action
button: Administrative Refund {personas: [Admin]}
</pre>
</div>
<div>
<h4 class="font-bold text-slate-900 dark:text-white mb-2 flex items-center font-sans">
<span class="bg-brand-100 text-brand-700 dark:bg-brand-950 dark:text-brand-300 w-5 h-5 rounded-full inline-flex items-center justify-center text-xs mr-2 font-bold">3</span>
Hierarchical Indentation
</h4>
<p class="text-xs mb-2">Build your tree levels using spaces or tabs at the start of each line (2 spaces per indent layer is recommended).</p>
<pre class="bg-slate-950 text-slate-100 p-3 rounded-lg text-xs leading-5">
page: Homepage
group: Header Section
button: Login {id: auth_btn}</pre>
</div>
<div>
<h4 class="font-bold text-slate-900 dark:text-white mb-2 flex items-center font-sans">
<span class="bg-brand-100 text-brand-700 dark:bg-brand-950 dark:text-brand-300 w-5 h-5 rounded-full inline-flex items-center justify-center text-xs mr-2 font-bold">4</span>
Advanced Element Reusability, Navigation Links & Comments
</h4>
<p class="text-xs mb-2">InfoArch supports powerful model declarations. You can specify comments (help tooltips), glossary descriptions, and navigation pointer paths using `href`:</p>
<ul class="list-disc pl-5 text-xs space-y-1.5 mb-3">
<li><strong>Navigation Links (`href`):</strong> Use ID tags to represent navigation pointers between distinct areas of your IA diagram:
<pre class="bg-slate-950 text-slate-100 p-2 rounded-lg text-[11px] mt-1">button: Proceed to Pay {id: proceed_btn, href: payment_view}</pre>
</li>
<li><strong>Explanatory Help tooltips (`comment`):</strong> Write descriptive guides on nodes using quotes:
<pre class="bg-slate-950 text-slate-100 p-2.5 rounded-lg text-[11px] mt-1">input: Promo Code {id: discount, comment: "Applies 10% coupon codes"}</pre>
</li>
<li><strong>Glossary descriptions (`description`):</strong> Add descriptions to declared personas and elements so they appear in the floating glossary:
<pre class="bg-slate-950 text-slate-100 p-2.5 rounded-lg text-[11px] mt-1">element: Promo Code {id: discount, description: "Reusable coupon input"}
persona: Customer {description: "Shops and completes checkout"}</pre>
</li>
</ul>
</div>
<div>
<h4 class="font-bold text-slate-900 dark:text-white mb-2 flex items-center font-sans">
<span class="bg-brand-100 text-brand-700 dark:bg-brand-950 dark:text-brand-300 w-5 h-5 rounded-full inline-flex items-center justify-center text-xs mr-2 font-bold">5</span>
Conditional Flow Controls & Navigation Shapes
</h4>
<p class="text-xs mb-2">Use VisVocab-inspired keywords to describe decisions, conditional branches, selectors, reusable flows, and navigation handoffs. Add <code class="text-indigo-500 font-semibold font-mono">condition</code> or <code class="text-indigo-500 font-semibold font-mono">action</code> inside child node attributes to label the incoming path.</p>
<pre class="bg-slate-950 text-slate-100 p-3 rounded-lg text-xs leading-5">
page: Sign In
decision: Credentials valid?
page: Dashboard {condition: "yes"}
branch: Recovery route {condition: "no"}
page: Reset Password {condition: "known email"}
continue: Contact Support {condition: "account locked"}
flow: MFA Flow {id: mfa_flow}
entry: One-time code
selector: Delivery channel
page: SMS Verify {condition: "sms enabled"}
page: Email Verify {condition: "email enabled"}
</pre>
<p class="text-xs mt-3 mb-2 font-bold text-slate-700 dark:text-slate-200">Flow keyword reference — an example of each option</p>
<div class="space-y-1 text-xs">
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 p-1.5 rounded-lg bg-slate-50 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800">
<code class="shrink-0 sm:w-24 font-mono font-bold text-rose-600 dark:text-rose-300">decision</code>
<span class="grow text-slate-600 dark:text-slate-300">Diamond. After an action, the system picks one result.</span>
<code class="shrink-0 font-mono text-[11px] text-slate-100 bg-slate-950 px-2 py-0.5 rounded">decision: Credentials valid?</code>
</div>
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 p-1.5 rounded-lg bg-slate-50 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800">
<code class="shrink-0 sm:w-24 font-mono font-bold text-amber-600 dark:text-amber-300">branch</code>
<span class="grow text-slate-600 dark:text-slate-300">Triangle. Exactly one path is presented (mutually exclusive).</span>
<code class="shrink-0 font-mono text-[11px] text-slate-100 bg-slate-950 px-2 py-0.5 rounded">branch: Recovery route {condition: "no"}</code>
</div>
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 p-1.5 rounded-lg bg-slate-50 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800">
<code class="shrink-0 sm:w-24 font-mono font-bold text-cyan-600 dark:text-cyan-300">selector</code>
<span class="grow text-slate-600 dark:text-slate-300">Trapezoid. One <strong>or more</strong> matching paths are presented.</span>
<code class="shrink-0 font-mono text-[11px] text-slate-100 bg-slate-950 px-2 py-0.5 rounded">selector: Search results</code>
</div>
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 p-1.5 rounded-lg bg-slate-50 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800">
<code class="shrink-0 sm:w-24 font-mono font-bold text-fuchsia-600 dark:text-fuchsia-300">cluster</code>
<span class="grow text-slate-600 dark:text-slate-300">Circle. One condition presents all its children together.</span>
<code class="shrink-0 font-mono text-[11px] text-slate-100 bg-slate-950 px-2 py-0.5 rounded">cluster: Premium bundle {condition: "premium"}</code>
</div>
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 p-1.5 rounded-lg bg-slate-50 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800">
<code class="shrink-0 sm:w-24 font-mono font-bold text-indigo-600 dark:text-indigo-300">flow</code>
<span class="grow text-slate-600 dark:text-slate-300">Reusable sequence container (like a subroutine). Give it an <code class="font-mono">id</code>.</span>
<code class="shrink-0 font-mono text-[11px] text-slate-100 bg-slate-950 px-2 py-0.5 rounded">flow: MFA Flow {id: mfa_flow}</code>
</div>
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 p-1.5 rounded-lg bg-slate-50 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800">
<code class="shrink-0 sm:w-24 font-mono font-bold text-violet-600 dark:text-violet-300">reference</code>
<span class="grow text-slate-600 dark:text-slate-300">Placeholder that reuses a flow via <code class="font-mono">href</code>.</span>
<code class="shrink-0 font-mono text-[11px] text-slate-100 bg-slate-950 px-2 py-0.5 rounded">reference: MFA Flow {href: mfa_flow}</code>
</div>
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 p-1.5 rounded-lg bg-slate-50 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800">
<code class="shrink-0 sm:w-24 font-mono font-bold text-emerald-600 dark:text-emerald-300">entry</code>
<span class="grow text-slate-600 dark:text-slate-300">Entry point where a flow begins.</span>
<code class="shrink-0 font-mono text-[11px] text-slate-100 bg-slate-950 px-2 py-0.5 rounded">entry: One-time code</code>
</div>
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 p-1.5 rounded-lg bg-slate-50 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800">
<code class="shrink-0 sm:w-24 font-mono font-bold text-orange-600 dark:text-orange-300">exit</code>
<span class="grow text-slate-600 dark:text-slate-300">Exit point where a flow hands control back.</span>
<code class="shrink-0 font-mono text-[11px] text-slate-100 bg-slate-950 px-2 py-0.5 rounded">exit: Resume sign in {condition: "verified"}</code>
</div>
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 p-1.5 rounded-lg bg-slate-50 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800">
<code class="shrink-0 sm:w-24 font-mono font-bold text-slate-600 dark:text-slate-300">continue</code>
<span class="grow text-slate-600 dark:text-slate-300">Continuation point bridging to another area or diagram.</span>
<code class="shrink-0 font-mono text-[11px] text-slate-100 bg-slate-950 px-2 py-0.5 rounded">continue: Contact Support {condition: "locked"}</code>
</div>
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 p-1.5 rounded-lg bg-slate-50 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800">
<code class="shrink-0 sm:w-24 font-mono font-bold text-sky-600 dark:text-sky-300">concurrent</code>
<span class="grow text-slate-600 dark:text-slate-300">Half-circle. One action generates several simultaneous results.</span>
<code class="shrink-0 font-mono text-[11px] text-slate-100 bg-slate-950 px-2 py-0.5 rounded">concurrent: Notify & redirect</code>
</div>
<div class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 p-1.5 rounded-lg bg-slate-50 dark:bg-slate-900/60 border border-slate-100 dark:border-slate-800">
<code class="shrink-0 sm:w-24 font-mono font-bold text-slate-600 dark:text-slate-300">file</code>
<span class="grow text-slate-600 dark:text-slate-300">Dog-eared document. A non-navigational file (PDF, export, log).</span>
<code class="shrink-0 font-mono text-[11px] text-slate-100 bg-slate-950 px-2 py-0.5 rounded">file: Audit Log {action: "write event"}</code>
</div>
</div>
<div class="mt-4 rounded-lg border border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-900/60 p-3">
<p class="text-xs font-bold text-slate-700 dark:text-slate-200 mb-2">Choice semantics — how many downstream paths are presented</p>
<ul class="text-xs space-y-2">
<li class="flex items-start gap-2">
<span class="shrink-0 mt-0.5 px-1.5 py-0.5 rounded-full text-[10px] font-bold bg-rose-100 text-rose-700 dark:bg-rose-950 dark:text-rose-200 border border-rose-300 dark:border-rose-800">1 of</span>
<span class="text-slate-600 dark:text-slate-300"><code class="text-indigo-500 font-semibold font-mono">branch</code> — mutually exclusive. Exactly <strong>one</strong> of its paths is presented (dashed rose connectors).</span>
</li>
<li class="flex items-start gap-2">
<span class="shrink-0 mt-0.5 px-1.5 py-0.5 rounded-full text-[10px] font-bold bg-cyan-100 text-cyan-700 dark:bg-cyan-950 dark:text-cyan-200 border border-cyan-300 dark:border-cyan-800">1+ of</span>
<span class="text-slate-600 dark:text-slate-300"><code class="text-indigo-500 font-semibold font-mono">selector</code> — non-exclusive. <strong>One or more</strong> paths that meet the condition are presented (dashed cyan connectors). Ideal for search results.</span>
</li>
<li class="flex items-start gap-2">
<span class="shrink-0 mt-0.5 px-1.5 py-0.5 rounded-full text-[10px] font-bold bg-fuchsia-100 text-fuchsia-700 dark:bg-fuchsia-950 dark:text-fuchsia-200 border border-fuchsia-300 dark:border-fuchsia-800">all of</span>
<span class="text-slate-600 dark:text-slate-300"><code class="text-indigo-500 font-semibold font-mono">cluster</code> — one decision, many paths. Put the <code class="text-indigo-500 font-semibold font-mono">condition</code> on the cluster itself; <strong>all</strong> its children are then presented <strong>together</strong> (solid fuchsia connectors). Place a cluster downstream of a branch or selector.</span>
</li>
</ul>
<pre class="bg-slate-950 text-slate-100 p-3 rounded-lg text-[11px] leading-5 mt-3">branch: Account plan
page: Free tier {condition: "free"}
cluster: Premium bundle {condition: "premium"}
page: Advanced Dashboard
page: Priority Support
selector: Search results
page: Products {condition: "matches query"}
page: Articles {condition: "matches query"}
page: People {condition: "matches query"}</pre>
</div>
</div>
</div>
<!-- Modal Footer -->
<div class="px-6 py-4 bg-slate-50 dark:bg-slate-800/50 border-t border-slate-200 dark:border-slate-700 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<p class="text-xs text-slate-500 dark:text-slate-400">
<span class="material-symbols-outlined text-sm mr-1 text-brand-500 align-middle">mail</span>
<span class="align-middle">If you have any question, reach me by e-mail: <a href="mailto:contact@infographdiagram.org" class="font-semibold text-brand-600 dark:text-brand-300 hover:underline whitespace-nowrap">contact@infographdiagram.org</a></span>
</p>
<button onclick="toggleHelpModal(false)" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-xs font-bold transition-all font-sans shrink-0">
Got it, Let's Design!
</button>
</div>
</div>
</div>
<!-- Donate Modal -->
<div id="donateModal" class="hidden fixed inset-0 z-50 overflow-auto bg-slate-900/50 backdrop-blur-sm flex items-center justify-center p-4" onclick="if (event.target === this) toggleDonateModal(false)">
<div class="bg-white dark:bg-slate-800 rounded-2xl max-w-md w-full overflow-hidden shadow-2xl flex flex-col animate-in fade-in zoom-in duration-200 font-sans">
<!-- Modal Header -->
<div class="px-6 py-4 border-b border-slate-200 dark:border-slate-700 flex items-center justify-between bg-slate-50 dark:bg-slate-800/50">
<div class="flex items-center space-x-2">
<span class="material-symbols-outlined text-amber-500 text-lg mr-1">favorite</span>
<h3 class="text-md font-bold text-slate-900 dark:text-white font-sans">Support InfoArch</h3>
</div>
<button onclick="toggleDonateModal(false)" class="text-slate-400 hover:text-slate-600 dark:hover:text-white transition-colors flex items-center">
<span class="material-symbols-outlined">close</span>
</button>
</div>
<!-- Modal Body -->
<div class="p-6 space-y-4 text-sm leading-relaxed text-slate-600 dark:text-slate-300">
<p class="text-xs">InfoArch is free and open. If it's useful to you, consider supporting its development with a Bitcoin donation. Thank you! 🙏</p>
<div>
<p class="text-[10px] font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500 mb-1.5 flex items-center">
<span class="material-symbols-outlined text-sm mr-1 text-amber-500">currency_bitcoin</span> Bitcoin Address
</p>
<div class="flex items-stretch gap-2">
<code id="donate-address" class="grow bg-slate-950 text-slate-100 p-3 rounded-lg text-[11px] leading-5 break-all font-mono select-all">bc1qjldu5t8udj2sr6tdugmg0tc5ur77njteqqjm6y</code>
<button onclick="copyDonateAddress()" id="donate-copy-btn" class="shrink-0 px-3 rounded-lg bg-amber-500 hover:bg-amber-600 text-white text-xs font-semibold flex items-center justify-center transition-colors" title="Copy address to clipboard">
<span id="donate-copy-icon" class="material-symbols-outlined text-base">content_copy</span>
</button>
</div>
<p id="donate-copy-feedback" class="hidden text-[11px] font-semibold text-emerald-600 dark:text-emerald-400 mt-1.5">Address copied to clipboard!</p>
</div>
</div>
<!-- Modal Footer -->
<div class="px-6 py-4 bg-slate-50 dark:bg-slate-800/50 border-t border-slate-200 dark:border-slate-700 flex justify-end">
<button onclick="toggleDonateModal(false)" class="px-4 py-2 bg-slate-200 dark:bg-slate-700 hover:bg-slate-300 dark:hover:bg-slate-600 text-slate-700 dark:text-slate-200 rounded-lg text-xs font-bold transition-all font-sans">
Close
</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/html-to-image@1.11.11/dist/html-to-image.min.js"></script>
<script src="./app.js"></script>
</body>
</html>