dbdocs 1.6.1__tar.gz → 1.6.2__tar.gz
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.
- {dbdocs-1.6.1 → dbdocs-1.6.2}/PKG-INFO +1 -1
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/bundle/assets/css/style.css +177 -1
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/bundle/assets/js/app.js +4 -4
- dbdocs-1.6.2/dbdocs/site/bundle/assets/js/ui/dom.js +26 -0
- dbdocs-1.6.2/dbdocs/site/bundle/assets/js/ui/overlays.js +265 -0
- {dbdocs-1.6.1/dbdocs/site/bundle/assets/js → dbdocs-1.6.2/dbdocs/site/bundle/assets/js/ui}/ui.js +70 -201
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/bundle/index.html +2 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/.gitignore +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/LICENSE +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/README.md +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/__init__.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/__main__.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/cli/__init__.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/cli/main.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/core/__init__.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/core/artifacts.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/core/config.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/core/exceptions.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/core/log.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/__init__.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/_sqlglot_lineage.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/column_lineage.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/erd.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/graph.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/__init__.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/dimensions.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/extractor.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/rules/__init__.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/rules/base.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/rules/dimensions/__init__.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/rules/dimensions/documentation.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/rules/dimensions/governance.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/rules/dimensions/modeling.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/rules/dimensions/performance.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/rules/dimensions/structure.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/rules/dimensions/testing.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/health/rules/registry.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/nodes.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/extract/tests_index.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/main.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/__init__.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/api_schema.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/builder.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/bundle/assets/css/icons.css +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/bundle/assets/favicon.svg +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/bundle/assets/graph/index.css +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/bundle/assets/graph/index.js +0 -0
- {dbdocs-1.6.1/dbdocs/site/bundle/assets/js → dbdocs-1.6.2/dbdocs/site/bundle/assets/js/data}/data.js +0 -0
- {dbdocs-1.6.1/dbdocs/site/bundle/assets/js → dbdocs-1.6.2/dbdocs/site/bundle/assets/js/service}/service.js +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/bundle/assets/vendor/marked.min.js +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/bundle/assets/vendor/minisearch.min.js +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/deploy.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/dbdocs/site/inject.py +0 -0
- {dbdocs-1.6.1 → dbdocs-1.6.2}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbdocs
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.2
|
|
4
4
|
Summary: Alternative dbt docs site: Catalog + ERD + column-level lineage
|
|
5
5
|
Project-URL: Homepage, https://github.com/datnguye/dbt-docs
|
|
6
6
|
Project-URL: Repository, https://github.com/datnguye/dbt-docs
|
|
@@ -87,7 +87,7 @@ code, pre { font-family: "SF Mono", "Roboto Mono", Menlo, Consolas, monospace; }
|
|
|
87
87
|
.brand-generated { color: var(--text-soft); font-size: 12px; font-weight: 400; white-space: nowrap; }
|
|
88
88
|
.search-wrap { position: relative; flex: 1; max-width: 520px; }
|
|
89
89
|
#search {
|
|
90
|
-
width: 100%; padding: 7px 12px; border-radius: var(--radius);
|
|
90
|
+
width: 100%; padding: 7px 76px 7px 12px; border-radius: var(--radius);
|
|
91
91
|
border: 1px solid var(--border); background: var(--bg-soft); color: var(--text);
|
|
92
92
|
}
|
|
93
93
|
#search:focus { outline: none; border-color: var(--accent); }
|
|
@@ -599,6 +599,178 @@ a.score-chip:hover {
|
|
|
599
599
|
a.score-chip:hover .score-name { text-decoration: none; }
|
|
600
600
|
a.score-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
601
601
|
@media (prefers-reduced-motion: reduce) { .score-chip { transition: none; } a.score-chip:hover { transform: none; } }
|
|
602
|
+
|
|
603
|
+
/* ── theme transition ───────────────────────────────────────────────────────
|
|
604
|
+
A brief ease so toggling dark mode fades rather than snapping. Scoped only
|
|
605
|
+
to background/border/color so it doesn't make every interaction feel laggy.
|
|
606
|
+
Applied via the .theme-transition class that ui.js adds for 300ms then removes. */
|
|
607
|
+
.theme-transition,
|
|
608
|
+
.theme-transition *,
|
|
609
|
+
.theme-transition *::before,
|
|
610
|
+
.theme-transition *::after {
|
|
611
|
+
transition: background-color 0.25s ease, border-color 0.25s ease, color 0.2s ease !important;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/* ── page-mount micro-transition ────────────────────────────────────────────
|
|
615
|
+
Subtle fade+slide-up when route() replaces #app content (150ms, fast). */
|
|
616
|
+
@keyframes page-enter {
|
|
617
|
+
from { opacity: 0; transform: translateY(6px); }
|
|
618
|
+
to { opacity: 1; transform: translateY(0); }
|
|
619
|
+
}
|
|
620
|
+
.page-enter { animation: page-enter 0.15s ease-out both; }
|
|
621
|
+
|
|
622
|
+
/* ── graph skeleton loader ──────────────────────────────────────────────────
|
|
623
|
+
Shimmer gradient shown inside .graph-host while the React Flow bundle mounts.
|
|
624
|
+
Replaced by the graph once dbdocsGraph.mount() fires. */
|
|
625
|
+
@keyframes shimmer {
|
|
626
|
+
0% { background-position: -200% 0; }
|
|
627
|
+
100% { background-position: 200% 0; }
|
|
628
|
+
}
|
|
629
|
+
.graph-skeleton {
|
|
630
|
+
width: 100%; height: 480px; border-radius: var(--radius);
|
|
631
|
+
background: linear-gradient(90deg,
|
|
632
|
+
var(--bg-soft) 25%,
|
|
633
|
+
var(--bg-elev) 37%,
|
|
634
|
+
var(--bg-soft) 63%);
|
|
635
|
+
background-size: 200% 100%;
|
|
636
|
+
animation: shimmer 1.4s linear infinite;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/* ── toast notifications ────────────────────────────────────────────────────
|
|
640
|
+
Corner-anchored stack. Individual toasts slide in from the right, fade out.
|
|
641
|
+
The ARIA live region (#toast-region) is visually positioned at the bottom-right. */
|
|
642
|
+
#toast-region {
|
|
643
|
+
position: fixed; bottom: 24px; right: 24px; z-index: 200;
|
|
644
|
+
display: flex; flex-direction: column; gap: 8px; pointer-events: none;
|
|
645
|
+
max-width: 320px;
|
|
646
|
+
}
|
|
647
|
+
.toast {
|
|
648
|
+
display: flex; align-items: center; gap: 10px; pointer-events: none;
|
|
649
|
+
padding: 10px 16px; border-radius: var(--radius);
|
|
650
|
+
background: var(--bg-elev); border: 1px solid var(--border);
|
|
651
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.15); color: var(--text);
|
|
652
|
+
font-size: 13px; font-weight: 500;
|
|
653
|
+
animation: toast-in 0.2s ease-out both;
|
|
654
|
+
}
|
|
655
|
+
.toast.toast-out {
|
|
656
|
+
animation: toast-out 0.2s ease-in both;
|
|
657
|
+
}
|
|
658
|
+
@keyframes toast-in {
|
|
659
|
+
from { opacity: 0; transform: translateX(24px); }
|
|
660
|
+
to { opacity: 1; transform: translateX(0); }
|
|
661
|
+
}
|
|
662
|
+
@keyframes toast-out {
|
|
663
|
+
from { opacity: 1; transform: translateX(0); }
|
|
664
|
+
to { opacity: 0; transform: translateX(24px); }
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/* ── command palette ────────────────────────────────────────────────────────
|
|
668
|
+
Centered modal overlay with a frosted backdrop. */
|
|
669
|
+
#cmd-backdrop {
|
|
670
|
+
position: fixed; inset: 0; z-index: 100;
|
|
671
|
+
background: rgba(0,0,0,0.45);
|
|
672
|
+
display: flex; align-items: flex-start; justify-content: center;
|
|
673
|
+
padding-top: 15vh;
|
|
674
|
+
}
|
|
675
|
+
#cmd-palette {
|
|
676
|
+
width: min(640px, 92vw);
|
|
677
|
+
background: var(--bg-elev); border: 1px solid var(--border);
|
|
678
|
+
border-radius: calc(var(--radius) + 2px); box-shadow: 0 12px 48px rgba(0,0,0,0.24);
|
|
679
|
+
overflow: hidden;
|
|
680
|
+
animation: palette-in 0.14s ease-out both;
|
|
681
|
+
}
|
|
682
|
+
@keyframes palette-in {
|
|
683
|
+
from { opacity: 0; transform: scale(0.97) translateY(-8px); }
|
|
684
|
+
to { opacity: 1; transform: scale(1) translateY(0); }
|
|
685
|
+
}
|
|
686
|
+
.cmd-input-wrap {
|
|
687
|
+
display: flex; align-items: center; gap: 10px; padding: 12px 16px;
|
|
688
|
+
border-bottom: 1px solid var(--border);
|
|
689
|
+
}
|
|
690
|
+
.cmd-input-wrap .ic { flex-shrink: 0; }
|
|
691
|
+
#cmd-input {
|
|
692
|
+
flex: 1; border: none; outline: none; background: transparent;
|
|
693
|
+
font-size: 15px; color: var(--text); line-height: 1.4;
|
|
694
|
+
}
|
|
695
|
+
#cmd-input::placeholder { color: var(--text-soft); }
|
|
696
|
+
.cmd-kbd-close {
|
|
697
|
+
flex-shrink: 0; padding: 2px 6px; border-radius: 4px;
|
|
698
|
+
background: var(--bg-soft); border: 1px solid var(--border);
|
|
699
|
+
color: var(--text-soft); font-size: 11px; font-family: inherit;
|
|
700
|
+
}
|
|
701
|
+
#cmd-results {
|
|
702
|
+
max-height: 360px; overflow-y: auto;
|
|
703
|
+
padding: 4px 0;
|
|
704
|
+
}
|
|
705
|
+
.cmd-result {
|
|
706
|
+
display: flex; align-items: center; gap: 10px;
|
|
707
|
+
padding: 9px 16px; cursor: pointer; color: var(--text);
|
|
708
|
+
font-size: 13px; text-decoration: none;
|
|
709
|
+
}
|
|
710
|
+
.cmd-result:hover, .cmd-result.cmd-active {
|
|
711
|
+
background: var(--accent-soft); text-decoration: none;
|
|
712
|
+
}
|
|
713
|
+
.cmd-result-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
714
|
+
.cmd-result-meta { font-size: 11px; color: var(--text-soft); white-space: nowrap; flex-shrink: 0; }
|
|
715
|
+
.cmd-section-label {
|
|
716
|
+
padding: 6px 16px 2px; font-size: 10px; font-weight: 700; letter-spacing: .06em;
|
|
717
|
+
text-transform: uppercase; color: var(--text-soft);
|
|
718
|
+
}
|
|
719
|
+
.cmd-empty { padding: 20px 16px; text-align: center; color: var(--text-soft); font-size: 13px; }
|
|
720
|
+
|
|
721
|
+
/* ── ⌘K hint in the search box ──────────────────────────────────────────────
|
|
722
|
+
A small kbd chip rendered inside .search-wrap (absolute-positioned right edge). */
|
|
723
|
+
.cmd-hint {
|
|
724
|
+
position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
|
|
725
|
+
pointer-events: none;
|
|
726
|
+
display: flex; align-items: center; gap: 3px;
|
|
727
|
+
}
|
|
728
|
+
.cmd-hint kbd {
|
|
729
|
+
padding: 1px 5px; border-radius: 4px;
|
|
730
|
+
background: var(--bg-elev); border: 1px solid var(--border);
|
|
731
|
+
color: var(--text-soft); font-size: 10px; font-family: inherit;
|
|
732
|
+
line-height: 1.6;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/* ── sticky table headers ───────────────────────────────────────────────────
|
|
736
|
+
th in the Columns/Tests/other node-page tables stick at the top of their
|
|
737
|
+
scroll container (.content) on vertical scroll. background is explicitly set
|
|
738
|
+
so rows don't bleed through. */
|
|
739
|
+
.node-section-body th {
|
|
740
|
+
position: sticky; top: 0; z-index: 2;
|
|
741
|
+
background: var(--bg-elev); box-shadow: 0 1px 0 var(--border);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/* ── scroll-spy active section ──────────────────────────────────────────────
|
|
745
|
+
When IntersectionObserver marks a node-section as visible, its summary gets a
|
|
746
|
+
subtle left accent stripe via the .section-in-view modifier. */
|
|
747
|
+
details.node-section.section-in-view > summary.node-section-summary {
|
|
748
|
+
color: var(--accent);
|
|
749
|
+
}
|
|
750
|
+
details.node-section.section-in-view > summary.node-section-summary .node-section-title {
|
|
751
|
+
color: var(--accent);
|
|
752
|
+
}
|
|
753
|
+
details.node-section.section-in-view > summary.node-section-summary::before {
|
|
754
|
+
color: var(--accent);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/* ── Reduced-motion gate ────────────────────────────────────────────────────
|
|
758
|
+
Neutralize the palette/toast/page/skeleton/theme keyframes + transitions. */
|
|
759
|
+
@media (prefers-reduced-motion: reduce) {
|
|
760
|
+
.theme-transition,
|
|
761
|
+
.theme-transition *,
|
|
762
|
+
.theme-transition *::before,
|
|
763
|
+
.theme-transition *::after {
|
|
764
|
+
transition: none !important;
|
|
765
|
+
}
|
|
766
|
+
.page-enter { animation: none; }
|
|
767
|
+
.graph-skeleton { animation: none; background: var(--bg-soft); }
|
|
768
|
+
.toast { animation: none; }
|
|
769
|
+
.toast.toast-out { animation: none; opacity: 0; }
|
|
770
|
+
#cmd-palette { animation: none; }
|
|
771
|
+
@keyframes toast-in { from { opacity: 1; transform: none; } to { opacity: 1; transform: none; } }
|
|
772
|
+
@keyframes toast-out { from { opacity: 0; transform: none; } to { opacity: 0; transform: none; } }
|
|
773
|
+
}
|
|
602
774
|
.score-chip.good { border-left-color: var(--score-good); }
|
|
603
775
|
.score-chip.warn { border-left-color: var(--score-warn); }
|
|
604
776
|
.score-chip.bad { border-left-color: var(--score-bad); }
|
|
@@ -625,6 +797,10 @@ a.score-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2
|
|
|
625
797
|
.health-error { background: var(--status-error-bg); color: var(--status-error-fg); border-color: var(--status-error-bd); }
|
|
626
798
|
.health-skip { background: var(--bg-soft); color: var(--text-soft); border-color: var(--border); }
|
|
627
799
|
|
|
800
|
+
[data-theme="dark"] .toast { box-shadow: 0 4px 16px rgba(0,0,0,0.45); }
|
|
801
|
+
[data-theme="dark"] #cmd-palette { box-shadow: 0 12px 48px rgba(0,0,0,0.55); }
|
|
802
|
+
[data-theme="dark"] #cmd-backdrop { background: rgba(0,0,0,0.65); }
|
|
803
|
+
|
|
628
804
|
/* Hamburger + search toggle — hidden on desktop, revealed on phones below. */
|
|
629
805
|
.nav-toggle { display: none; font-size: 18px; }
|
|
630
806
|
.search-toggle { display: none; }
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
Three tiers, one-way dependency (ui → service → data):
|
|
4
4
|
data.js loads + normalizes the project data dict.
|
|
5
5
|
service.js pure domain logic over that dict (no DOM).
|
|
6
|
-
ui
|
|
6
|
+
ui/ all DOM rendering (dom.js primitives, ui.js renderer, overlays.js).
|
|
7
7
|
|
|
8
8
|
This entry is the only place the three meet: load the data, hand it to the
|
|
9
9
|
service tier, then boot the ui. Loaded as a native ES module (no build step).
|
|
10
10
|
The vendored libs (MiniSearch, marked) and the React Flow graph bundle remain
|
|
11
11
|
classic scripts that set globals, which the modules read directly. */
|
|
12
12
|
|
|
13
|
-
import { loadData } from "./data.js";
|
|
14
|
-
import * as svc from "./service.js";
|
|
15
|
-
import { boot } from "./ui.js";
|
|
13
|
+
import { loadData } from "./data/data.js";
|
|
14
|
+
import * as svc from "./service/service.js";
|
|
15
|
+
import { boot } from "./ui/ui.js";
|
|
16
16
|
|
|
17
17
|
loadData().then(function (data) {
|
|
18
18
|
svc.init(data);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* dbdocs SPA — tier 3 (ui): shared DOM primitives. */
|
|
2
|
+
|
|
3
|
+
export function el(tag, attrs, children) {
|
|
4
|
+
var node = document.createElement(tag);
|
|
5
|
+
if (attrs) Object.keys(attrs).forEach(function (k) {
|
|
6
|
+
if (k === "class") node.className = attrs[k];
|
|
7
|
+
else if (k === "html") node.innerHTML = attrs[k];
|
|
8
|
+
else if (k === "text") node.textContent = attrs[k];
|
|
9
|
+
else if (k.slice(0, 2) === "on" && typeof attrs[k] === "function") node.addEventListener(k.slice(2), attrs[k]);
|
|
10
|
+
else if (attrs[k] != null) node.setAttribute(k, attrs[k]);
|
|
11
|
+
});
|
|
12
|
+
(children || []).forEach(function (c) { if (c != null) node.appendChild(typeof c === "string" ? document.createTextNode(c) : c); });
|
|
13
|
+
return node;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function clear(node) { while (node.firstChild) node.removeChild(node.firstChild); }
|
|
17
|
+
|
|
18
|
+
export var KNOWN_ICONS = {
|
|
19
|
+
api: 1, catalog: 1, dag: 1, database: 1, info: 1, schema: 1, graph: 1, model: 1, source: 1,
|
|
20
|
+
seed: 1, snapshot: 1, test: 1, unit_test: 1, metric: 1, semantic_model: 1,
|
|
21
|
+
exposure: 1, saved_query: 1, operation: 1, fullscreen: 1, link: 1, health: 1,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function icon(name, size) {
|
|
25
|
+
return el("span", { class: "ic ic--" + name, style: "font-size:" + (size || 16) + "px" });
|
|
26
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/* dbdocs SPA — tier 3 (ui): command palette + toast notifications. */
|
|
2
|
+
|
|
3
|
+
import { el, icon } from "./dom.js";
|
|
4
|
+
import * as svc from "../service/service.js";
|
|
5
|
+
|
|
6
|
+
export var SEARCH_RESULT_CAP = 12;
|
|
7
|
+
|
|
8
|
+
var _sharedMini = null;
|
|
9
|
+
var _sharedDocs = null;
|
|
10
|
+
|
|
11
|
+
export function isMacPlatform() {
|
|
12
|
+
return /Mac|iPhone|iPad|iPod/.test(navigator.platform || "");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function ensureSearchIndex() {
|
|
16
|
+
if (_sharedMini) return;
|
|
17
|
+
_sharedDocs = svc.searchDocs();
|
|
18
|
+
_sharedMini = new MiniSearch({
|
|
19
|
+
fields: svc.SEARCH_FIELDS.map(function (f) { return f.key; }),
|
|
20
|
+
storeFields: ["label", "resource_type", "schema"],
|
|
21
|
+
searchOptions: {
|
|
22
|
+
prefix: true,
|
|
23
|
+
fuzzy: 0.2,
|
|
24
|
+
boost: { label: 6, name: 6, columns: 3, tags: 2, relation: 2, description: 1, columnDescriptions: 1, code: 0.4 },
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
_sharedMini.addAll(_sharedDocs);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function sharedDocs() { return _sharedDocs; }
|
|
31
|
+
export function sharedMini() { return _sharedMini; }
|
|
32
|
+
|
|
33
|
+
export function runSearchQuery(raw) {
|
|
34
|
+
var p = svc.parseSearchQuery(raw);
|
|
35
|
+
if (!p.text) {
|
|
36
|
+
if (!p.filterType) return [];
|
|
37
|
+
var picked = [];
|
|
38
|
+
for (var i = 0; i < _sharedDocs.length && picked.length < SEARCH_RESULT_CAP; i++) {
|
|
39
|
+
if (_sharedDocs[i].resource_type === p.filterType) picked.push(_sharedDocs[i]);
|
|
40
|
+
}
|
|
41
|
+
return picked;
|
|
42
|
+
}
|
|
43
|
+
var opts = {};
|
|
44
|
+
if (p.fields) opts.fields = p.fields;
|
|
45
|
+
if (p.filterType) opts.filter = function (h) { return h.resource_type === p.filterType; };
|
|
46
|
+
return _sharedMini.search(p.text, opts);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function showToast(message) {
|
|
50
|
+
var region = document.getElementById("toast-region");
|
|
51
|
+
if (!region) return;
|
|
52
|
+
var toast = el("div", { class: "toast" }, [message]);
|
|
53
|
+
region.appendChild(toast);
|
|
54
|
+
setTimeout(function () {
|
|
55
|
+
toast.classList.add("toast-out");
|
|
56
|
+
toast.addEventListener("animationend", function () {
|
|
57
|
+
if (toast.parentNode) toast.parentNode.removeChild(toast);
|
|
58
|
+
}, { once: true });
|
|
59
|
+
}, 2200);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function initCommandPalette() {
|
|
63
|
+
if (typeof MiniSearch === "undefined") return;
|
|
64
|
+
|
|
65
|
+
var isMac = isMacPlatform();
|
|
66
|
+
var focusBeforeOpen = null;
|
|
67
|
+
var paletteEl = null;
|
|
68
|
+
var activeIdx = -1;
|
|
69
|
+
|
|
70
|
+
function closePalette() {
|
|
71
|
+
if (!paletteEl) return;
|
|
72
|
+
var backdrop = document.getElementById("cmd-backdrop");
|
|
73
|
+
if (backdrop && backdrop.parentNode) backdrop.parentNode.removeChild(backdrop);
|
|
74
|
+
paletteEl = null;
|
|
75
|
+
activeIdx = -1;
|
|
76
|
+
if (focusBeforeOpen && typeof focusBeforeOpen.focus === "function") {
|
|
77
|
+
focusBeforeOpen.focus();
|
|
78
|
+
}
|
|
79
|
+
focusBeforeOpen = null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function paletteResultRows() {
|
|
83
|
+
if (!paletteEl) return [];
|
|
84
|
+
return Array.prototype.slice.call(paletteEl.querySelectorAll(".cmd-result[data-idx]"));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function movePaletteActive(delta) {
|
|
88
|
+
var rows = paletteResultRows();
|
|
89
|
+
if (!rows.length) return;
|
|
90
|
+
rows.forEach(function (r) {
|
|
91
|
+
r.classList.remove("cmd-active");
|
|
92
|
+
r.setAttribute("aria-selected", "false");
|
|
93
|
+
});
|
|
94
|
+
if (activeIdx < 0 && delta < 0) {
|
|
95
|
+
activeIdx = rows.length - 1;
|
|
96
|
+
} else {
|
|
97
|
+
activeIdx = (activeIdx + delta + rows.length) % rows.length;
|
|
98
|
+
}
|
|
99
|
+
var target = rows[activeIdx];
|
|
100
|
+
if (target) {
|
|
101
|
+
target.classList.add("cmd-active");
|
|
102
|
+
target.setAttribute("aria-selected", "true");
|
|
103
|
+
target.scrollIntoView({ block: "nearest" });
|
|
104
|
+
var input = document.getElementById("cmd-input");
|
|
105
|
+
if (input) input.setAttribute("aria-activedescendant", "cmd-opt-" + activeIdx);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function activateRow(row) {
|
|
110
|
+
if (!row) return;
|
|
111
|
+
var href = row.getAttribute("data-href");
|
|
112
|
+
if (href) location.hash = href;
|
|
113
|
+
else {
|
|
114
|
+
var action = row.getAttribute("data-action");
|
|
115
|
+
if (action === "toggle-theme") {
|
|
116
|
+
var t = document.getElementById("theme-toggle");
|
|
117
|
+
if (t) t.click();
|
|
118
|
+
} else if (action === "overview") {
|
|
119
|
+
location.hash = "#/overview";
|
|
120
|
+
} else if (action === "dag") {
|
|
121
|
+
location.hash = "#/dag";
|
|
122
|
+
} else if (action === "health") {
|
|
123
|
+
location.hash = "#/health";
|
|
124
|
+
} else if (action === "about") {
|
|
125
|
+
location.hash = "#/about";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
closePalette();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function wireResultRow(row, i) {
|
|
132
|
+
row.addEventListener("mousedown", function (e) { e.preventDefault(); activateRow(row); });
|
|
133
|
+
row.addEventListener("click", function (e) { e.preventDefault(); activateRow(row); });
|
|
134
|
+
row.addEventListener("mouseover", function () {
|
|
135
|
+
activeIdx = i;
|
|
136
|
+
paletteResultRows().forEach(function (r) {
|
|
137
|
+
r.classList.remove("cmd-active");
|
|
138
|
+
r.setAttribute("aria-selected", "false");
|
|
139
|
+
});
|
|
140
|
+
row.classList.add("cmd-active");
|
|
141
|
+
row.setAttribute("aria-selected", "true");
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function renderPaletteResults(query, resultsEl, input) {
|
|
146
|
+
while (resultsEl.firstChild) resultsEl.removeChild(resultsEl.firstChild);
|
|
147
|
+
activeIdx = -1;
|
|
148
|
+
input.removeAttribute("aria-activedescendant");
|
|
149
|
+
|
|
150
|
+
var q = query.trim();
|
|
151
|
+
|
|
152
|
+
if (!q) {
|
|
153
|
+
var actions = [
|
|
154
|
+
{ label: "Catalog overview", icon: "catalog", action: "overview" },
|
|
155
|
+
{ label: "Lineage / DAG", icon: "dag", action: "dag" },
|
|
156
|
+
{ label: "Toggle theme", icon: "info", action: "toggle-theme" },
|
|
157
|
+
];
|
|
158
|
+
if (svc.healthEnabled()) {
|
|
159
|
+
actions.splice(2, 0, { label: "Health Check", icon: "health", action: "health" });
|
|
160
|
+
}
|
|
161
|
+
if (svc.meta().show_about !== false) {
|
|
162
|
+
actions.splice(actions.length, 0, { label: "About / JSON API", icon: "api", action: "about" });
|
|
163
|
+
}
|
|
164
|
+
resultsEl.appendChild(el("div", { class: "cmd-section-label" }, ["Quick actions"]));
|
|
165
|
+
actions.forEach(function (a, i) {
|
|
166
|
+
var row = el("div", {
|
|
167
|
+
class: "cmd-result", tabindex: "-1",
|
|
168
|
+
role: "option", "aria-selected": "false",
|
|
169
|
+
id: "cmd-opt-" + i, "data-idx": String(i), "data-action": a.action,
|
|
170
|
+
}, [icon(a.icon, 15), el("span", { class: "cmd-result-label" }, [a.label])]);
|
|
171
|
+
wireResultRow(row, i);
|
|
172
|
+
resultsEl.appendChild(row);
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
ensureSearchIndex();
|
|
178
|
+
var hits = runSearchQuery(q).slice(0, SEARCH_RESULT_CAP);
|
|
179
|
+
|
|
180
|
+
if (!hits.length) {
|
|
181
|
+
resultsEl.appendChild(el("div", { class: "cmd-empty" }, ['No results for "' + q + '".']));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
resultsEl.appendChild(el("div", { class: "cmd-section-label" }, ["Nodes"]));
|
|
186
|
+
hits.forEach(function (h, i) {
|
|
187
|
+
var href = "#/node/" + encodeURIComponent(h.id);
|
|
188
|
+
var row = el("div", {
|
|
189
|
+
class: "cmd-result", tabindex: "-1",
|
|
190
|
+
role: "option", "aria-selected": "false",
|
|
191
|
+
id: "cmd-opt-" + i, "data-idx": String(i), "data-href": href,
|
|
192
|
+
}, [
|
|
193
|
+
el("span", { class: "dot " + h.resource_type }),
|
|
194
|
+
el("span", { class: "cmd-result-label" }, [h.label]),
|
|
195
|
+
el("span", { class: "cmd-result-meta" }, [h.resource_type + (h.schema ? " · " + h.schema : "")]),
|
|
196
|
+
]);
|
|
197
|
+
wireResultRow(row, i);
|
|
198
|
+
resultsEl.appendChild(row);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function openPalette() {
|
|
203
|
+
if (document.getElementById("cmd-backdrop")) return;
|
|
204
|
+
focusBeforeOpen = document.activeElement;
|
|
205
|
+
|
|
206
|
+
var input = el("input", {
|
|
207
|
+
id: "cmd-input", type: "search", autocomplete: "off",
|
|
208
|
+
placeholder: "Search nodes or type a command…",
|
|
209
|
+
"aria-label": "Command palette",
|
|
210
|
+
"aria-autocomplete": "list",
|
|
211
|
+
"aria-controls": "cmd-results",
|
|
212
|
+
});
|
|
213
|
+
var resultsEl = el("div", { id: "cmd-results", role: "listbox", "aria-label": "Results" });
|
|
214
|
+
|
|
215
|
+
var palette = el("div", {
|
|
216
|
+
id: "cmd-palette",
|
|
217
|
+
role: "dialog", "aria-modal": "true", "aria-label": "Command palette",
|
|
218
|
+
}, [
|
|
219
|
+
el("div", { class: "cmd-input-wrap" }, [
|
|
220
|
+
icon("catalog", 16),
|
|
221
|
+
input,
|
|
222
|
+
el("kbd", { class: "cmd-kbd-close" }, ["Esc"]),
|
|
223
|
+
]),
|
|
224
|
+
resultsEl,
|
|
225
|
+
]);
|
|
226
|
+
|
|
227
|
+
var backdrop = el("div", { id: "cmd-backdrop" }, [palette]);
|
|
228
|
+
document.body.appendChild(backdrop);
|
|
229
|
+
paletteEl = palette;
|
|
230
|
+
|
|
231
|
+
renderPaletteResults("", resultsEl, input);
|
|
232
|
+
input.focus();
|
|
233
|
+
|
|
234
|
+
input.addEventListener("input", function () {
|
|
235
|
+
renderPaletteResults(input.value, resultsEl, input);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
input.addEventListener("keydown", function (e) {
|
|
239
|
+
if (e.key === "Escape") { e.preventDefault(); closePalette(); }
|
|
240
|
+
else if (e.key === "ArrowDown") { e.preventDefault(); movePaletteActive(1); }
|
|
241
|
+
else if (e.key === "ArrowUp") { e.preventDefault(); movePaletteActive(-1); }
|
|
242
|
+
else if (e.key === "Enter") {
|
|
243
|
+
e.preventDefault();
|
|
244
|
+
var rows = paletteResultRows();
|
|
245
|
+
activateRow(rows[activeIdx >= 0 ? activeIdx : 0]);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
backdrop.addEventListener("mousedown", function (e) {
|
|
250
|
+
if (e.target === backdrop) closePalette();
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
document.addEventListener("keydown", function (e) {
|
|
255
|
+
var modKey = isMac ? e.metaKey : e.ctrlKey;
|
|
256
|
+
if (modKey && e.key === "k") {
|
|
257
|
+
e.preventDefault();
|
|
258
|
+
if (document.getElementById("cmd-backdrop")) closePalette();
|
|
259
|
+
else openPalette();
|
|
260
|
+
}
|
|
261
|
+
if (e.key === "Escape" && document.getElementById("cmd-backdrop")) {
|
|
262
|
+
closePalette();
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|