@repobit/dex-system-design 0.24.7 → 0.24.9
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/CHANGELOG.md +14 -0
- package/package.json +4 -4
- package/src/components/ai-guardian-demo.stories.js +245 -21
- package/src/components/anchor/anchor-nav.css.js +16 -18
- package/src/components/anchor/anchor-nav.js +86 -23
- package/src/components/anchor/anchor.stories.js +87 -4
- package/src/components/cta-banner/cta-banner.stories.js +122 -0
- package/src/components/install-options/install-options.css.js +6 -0
- package/src/components/install-options/install-options.js +17 -2
- package/src/components/install-options/install-options.stories.js +219 -0
- package/src/components/review/review-grid.css.js +3 -2
- package/src/components/review/review.css.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [0.24.9](https://github.com/bitdefender/dex-core/compare/@repobit/dex-system-design@0.24.8...@repobit/dex-system-design@0.24.9) (2026-09-08)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **DEX-1014:** new AI Bitdefender components and anchor nav changes
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## [0.24.8](https://github.com/bitdefender/dex-core/compare/@repobit/dex-system-design@0.24.7...@repobit/dex-system-design@0.24.8) (2026-09-08)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **DEX-1014:** review component adjustments and AI Bitdefender new component
|
|
18
|
+
|
|
19
|
+
|
|
6
20
|
## [0.24.7](https://github.com/bitdefender/dex-core/compare/@repobit/dex-system-design@0.24.6...@repobit/dex-system-design@0.24.7) (2026-09-07)
|
|
7
21
|
|
|
8
22
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@repobit/dex-system-design",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.9",
|
|
4
4
|
"description": "Design system based on Web Components.",
|
|
5
5
|
"author": "Iordache Matei Cezar <miordache@bitdefender.com>",
|
|
6
6
|
"homepage": "https://github.com/bitdefender/dex-core#readme",
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
"url": "https://github.com/bitdefender/dex-core/issues"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@repobit/dex-store": "1.6.
|
|
78
|
-
"@repobit/dex-store-elements": "2.0.
|
|
77
|
+
"@repobit/dex-store": "1.6.25",
|
|
78
|
+
"@repobit/dex-store-elements": "2.0.30",
|
|
79
79
|
"lit": "^3.3.2"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
@@ -92,5 +92,5 @@
|
|
|
92
92
|
"volta": {
|
|
93
93
|
"node": "24.14.0"
|
|
94
94
|
},
|
|
95
|
-
"gitHead": "
|
|
95
|
+
"gitHead": "1f08b0389af256faebe7db77b276d350c9be31ae"
|
|
96
96
|
}
|
|
@@ -93,6 +93,7 @@ function aiGuardianBody() {
|
|
|
93
93
|
aria-label="Page sections"
|
|
94
94
|
cta-label="Download BETA Today"
|
|
95
95
|
cta-href="#download"
|
|
96
|
+
sticky-top="65px"
|
|
96
97
|
.items=${[
|
|
97
98
|
{ title: "Overview" },
|
|
98
99
|
{ title: "Compatibility" },
|
|
@@ -430,14 +431,6 @@ function aiGuardianBody() {
|
|
|
430
431
|
}
|
|
431
432
|
|
|
432
433
|
|
|
433
|
-
|
|
434
|
-
// ---------------------------------------------------------------------------
|
|
435
|
-
// PATCH pentru ai-guardian-demo.stories.js
|
|
436
|
-
// 1. Adauga importul: import "./install-options/install-options.js";
|
|
437
|
-
// (prompt-panel.js NU mai e nevoie -- totul e integrat in bd-install-options)
|
|
438
|
-
// 2. Inlocuieste functia skillScannerBody() cu varianta de mai jos.
|
|
439
|
-
// ---------------------------------------------------------------------------
|
|
440
|
-
|
|
441
434
|
function skillScannerBody() {
|
|
442
435
|
return html`
|
|
443
436
|
<bd-header-code
|
|
@@ -506,6 +499,7 @@ function skillScannerBody() {
|
|
|
506
499
|
<div style="max-width: 1200px; margin: 0 auto;">
|
|
507
500
|
<bd-anchor-nav
|
|
508
501
|
aria-label="Skill Scanner sections"
|
|
502
|
+
sticky-top="65px"
|
|
509
503
|
.items=${[
|
|
510
504
|
{ title: "Install" },
|
|
511
505
|
{ title: "Overview" },
|
|
@@ -668,22 +662,242 @@ function skillScannerBody() {
|
|
|
668
662
|
</bd-bento-item>
|
|
669
663
|
</bd-bento-grid>
|
|
670
664
|
</div>
|
|
665
|
+
|
|
666
|
+
<bd-process-steps
|
|
667
|
+
title="How a scan works"
|
|
668
|
+
description='You ask in plain language, "scan the foo skill for security risks", and the skill does the rest. There is no separate command-line tool to learn.'
|
|
669
|
+
.steps=${[
|
|
670
|
+
{
|
|
671
|
+
title : "1. Resolve",
|
|
672
|
+
description: "The scanner resolves the target (a directory, an installed skill name, a zip, or a URL) into a skill root."
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
title : "2. Pack & hash",
|
|
676
|
+
description: "It packs the skill into a normalized archive and computes a skill-level SHA-256, then checks for a cached report before repeating any work."
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
title : "3. Collect & enrich",
|
|
680
|
+
description: "It walks, hashes and recurses into every file, then runs all 18 indicator extractors and 71 behavioural patterns over the text content."
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
title : "4. Embed",
|
|
684
|
+
description: "It computes semantic embeddings on your machine, unless you disable them."
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
title : "5. Submit metadata",
|
|
688
|
+
description: "Hashes, indicators with short matched excerpts, counts, metadata and embeddings go to the backend. In scan mode it sends no full file contents."
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
title : "6. Report",
|
|
692
|
+
description: "The backend returns a risk level, summary and findings, rendered as Markdown in your session."
|
|
693
|
+
}
|
|
694
|
+
]}
|
|
695
|
+
></bd-process-steps>
|
|
696
|
+
|
|
697
|
+
<div style="padding: 48px 24px 24px;">
|
|
698
|
+
<bd-compatibility-table
|
|
699
|
+
variant="risk"
|
|
700
|
+
title="What you can point it at"
|
|
701
|
+
description="The scanner accepts four target types: a directory, an installed skill name, a zip archive, or a URL."
|
|
702
|
+
.columns=${["Target", "Behaviour"]}
|
|
703
|
+
.rows=${[
|
|
704
|
+
{ category: "Directory path", coverage: "Scanned directly as the skill root." },
|
|
705
|
+
{ category: "Skill name", coverage: "Resolved via the harness's known skill directories, project- and user-level." },
|
|
706
|
+
{ category: ".zip archive", coverage: "Extracted to a temp directory, then scanned." },
|
|
707
|
+
{ category: "URL", coverage: "Sent to the backend for scanning (scan mode only)." }
|
|
708
|
+
]}
|
|
709
|
+
></bd-compatibility-table>
|
|
710
|
+
</div>
|
|
711
|
+
</section>
|
|
712
|
+
|
|
713
|
+
<section id="anchor-5-section">
|
|
714
|
+
<bd-install-options
|
|
715
|
+
eyebrow="Trust & transparency"
|
|
716
|
+
title="What leaves your machine"
|
|
717
|
+
description="Two modes, both agent-driven. Scan is the default: only hashes, extracted indicators and metadata are transmitted, and full files are never uploaded. Submit is opt-in: the complete skill archive is uploaded for deeper analysis, and only when you explicitly ask for it."
|
|
718
|
+
>
|
|
719
|
+
<bd-compatibility-table
|
|
720
|
+
variant="risk"
|
|
721
|
+
.columns=${["Mode", "Transmitted"]}
|
|
722
|
+
.rows=${[
|
|
723
|
+
{
|
|
724
|
+
category: "Scan (default)",
|
|
725
|
+
coverage: "File hashes (SHA-256/MD5), extracted indicators and pattern matches — including short excerpts of matched content (regex fragments ≤100 chars, decoded base64 snippets ≤500 chars) — file metadata, finding counts, SKILL.md frontmatter, dependency metadata, and local embeddings. Full file contents are not transmitted."
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
category: "Submit (opt-in)",
|
|
729
|
+
coverage: "The complete skill archive, uploaded for deep analysis. The agent will not do this unless you explicitly ask."
|
|
730
|
+
}
|
|
731
|
+
]}
|
|
732
|
+
></bd-compatibility-table>
|
|
733
|
+
|
|
734
|
+
<div style="background: var(--color-neutral-0); border: 1px solid var(--color-neutral-100); border-radius: var(--radius-xl); padding: 24px; box-sizing: border-box; margin-top: 24px;">
|
|
735
|
+
<bd-h as="h3" style="margin: 0 0 16px;">The contract the agent is held to</bd-h>
|
|
736
|
+
<ul style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 16px;">
|
|
737
|
+
<li style="display: flex; align-items: flex-start; gap: 12px;">
|
|
738
|
+
<span aria-hidden="true" style="flex-shrink: 0; width: 20px; height: 20px; border-radius: 999px; background: var(--color-green-600, #16a34a); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; margin-top: 2px;">✓</span>
|
|
739
|
+
<bd-p kind="regular" style="margin:0;">Scan mode is the default: a casual request never uploads your files.</bd-p>
|
|
740
|
+
</li>
|
|
741
|
+
<li style="display: flex; align-items: flex-start; gap: 12px;">
|
|
742
|
+
<span aria-hidden="true" style="flex-shrink: 0; width: 20px; height: 20px; border-radius: 999px; background: var(--color-green-600, #16a34a); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; margin-top: 2px;">✓</span>
|
|
743
|
+
<bd-p kind="regular" style="margin:0;">Submit is opt-in only: the agent won't upload the full archive unless you explicitly ask.</bd-p>
|
|
744
|
+
</li>
|
|
745
|
+
<li style="display: flex; align-items: flex-start; gap: 12px;">
|
|
746
|
+
<span aria-hidden="true" style="flex-shrink: 0; width: 20px; height: 20px; border-radius: 999px; background: var(--color-green-600, #16a34a); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; margin-top: 2px;">✓</span>
|
|
747
|
+
<bd-p kind="regular" style="margin:0;">The agent relays, it doesn't overrule: it presents the backend's report and never substitutes its own verdict.</bd-p>
|
|
748
|
+
</li>
|
|
749
|
+
<li style="display: flex; align-items: flex-start; gap: 12px;">
|
|
750
|
+
<span aria-hidden="true" style="flex-shrink: 0; width: 20px; height: 20px; border-radius: 999px; background: var(--color-green-600, #16a34a); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; margin-top: 2px;">✓</span>
|
|
751
|
+
<bd-p kind="regular" style="margin:0;">Errors are surfaced as they are: on failure you get the specific reason.</bd-p>
|
|
752
|
+
</li>
|
|
753
|
+
<li style="display: flex; align-items: flex-start; gap: 12px;">
|
|
754
|
+
<span aria-hidden="true" style="flex-shrink: 0; width: 20px; height: 20px; border-radius: 999px; background: var(--color-green-600, #16a34a); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; margin-top: 2px;">✓</span>
|
|
755
|
+
<bd-p kind="regular" style="margin:0;">Self-updates are Ed25519-signature-verified against a key pinned in the client, staged with path-traversal-safe extraction, and rolled back automatically on failure.</bd-p>
|
|
756
|
+
</li>
|
|
757
|
+
</ul>
|
|
758
|
+
</div>
|
|
759
|
+
|
|
760
|
+
<div style="background: var(--color-yellow-50, #fffbeb); border: 1px solid var(--color-yellow-300, #fcd34d); border-radius: var(--radius-md); padding: 16px 20px; color: var(--color-yellow-800, #92400e); margin-top: 24px;">
|
|
761
|
+
<bd-p kind="small" style="margin:0;">A scan that returns no findings means no known threat patterns were detected — not that the skill is safe.</bd-p>
|
|
762
|
+
</div>
|
|
763
|
+
</bd-install-options>
|
|
671
764
|
</section>
|
|
672
765
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
766
|
+
<section id="anchor-6-section" class="faq">
|
|
767
|
+
<bd-trust-panel
|
|
768
|
+
eyebrow="Why trust it"
|
|
769
|
+
title="Built by Bitdefender"
|
|
770
|
+
description="Agent Skill Scanner is made by Bitdefender, the company that has protected people and organisations since 2001. The figures and recognitions below describe Bitdefender the company — not the scanner itself."
|
|
771
|
+
link-label="Why millions trust Bitdefender"
|
|
772
|
+
link-href="#"
|
|
773
|
+
.stats=${[
|
|
774
|
+
{ value: "50B+", label: "threats blocked every year" },
|
|
775
|
+
{ value: "580+", label: "technology patents" },
|
|
776
|
+
{ value: "170+", label: "countries protected" },
|
|
777
|
+
{ value: "15+", label: "years of AI-driven security" },
|
|
778
|
+
{ value: "50%+", label: "of employees work in R&D" },
|
|
779
|
+
{ value: "30+", label: "law-enforcement partnerships" }
|
|
780
|
+
]}
|
|
781
|
+
.cards=${[
|
|
782
|
+
{
|
|
783
|
+
eyebrow : "Brand partnership",
|
|
784
|
+
title : "Trusted by Ferrari as cybersecurity partner",
|
|
785
|
+
description: "A partnership born from a passion for high performance and technological innovation — Bitdefender supports Ferrari with Advanced Threat Intelligence to improve detection and response to cyber threats.",
|
|
786
|
+
linkLabel : "Learn more",
|
|
787
|
+
linkHref : "#"
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
eyebrow : "Brand recognition",
|
|
791
|
+
title : "Award-winning security software you can trust. Always.",
|
|
792
|
+
description: "Consistently top-ranked by the world's leading independent testing labs and tech publications.",
|
|
793
|
+
linkLabel : "Learn more",
|
|
794
|
+
linkHref : "#"
|
|
795
|
+
}
|
|
796
|
+
]}
|
|
797
|
+
>
|
|
798
|
+
<span slot="card-1-logos" style="font-weight:700; color:#006DFF; font-size:18px;">Bitdefender</span>
|
|
799
|
+
<span slot="card-1-logos" style="font-size:12px; color:#374151; font-weight:600;">FERRARI TEAM PARTNER</span>
|
|
800
|
+
|
|
801
|
+
<bd-pc-mag-icon slot="card-2-logos" height="32"></bd-pc-mag-icon>
|
|
802
|
+
<bd-av-comparatives-icon slot="card-2-logos" height="32"></bd-av-comparatives-icon>
|
|
803
|
+
<bd-top-product-icon slot="card-2-logos" height="32"></bd-top-product-icon>
|
|
804
|
+
<bd-best-brands-icon slot="card-2-logos" height="32"></bd-best-brands-icon>
|
|
805
|
+
<bd-laurel-icon slot="card-2-logos" height="32"></bd-laurel-icon>
|
|
806
|
+
</bd-trust-panel>
|
|
807
|
+
|
|
808
|
+
<div style="padding: 24px 24px 48px;">
|
|
809
|
+
<bd-accordion-bg title="Frequently asked questions">
|
|
810
|
+
<bd-accordion-bg-item title="What is Agent Skill Scanner?">
|
|
811
|
+
<bd-p>Agent Skill Scanner is a Bitdefender agent skill that audits other agent skills before you install or trust them. It runs 71 behavioural detectors across 16 threat categories, extracts 18 indicator types from every file, and returns an LLM-generated security report with a risk level, summary, and per-file findings.</bd-p>
|
|
812
|
+
</bd-accordion-bg-item>
|
|
813
|
+
|
|
814
|
+
<bd-accordion-bg-item title="How do I install Agent Skill Scanner?">
|
|
815
|
+
<bd-p>Three ways: run "npx skills add bitdefender/agent-skill-scanner" via skills.sh; clone the GitHub repository and copy the agent-skill-scanner folder into your skills directory; or download the zip archive and unzip it there. It requires Python 3.12 or 3.13 and installs its own dependencies on first use.</bd-p>
|
|
816
|
+
</bd-accordion-bg-item>
|
|
817
|
+
|
|
818
|
+
<bd-accordion-bg-item title="How do I scan a skill?">
|
|
819
|
+
<bd-p>Ask your agent in plain language, for example "scan the foo skill for security risks". There is no separate command to learn: the harness invokes the skill, resolves the target (a directory, an installed skill name, a zip, or a URL), and presents the Markdown report.</bd-p>
|
|
820
|
+
</bd-accordion-bg-item>
|
|
821
|
+
|
|
822
|
+
<bd-accordion-bg-item title="Does it upload my files?">
|
|
823
|
+
<bd-p>Not by default. In scan mode the scanner transmits only file hashes, extracted indicators with short matched excerpts, metadata and local embeddings; full file contents are never uploaded. The opt-in submit mode uploads the complete skill archive for deeper analysis, and the agent will only use it when you explicitly ask.</bd-p>
|
|
824
|
+
</bd-accordion-bg-item>
|
|
825
|
+
|
|
826
|
+
<bd-accordion-bg-item title="Which agents and harnesses does it work with?">
|
|
827
|
+
<bd-p>It is an agent skill for use inside a harness such as Claude Code, Codex, OpenClaw, or opencode. It is not a standalone CLI tool. Any harness that reads a skills directory and lets the agent execute the skill's instructions can run it.</bd-p>
|
|
828
|
+
</bd-accordion-bg-item>
|
|
829
|
+
|
|
830
|
+
<bd-accordion-bg-item title="What does a CLEAN result mean?">
|
|
831
|
+
<bd-p>CLEAN means no known threat patterns were detected, not that the skill is safe. Detection covers 71 behavioural patterns and 18 indicator types, but no scanner proves the absence of malicious intent. Treat the report as strong signal for a trust decision, not a guarantee.</bd-p>
|
|
832
|
+
</bd-accordion-bg-item>
|
|
833
|
+
|
|
834
|
+
<bd-accordion-bg-item title="How much does it cost?">
|
|
835
|
+
<bd-p>Agent Skill Scanner is available at no charge from skills.sh and GitHub. Use is subject to Bitdefender's End User License Agreement and Privacy Policy.</bd-p>
|
|
836
|
+
</bd-accordion-bg-item>
|
|
837
|
+
</bd-accordion-bg>
|
|
838
|
+
</div>
|
|
839
|
+
</section>
|
|
678
840
|
</div>
|
|
679
841
|
|
|
842
|
+
<bd-cta-banner
|
|
843
|
+
title="Scan before you trust"
|
|
844
|
+
description="A skill is code you hand to your agent. One command stands between you and running it blind."
|
|
845
|
+
>
|
|
846
|
+
<bd-button-link
|
|
847
|
+
slot="primary-button"
|
|
848
|
+
label="Install with npx"
|
|
849
|
+
kind="white"
|
|
850
|
+
size="md"
|
|
851
|
+
strong
|
|
852
|
+
>Install with npx</bd-button-link>
|
|
853
|
+
|
|
854
|
+
<bd-button-link
|
|
855
|
+
slot="secondary-button"
|
|
856
|
+
label="View on GitHub"
|
|
857
|
+
kind="outline-white"
|
|
858
|
+
size="md"
|
|
859
|
+
strong
|
|
860
|
+
>View on GitHub</bd-button-link>
|
|
861
|
+
</bd-cta-banner>
|
|
862
|
+
|
|
863
|
+
<section id="anchor-11-section">
|
|
864
|
+
<bd-agent-endpoints
|
|
865
|
+
eyebrow="For AI agents and assistants"
|
|
866
|
+
title="Machine-readable endpoints"
|
|
867
|
+
description="This site is built to be read by AI assistants, browsers, and agents. Structured, curated context is available at:"
|
|
868
|
+
.endpoints=${[
|
|
869
|
+
{ label: "/llms.txt", href: "/llms.txt" },
|
|
870
|
+
{ label: "/llms-full.txt", href: "/llms-full.txt" },
|
|
871
|
+
{ label: "/skill-scanner/index.md", href: "/skill-scanner/index.md" },
|
|
872
|
+
{ label: "/sitemap.xml", href: "/sitemap.xml" },
|
|
873
|
+
{ label: "/robots.txt", href: "/robots.txt" }
|
|
874
|
+
]}
|
|
875
|
+
>
|
|
876
|
+
<div slot="rows" style="border-top: 1px solid var(--color-neutral-100); margin-top: 24px; padding-top: 24px;">
|
|
877
|
+
<bd-p kind="regular" style="color: var(--color-neutral-700); margin: 0;">
|
|
878
|
+
By using Bitdefender Agent Skill Scanner, you confirm that you are over 16 years old and you have read and agreed to Bitdefender's
|
|
879
|
+
<bd-link href="#" kind="primary">End User License Agreement</bd-link>
|
|
880
|
+
and
|
|
881
|
+
<bd-link href="#" kind="primary">Privacy Policy</bd-link>.
|
|
882
|
+
</bd-p>
|
|
883
|
+
</div>
|
|
884
|
+
|
|
885
|
+
<div slot="rows" style="border-top: 1px solid var(--color-neutral-100); margin-top: 24px; padding-top: 24px;">
|
|
886
|
+
<bd-p kind="regular" style="color: var(--color-neutral-700); margin: 0;">
|
|
887
|
+
No security product guarantees complete protection. A scan that returns no findings means no known threat patterns were detected — not that the skill is safe. Effectiveness depends on configuration, runtime environment and the skills analysed. Features and availability may vary.
|
|
888
|
+
</bd-p>
|
|
889
|
+
</div>
|
|
890
|
+
</bd-agent-endpoints>
|
|
891
|
+
</section>
|
|
892
|
+
|
|
680
893
|
<!--
|
|
681
|
-
TODO:
|
|
682
|
-
|
|
683
|
-
la fel ca înainte — se adaugă când vine conținutul.
|
|
894
|
+
TODO: daca mai exista sectiuni sub asta pentru Skill Scanner,
|
|
895
|
+
se adauga cand vine continutul.
|
|
684
896
|
-->
|
|
685
897
|
`;
|
|
686
898
|
}
|
|
899
|
+
|
|
900
|
+
|
|
687
901
|
function sharedFooter() {
|
|
688
902
|
return html`
|
|
689
903
|
<bd-footer>
|
|
@@ -822,18 +1036,28 @@ switches, in place, between two full "pages" without any real navigation —
|
|
|
822
1036
|
clicking a tab intercepts the anchor click and swaps local state instead of
|
|
823
1037
|
following the href (which has no route in Storybook and would 404).
|
|
824
1038
|
|
|
1039
|
+
Both anchor-nav bars use \`sticky-top="65px"\` so they pin themselves *below*
|
|
1040
|
+
the sticky page-switcher instead of overlapping it once scrolled. That value
|
|
1041
|
+
is an estimate — measure bd-page-switcher's real rendered height in DevTools
|
|
1042
|
+
and adjust if the two bars still overlap or leave a gap.
|
|
1043
|
+
|
|
825
1044
|
**AI Guardian** page: \`bd-header-code\`, \`bd-quick-answer\`, sticky \`bd-anchor-nav\`
|
|
826
1045
|
(9 items) wrapping \`bd-overview\`, \`bd-compatibility-table\` (default variant), the
|
|
827
1046
|
"Protections" section (\`bd-bento-grid\` + \`bd-comparison-panel\`), \`bd-process-steps\`,
|
|
828
|
-
\`bd-compatibility-table\` (\`variant="
|
|
829
|
-
revealing \`bd-compatibility-table\` (\`variant="risk"\` / \`variant="light"\`), an FAQ
|
|
1047
|
+
\`bd-compatibility-table\` (\`variant="risk"\` / \`variant="light"\`), an FAQ
|
|
830
1048
|
section (\`bd-accordion-bg\`, 9 questions), \`bd-cta-banner\`, \`bd-agent-endpoints\`,
|
|
831
1049
|
and \`bd-trust-panel\`.
|
|
832
1050
|
|
|
833
1051
|
**Skill Scanner** page: same top nav (tab now active), a dedicated
|
|
834
|
-
\`bd-header-code\`
|
|
835
|
-
|
|
836
|
-
|
|
1052
|
+
\`bd-header-code\`, \`bd-quick-answer\`, and a sticky \`bd-anchor-nav\` (6 items:
|
|
1053
|
+
Install, Overview, What it detects, How it works, Privacy, FAQ) wrapping
|
|
1054
|
+
\`bd-install-options\` (cards + "Then just ask" prompt panel), \`bd-overview\`,
|
|
1055
|
+
a \`bd-compatibility-table\` inside a chevron accordion, a \`bd-bento-grid\` +
|
|
1056
|
+
\`bd-process-steps\` + another \`bd-compatibility-table\` for "How it works",
|
|
1057
|
+
another \`bd-install-options\` (generic-slot variant: table + checklist + note)
|
|
1058
|
+
for "Privacy", a \`bd-trust-panel\` + \`bd-accordion-bg\` FAQ (7 questions) under
|
|
1059
|
+
"FAQ", a \`bd-cta-banner\`, and \`bd-agent-endpoints\` with legal copy in its
|
|
1060
|
+
\`rows\` slot.
|
|
837
1061
|
|
|
838
1062
|
Both pages share the same \`bd-footer\` at the bottom.
|
|
839
1063
|
`
|
|
@@ -11,14 +11,17 @@ export const anchorNavStyles = css`
|
|
|
11
11
|
|
|
12
12
|
:host(.is-sticky) {
|
|
13
13
|
position: fixed;
|
|
14
|
-
top: 0;
|
|
14
|
+
top: var(--sticky-top, 0);
|
|
15
15
|
left: var(--sticky-left, 0);
|
|
16
16
|
width: var(--sticky-width, 100%);
|
|
17
17
|
box-shadow: var(--shadow-sm);
|
|
18
18
|
z-index: 1000;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
:host(.is-sticky) nav {
|
|
22
|
+
background: var(--color-neutral-0);
|
|
23
|
+
}
|
|
24
|
+
|
|
22
25
|
nav {
|
|
23
26
|
display: block;
|
|
24
27
|
margin: var(--spacing-0);
|
|
@@ -29,11 +32,6 @@ export const anchorNavStyles = css`
|
|
|
29
32
|
border-bottom: var(--border-width-1) solid var(--color-neutral-100);
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
/* Când e sticky, păstrăm aceleași border-uri */
|
|
33
|
-
:host(.is-sticky) nav {
|
|
34
|
-
background: var(--color-neutral-0);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
35
|
.bd-anchor-nav__inner {
|
|
38
36
|
display: flex;
|
|
39
37
|
flex-wrap: wrap;
|
|
@@ -48,7 +46,6 @@ export const anchorNavStyles = css`
|
|
|
48
46
|
box-sizing: border-box;
|
|
49
47
|
}
|
|
50
48
|
|
|
51
|
-
/* Restul codului tău CSS rămâne identic de aici în jos */
|
|
52
49
|
|
|
53
50
|
.anchor-link:focus-visible,
|
|
54
51
|
.bd-anchor-nav__cta:focus-visible,
|
|
@@ -118,19 +115,19 @@ export const anchorNavStyles = css`
|
|
|
118
115
|
text-decoration: none !important;
|
|
119
116
|
}
|
|
120
117
|
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
|
|
119
|
+
.anchor-indicator {
|
|
123
120
|
position: absolute;
|
|
124
|
-
left:
|
|
121
|
+
left: 0;
|
|
125
122
|
bottom: calc(-1 * var(--spacing-14));
|
|
126
|
-
width: 0
|
|
123
|
+
width: 0;
|
|
127
124
|
height: var(--border-width-3);
|
|
128
125
|
background-color: var(--color-blue-500);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
126
|
+
transform: translateX(0);
|
|
127
|
+
transition:
|
|
128
|
+
transform var(--transition-duration-slow) var(--transition-easing-ease-out),
|
|
129
|
+
width var(--transition-duration-slow) var(--transition-easing-ease-out);
|
|
130
|
+
pointer-events: none;
|
|
134
131
|
}
|
|
135
132
|
|
|
136
133
|
.bd-anchor-nav__dropdown {
|
|
@@ -299,10 +296,11 @@ export const anchorNavStyles = css`
|
|
|
299
296
|
}
|
|
300
297
|
|
|
301
298
|
@media (prefers-reduced-motion: reduce) {
|
|
302
|
-
.anchor-
|
|
299
|
+
.anchor-indicator,
|
|
303
300
|
.bd-anchor-nav__dropdown-chevron,
|
|
304
301
|
.bd-cta,
|
|
305
302
|
.bd-anchor-nav__dropdown-label--swap {
|
|
303
|
+
transition: none !important;
|
|
306
304
|
animation: none !important;
|
|
307
305
|
}
|
|
308
306
|
}
|
|
@@ -33,7 +33,12 @@ class BdAnchorNav extends LitElement {
|
|
|
33
33
|
ctaLabel : { type: String, attribute: "cta-label" },
|
|
34
34
|
ctaHref : { type: String, attribute: "cta-href" },
|
|
35
35
|
items : { type: Array },
|
|
36
|
-
maxWidth : { type: String, attribute: "max-width" }
|
|
36
|
+
maxWidth : { type: String, attribute: "max-width" },
|
|
37
|
+
// How far from the viewport top this nav sticks once pinned — set this
|
|
38
|
+
// to the rendered height of whatever sticky element sits above it (e.g.
|
|
39
|
+
// a page-switcher), so the two don't stack on top of each other at
|
|
40
|
+
// top:0. Accepts any CSS length, e.g. "64px", "4rem".
|
|
41
|
+
stickyTop : { type: String, attribute: "sticky-top" }
|
|
37
42
|
};
|
|
38
43
|
|
|
39
44
|
static styles = [tokens, anchorNavStyles];
|
|
@@ -48,6 +53,7 @@ class BdAnchorNav extends LitElement {
|
|
|
48
53
|
this.ctaHref = "";
|
|
49
54
|
this.items = null;
|
|
50
55
|
this.maxWidth = "";
|
|
56
|
+
this.stickyTop = "0px";
|
|
51
57
|
this._panelId = `bd-anchor-nav-panel-${Math.random().toString(36)
|
|
52
58
|
.slice(2, 11)}`;
|
|
53
59
|
this._onScrollSpy = this._onScrollSpy.bind(this);
|
|
@@ -61,6 +67,10 @@ class BdAnchorNav extends LitElement {
|
|
|
61
67
|
this._stickyPlaceholder = null;
|
|
62
68
|
this._onStickyScroll = null;
|
|
63
69
|
this._handleResize = this._handleResize.bind(this);
|
|
70
|
+
// sliding underline indicator: false until the first successful
|
|
71
|
+
// measurement + placement, so it doesn't visibly animate in from the
|
|
72
|
+
// left edge on first paint.
|
|
73
|
+
this._indicatorReady = false;
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
connectedCallback() {
|
|
@@ -85,8 +95,76 @@ class BdAnchorNav extends LitElement {
|
|
|
85
95
|
this._destroySticky();
|
|
86
96
|
}
|
|
87
97
|
|
|
98
|
+
updated(changedProperties) {
|
|
99
|
+
super.updated(changedProperties);
|
|
100
|
+
|
|
101
|
+
if (changedProperties.has("stickyTop")) {
|
|
102
|
+
this.style.setProperty("--sticky-top", this.stickyTop || "0px");
|
|
103
|
+
// the scroll-spy offset (how far to scroll past the sticky bar when
|
|
104
|
+
// clicking a link) depends on this too, since the nav's own visible
|
|
105
|
+
// top now sits lower than the viewport top while stuck.
|
|
106
|
+
this._runScrollSpy();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (changedProperties.has("activeId") || changedProperties.has("items")) {
|
|
110
|
+
// wait a frame so the .active class has actually painted before
|
|
111
|
+
// measuring its box — requestAnimationFrame is enough here, no need
|
|
112
|
+
// for updateComplete since nothing else in render() depends on this.
|
|
113
|
+
requestAnimationFrame(() => this._updateIndicator());
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!changedProperties.has("activeId")) return;
|
|
117
|
+
const prev = changedProperties.get("activeId");
|
|
118
|
+
if (prev === undefined || prev === "") return;
|
|
119
|
+
if (prev === this.activeId) return;
|
|
120
|
+
queueMicrotask(() => this._playMobileDropdownLabelSwap());
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
firstUpdated() {
|
|
124
|
+
const resolved = this._resolvedItems();
|
|
125
|
+
if (resolved.length) {
|
|
126
|
+
this.activeId = "anchor-1";
|
|
127
|
+
this.requestUpdate();
|
|
128
|
+
}
|
|
129
|
+
queueMicrotask(() => this._runScrollSpy());
|
|
130
|
+
this._handleMobileFullWidth();
|
|
131
|
+
requestAnimationFrame(() => this._updateIndicator());
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Positions the sliding underline under the currently active desktop
|
|
135
|
+
// link. Reads offsetLeft/offsetWidth directly off the active bd-link —
|
|
136
|
+
// as a flex item it's block-level regardless of its own :host display,
|
|
137
|
+
// so these are reliable without any extra math against a bounding rect.
|
|
138
|
+
_updateIndicator() {
|
|
139
|
+
const indicator = this.renderRoot?.querySelector(".anchor-indicator");
|
|
140
|
+
const activeLink = this.renderRoot?.querySelector(
|
|
141
|
+
".anchor-links--desktop .anchor-link.active"
|
|
142
|
+
);
|
|
143
|
+
if (!indicator || !activeLink) return;
|
|
144
|
+
|
|
145
|
+
const left = activeLink.offsetLeft;
|
|
146
|
+
const width = activeLink.offsetWidth;
|
|
147
|
+
|
|
148
|
+
if (!this._indicatorReady) {
|
|
149
|
+
// first placement: skip the transition so it doesn't slide in from
|
|
150
|
+
// the left edge on initial paint, then re-enable it next frame.
|
|
151
|
+
indicator.style.transition = "none";
|
|
152
|
+
indicator.style.transform = `translateX(${left}px)`;
|
|
153
|
+
indicator.style.width = `${width}px`;
|
|
154
|
+
// force a style flush before removing the transition override
|
|
155
|
+
void indicator.offsetWidth;
|
|
156
|
+
indicator.style.transition = "";
|
|
157
|
+
this._indicatorReady = true;
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
indicator.style.transform = `translateX(${left}px)`;
|
|
162
|
+
indicator.style.width = `${width}px`;
|
|
163
|
+
}
|
|
164
|
+
|
|
88
165
|
_handleResize() {
|
|
89
166
|
this._handleMobileFullWidth();
|
|
167
|
+
this._updateIndicator();
|
|
90
168
|
}
|
|
91
169
|
|
|
92
170
|
_handleMobileFullWidth() {
|
|
@@ -109,6 +187,7 @@ class BdAnchorNav extends LitElement {
|
|
|
109
187
|
|
|
110
188
|
_initSticky() {
|
|
111
189
|
this._stickyOriginalTop = null;
|
|
190
|
+
this.style.setProperty("--sticky-top", this.stickyTop || "0px");
|
|
112
191
|
|
|
113
192
|
this._onStickyScroll = () => {
|
|
114
193
|
const parent = this.parentElement;
|
|
@@ -118,14 +197,15 @@ class BdAnchorNav extends LitElement {
|
|
|
118
197
|
const navHeight = this._stickyPlaceholder
|
|
119
198
|
? this._stickyPlaceholder.offsetHeight
|
|
120
199
|
: this.offsetHeight;
|
|
200
|
+
const stickyTopPx = parseLengthToPx(this.stickyTop) || 0;
|
|
121
201
|
|
|
122
202
|
if (this._stickyOriginalTop === null) {
|
|
123
203
|
const rect = this.getBoundingClientRect();
|
|
124
|
-
this._stickyOriginalTop = rect.top + window.scrollY;
|
|
204
|
+
this._stickyOriginalTop = rect.top + window.scrollY - stickyTopPx;
|
|
125
205
|
}
|
|
126
206
|
|
|
127
207
|
const passedTop = window.scrollY >= this._stickyOriginalTop;
|
|
128
|
-
const parentExited = parentRect.bottom <= navHeight;
|
|
208
|
+
const parentExited = parentRect.bottom <= navHeight + stickyTopPx;
|
|
129
209
|
|
|
130
210
|
if (passedTop && !parentExited) {
|
|
131
211
|
if (!this.classList.contains("is-sticky")) {
|
|
@@ -297,25 +377,6 @@ class BdAnchorNav extends LitElement {
|
|
|
297
377
|
}
|
|
298
378
|
}
|
|
299
379
|
|
|
300
|
-
firstUpdated() {
|
|
301
|
-
const resolved = this._resolvedItems();
|
|
302
|
-
if (resolved.length) {
|
|
303
|
-
this.activeId = "anchor-1";
|
|
304
|
-
this.requestUpdate();
|
|
305
|
-
}
|
|
306
|
-
queueMicrotask(() => this._runScrollSpy());
|
|
307
|
-
this._handleMobileFullWidth();
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
updated(changedProperties) {
|
|
311
|
-
super.updated(changedProperties);
|
|
312
|
-
if (!changedProperties.has("activeId")) return;
|
|
313
|
-
const prev = changedProperties.get("activeId");
|
|
314
|
-
if (prev === undefined || prev === "") return;
|
|
315
|
-
if (prev === this.activeId) return;
|
|
316
|
-
queueMicrotask(() => this._playMobileDropdownLabelSwap());
|
|
317
|
-
}
|
|
318
|
-
|
|
319
380
|
_playMobileDropdownLabelSwap() {
|
|
320
381
|
if (typeof window === "undefined") return;
|
|
321
382
|
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
@@ -334,10 +395,11 @@ class BdAnchorNav extends LitElement {
|
|
|
334
395
|
|
|
335
396
|
_stickyOffsetPx() {
|
|
336
397
|
const barPx = this.offsetHeight;
|
|
398
|
+
const stickyTopPx = parseLengthToPx(this.stickyTop) || 0;
|
|
337
399
|
const insetRaw = getComputedStyle(this).getPropertyValue("--spacing-8")
|
|
338
400
|
.trim();
|
|
339
401
|
const insetPx = parseLengthToPx(insetRaw) || 8;
|
|
340
|
-
if (barPx > 0) return barPx + insetPx;
|
|
402
|
+
if (barPx > 0) return barPx + insetPx + stickyTopPx;
|
|
341
403
|
const fallback = getComputedStyle(this).getPropertyValue("--spacing-128")
|
|
342
404
|
.trim();
|
|
343
405
|
return parseLengthToPx(fallback) || 128;
|
|
@@ -413,6 +475,7 @@ class BdAnchorNav extends LitElement {
|
|
|
413
475
|
>${title}</bd-link>
|
|
414
476
|
`;
|
|
415
477
|
})}
|
|
478
|
+
<span class="anchor-indicator" aria-hidden="true"></span>
|
|
416
479
|
</div>
|
|
417
480
|
`;
|
|
418
481
|
|
|
@@ -27,6 +27,9 @@ prin \`ResizeObserver\` — fără a fi necesară setarea manuală a \`max-width
|
|
|
27
27
|
- Când navul iese din viewport, devine \`position: fixed\` full-width
|
|
28
28
|
- Border-ul și background-ul sunt full-width
|
|
29
29
|
- Conținutul (linkuri + CTA) rămâne centrat și aliniat cu cel mai lat sibling
|
|
30
|
+
- Dacă mai există un alt element sticky **deasupra** lui (ex. un header/page-switcher
|
|
31
|
+
propriu sticky la \`top: 0\`), setează \`sticky-top\` cu înălțimea acelui element —
|
|
32
|
+
altfel cele două se suprapun, fiindcă implicit navul se lipește tot la \`top: 0\`.
|
|
30
33
|
|
|
31
34
|
---
|
|
32
35
|
|
|
@@ -76,6 +79,18 @@ prin \`ResizeObserver\` — fără a fi necesară setarea manuală a \`max-width
|
|
|
76
79
|
></bd-anchor-nav>
|
|
77
80
|
\`\`\`
|
|
78
81
|
|
|
82
|
+
### Utilizare cu \`sticky-top\` (nav secundar sub un header sticky propriu)
|
|
83
|
+
\`\`\`html
|
|
84
|
+
<my-sticky-header></my-sticky-header>
|
|
85
|
+
|
|
86
|
+
<bd-anchor-nav sticky-top="64px" cta-label="Buy now" cta-href="#pricing">
|
|
87
|
+
<bd-anchor-nav-item title="Overview" href="#overview"></bd-anchor-nav-item>
|
|
88
|
+
<bd-anchor-nav-item title="Pricing" href="#pricing"></bd-anchor-nav-item>
|
|
89
|
+
</bd-anchor-nav>
|
|
90
|
+
\`\`\`
|
|
91
|
+
\`64px\` trebuie să fie înălțimea reală (măsurată în DevTools) a elementului sticky de deasupra —
|
|
92
|
+
nu-i o valoare universală, depinde de conținutul acelui header.
|
|
93
|
+
|
|
79
94
|
---
|
|
80
95
|
|
|
81
96
|
### Atribute \`bd-anchor-nav\`
|
|
@@ -85,6 +100,7 @@ prin \`ResizeObserver\` — fără a fi necesară setarea manuală a \`max-width
|
|
|
85
100
|
| \`cta-href\` | String | \`"#section-pricing"\` | href-ul butonului CTA fallback |
|
|
86
101
|
| \`aria-label\` | String | \`"Section navigation"\` | Label accesibil pentru \`<nav>\` |
|
|
87
102
|
| \`items\` | Array | — | \`{ title, href }[]\` — suprascrie child items |
|
|
103
|
+
| \`sticky-top\` | String | \`"0px"\` | Cât de departe de vârful viewport-ului se lipește navul când devine sticky — setează-l la înălțimea oricărui alt element sticky de deasupra, ca să nu se suprapună |
|
|
88
104
|
|
|
89
105
|
### Atribute \`bd-anchor-nav-item\`
|
|
90
106
|
| Atribut | Tip | Descriere |
|
|
@@ -114,6 +130,12 @@ Pe mobile devine automat full-width prin \`::slotted([slot="cta"])\`.
|
|
|
114
130
|
control : "object",
|
|
115
131
|
description: "Array de { title, href } — suprascrie child elements",
|
|
116
132
|
table : { type: { summary: "{ title: string, href: string }[]" }, category: "Content" }
|
|
133
|
+
},
|
|
134
|
+
stickyTop: {
|
|
135
|
+
name : "sticky-top",
|
|
136
|
+
control : "text",
|
|
137
|
+
description: "Cât de departe de vârful viewport-ului se lipește navul când devine sticky — setează-l la înălțimea oricărui element sticky de deasupra (ex. un header propriu), ca sa nu se suprapună la scroll",
|
|
138
|
+
table : { type: { summary: "string" }, defaultValue: { summary: "0px" }, category: "Sticky" }
|
|
117
139
|
}
|
|
118
140
|
}
|
|
119
141
|
};
|
|
@@ -404,6 +426,65 @@ export const WithScrollTargets = {
|
|
|
404
426
|
}
|
|
405
427
|
};
|
|
406
428
|
|
|
429
|
+
export const StickyBelowFixedHeader = {
|
|
430
|
+
name : "Sticky sub un header propriu — sticky-top",
|
|
431
|
+
render: () => html`
|
|
432
|
+
<div>
|
|
433
|
+
<!-- Simuleaza un header sticky separat (ex. bd-page-switcher), la fel
|
|
434
|
+
pozitionat: position: sticky; top: 0. Fara sticky-top pe nav,
|
|
435
|
+
cele doua s-ar suprapune identic la scroll. -->
|
|
436
|
+
<div style="
|
|
437
|
+
position: sticky; top: 0; z-index: 200;
|
|
438
|
+
background: #0a1a2f; color: #fff;
|
|
439
|
+
padding: 16px 24px; box-sizing: border-box;
|
|
440
|
+
font-family: sans-serif; font-weight: 700; font-size: 18px;
|
|
441
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
442
|
+
">
|
|
443
|
+
<span>Mock Header (simulează bd-page-switcher, ~64px înălțime)</span>
|
|
444
|
+
<span style="opacity: .7; font-weight: 400; font-size: 13px;">Tab A · Tab B</span>
|
|
445
|
+
</div>
|
|
446
|
+
|
|
447
|
+
<bd-anchor-nav
|
|
448
|
+
sticky-top="64px"
|
|
449
|
+
cta-label="Buy now"
|
|
450
|
+
cta-href="#anchor-3-section"
|
|
451
|
+
>
|
|
452
|
+
<bd-anchor-nav-item title="Overview" href="#anchor-1-section"></bd-anchor-nav-item>
|
|
453
|
+
<bd-anchor-nav-item title="Features" href="#anchor-2-section"></bd-anchor-nav-item>
|
|
454
|
+
<bd-anchor-nav-item title="Pricing" href="#anchor-3-section"></bd-anchor-nav-item>
|
|
455
|
+
</bd-anchor-nav>
|
|
456
|
+
|
|
457
|
+
<div style="max-width: 800px; margin: 0 auto;">
|
|
458
|
+
<div id="anchor-1-section" style="min-height: 60vh; padding: 80px 24px; background: #EDF9FF;">
|
|
459
|
+
<bd-h as="h2">Overview Section</bd-h>
|
|
460
|
+
<bd-p kind="regular">
|
|
461
|
+
Scroll în jos: mock header-ul de sus rămâne fix la top: 0, iar
|
|
462
|
+
bd-anchor-nav se lipește exact sub el (la 64px), nu peste el.
|
|
463
|
+
</bd-p>
|
|
464
|
+
</div>
|
|
465
|
+
<div id="anchor-2-section" style="min-height: 60vh; padding: 80px 24px; background: #fff;">
|
|
466
|
+
<bd-h as="h2">Features Section</bd-h>
|
|
467
|
+
</div>
|
|
468
|
+
<div id="anchor-3-section" style="min-height: 60vh; padding: 80px 24px; background: #EDF9FF;">
|
|
469
|
+
<bd-h as="h2">Pricing Section</bd-h>
|
|
470
|
+
</div>
|
|
471
|
+
</div>
|
|
472
|
+
</div>
|
|
473
|
+
`,
|
|
474
|
+
parameters: {
|
|
475
|
+
docs: {
|
|
476
|
+
description: {
|
|
477
|
+
story: `Reproduce cazul real din demo-ul AI Guardian / Skill Scanner: un header sticky
|
|
478
|
+
propriu deasupra (aici simulat printr-un \`<div>\`, în producție \`bd-page-switcher\`)
|
|
479
|
+
și \`bd-anchor-nav\` cu \`sticky-top="64px"\`. Fără acest atribut, ambele s-ar
|
|
480
|
+
fixa la \`top: 0\` și s-ar suprapune perfect la scroll. Valoarea \`64px\` trebuie
|
|
481
|
+
să corespundă înălțimii reale a elementului sticky de deasupra — verifică în
|
|
482
|
+
DevTools dacă diferă la tine.`
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
|
|
407
488
|
export const MobileView = {
|
|
408
489
|
name : "Mobile View",
|
|
409
490
|
render : () => renderWithSlotCta(["Overview", "Features", "Reviews", "Pricing"]),
|
|
@@ -420,9 +501,10 @@ export const MobileView = {
|
|
|
420
501
|
export const Playground = {
|
|
421
502
|
name: "Playground",
|
|
422
503
|
args: {
|
|
423
|
-
ctaLabel: "Buy now",
|
|
424
|
-
ctaHref
|
|
425
|
-
|
|
504
|
+
ctaLabel : "Buy now",
|
|
505
|
+
ctaHref : "#section-pricing",
|
|
506
|
+
stickyTop: "0px",
|
|
507
|
+
items : [
|
|
426
508
|
{ title: "Overview", href: "#anchor-1-section" },
|
|
427
509
|
{ title: "Features", href: "#anchor-2-section" },
|
|
428
510
|
{ title: "Reviews", href: "#anchor-3-section" },
|
|
@@ -433,13 +515,14 @@ export const Playground = {
|
|
|
433
515
|
<bd-anchor-nav
|
|
434
516
|
cta-label=${args.ctaLabel}
|
|
435
517
|
cta-href=${args.ctaHref}
|
|
518
|
+
sticky-top=${args.stickyTop}
|
|
436
519
|
.items=${args.items}
|
|
437
520
|
></bd-anchor-nav>
|
|
438
521
|
`,
|
|
439
522
|
parameters: {
|
|
440
523
|
docs: {
|
|
441
524
|
description: {
|
|
442
|
-
story: "Playground interactiv. Editează `items`, `ctaLabel` și `
|
|
525
|
+
story: "Playground interactiv. Editează `items`, `ctaLabel`, `ctaHref` și `sticky-top` din Controls."
|
|
443
526
|
}
|
|
444
527
|
}
|
|
445
528
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import "../button/button.js";
|
|
3
|
+
import "./cta-banner.js";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title : "Components/CtaBanner",
|
|
7
|
+
tags : ["autodocs"],
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: "fullscreen",
|
|
10
|
+
docs : {
|
|
11
|
+
description: {
|
|
12
|
+
component: `
|
|
13
|
+
Full-width closing call-to-action banner on a blue gradient background —
|
|
14
|
+
centered title, description, one or two buttons, and an optional small
|
|
15
|
+
mono caption underneath.
|
|
16
|
+
|
|
17
|
+
\`\`\`html
|
|
18
|
+
<bd-cta-banner
|
|
19
|
+
title="Run your AI agents with a guardrail"
|
|
20
|
+
description="AI Guardian is in open BETA on macOS. Download the BETA and secure your agentic workflows."
|
|
21
|
+
caption="Downloads the macOS BETA (.dmg). Free during BETA."
|
|
22
|
+
>
|
|
23
|
+
<bd-button-link slot="primary-button" kind="white" strong>Download BETA Today</bd-button-link>
|
|
24
|
+
<bd-button-link slot="secondary-button" kind="outline-white" strong>Read the docs</bd-button-link>
|
|
25
|
+
</bd-cta-banner>
|
|
26
|
+
\`\`\`
|
|
27
|
+
|
|
28
|
+
### Attribute table
|
|
29
|
+
| Attribute / slot | Type | Description |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| \`title\` / \`slot="title"\` | String | Large centred heading |
|
|
32
|
+
| \`description\` / \`slot="description"\` | String | Paragraph under the title |
|
|
33
|
+
| \`slot="primary-button"\` | — | First button, typically \`kind="white"\` |
|
|
34
|
+
| \`slot="secondary-button"\` | — | Second button, typically \`kind="outline-white"\` |
|
|
35
|
+
| \`caption\` / \`slot="caption"\` | String | Optional small mono line under the buttons |
|
|
36
|
+
|
|
37
|
+
Buttons are expected to use \`bd-button-link\` with \`kind="white"\` /
|
|
38
|
+
\`kind="outline-white"\` and \`strong\`, so they read against the blue
|
|
39
|
+
background — that pairing isn't enforced by the component, just the
|
|
40
|
+
convention used everywhere it's been placed so far.
|
|
41
|
+
`
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// ─── Cu caption (AI Guardian) ──────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
export const WithCaption = {
|
|
50
|
+
name : "Cu caption",
|
|
51
|
+
render: () => html`
|
|
52
|
+
<bd-cta-banner
|
|
53
|
+
title="Run your AI agents with a guardrail"
|
|
54
|
+
description="AI Guardian is in open BETA on macOS. Download the BETA and secure your agentic workflows."
|
|
55
|
+
caption="Downloads the macOS BETA (.dmg). Free during BETA."
|
|
56
|
+
>
|
|
57
|
+
<bd-button-link
|
|
58
|
+
slot="primary-button"
|
|
59
|
+
label="Download BETA Today"
|
|
60
|
+
kind="white"
|
|
61
|
+
size="md"
|
|
62
|
+
strong
|
|
63
|
+
>Download BETA Today</bd-button-link>
|
|
64
|
+
|
|
65
|
+
<bd-button-link
|
|
66
|
+
slot="secondary-button"
|
|
67
|
+
label="Read the docs"
|
|
68
|
+
kind="outline-white"
|
|
69
|
+
size="md"
|
|
70
|
+
strong
|
|
71
|
+
>Read the docs</bd-button-link>
|
|
72
|
+
</bd-cta-banner>
|
|
73
|
+
`
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// ─── Fara caption (Skill Scanner) ──────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
export const WithoutCaption = {
|
|
79
|
+
name : "Fără caption",
|
|
80
|
+
render: () => html`
|
|
81
|
+
<bd-cta-banner
|
|
82
|
+
title="Scan before you trust"
|
|
83
|
+
description="A skill is code you hand to your agent. One command stands between you and running it blind."
|
|
84
|
+
>
|
|
85
|
+
<bd-button-link
|
|
86
|
+
slot="primary-button"
|
|
87
|
+
label="Install with npx"
|
|
88
|
+
kind="white"
|
|
89
|
+
size="md"
|
|
90
|
+
strong
|
|
91
|
+
>Install with npx</bd-button-link>
|
|
92
|
+
|
|
93
|
+
<bd-button-link
|
|
94
|
+
slot="secondary-button"
|
|
95
|
+
label="View on GitHub"
|
|
96
|
+
kind="outline-white"
|
|
97
|
+
size="md"
|
|
98
|
+
strong
|
|
99
|
+
>View on GitHub</bd-button-link>
|
|
100
|
+
</bd-cta-banner>
|
|
101
|
+
`
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// ─── Un singur buton ────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
export const SingleButton = {
|
|
107
|
+
name : "Un singur buton",
|
|
108
|
+
render: () => html`
|
|
109
|
+
<bd-cta-banner
|
|
110
|
+
title="Ready to get started?"
|
|
111
|
+
description="Free during BETA, no waitlist and no licence key."
|
|
112
|
+
>
|
|
113
|
+
<bd-button-link
|
|
114
|
+
slot="primary-button"
|
|
115
|
+
label="Install now"
|
|
116
|
+
kind="white"
|
|
117
|
+
size="md"
|
|
118
|
+
strong
|
|
119
|
+
>Install now</bd-button-link>
|
|
120
|
+
</bd-cta-banner>
|
|
121
|
+
`
|
|
122
|
+
};
|
|
@@ -43,6 +43,12 @@ export default css`
|
|
|
43
43
|
margin: 0 auto var(--spacing-40);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/* Slot generic pentru orice continut in afara grid-ului fix de carduri */
|
|
47
|
+
.bd-installoptions-body {
|
|
48
|
+
max-width: var(--layout-ensemble-max-width, 1200px);
|
|
49
|
+
margin: 0 auto;
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
.bd-installoptions-worksin-label {
|
|
47
53
|
color: var(--color-neutral-500);
|
|
48
54
|
font-family: var(--typography-fontFamily-mono, monospace);
|
|
@@ -171,10 +171,25 @@ class BdInstallOptions extends LitElement {
|
|
|
171
171
|
|
|
172
172
|
${this._renderWorksIn()}
|
|
173
173
|
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
<!--
|
|
175
|
+
Slot generic (fara nume) -- pentru orice continut care nu se
|
|
176
|
+
potriveste cu grid-ul fix de carduri (ex: un tabel, o casuta
|
|
177
|
+
checklist, o nota de avertizare). Cardurile ramase implicite
|
|
178
|
+
(this.cards) tot se randeaza mai jos daca sunt pasate; cele doua
|
|
179
|
+
pot coexista sau poti folosi doar unul din ele.
|
|
180
|
+
-->
|
|
181
|
+
<div class="bd-installoptions-body">
|
|
182
|
+
<slot></slot>
|
|
176
183
|
</div>
|
|
177
184
|
|
|
185
|
+
${this.cards.length
|
|
186
|
+
? html`
|
|
187
|
+
<div class="bd-installoptions-grid">
|
|
188
|
+
${this.cards.map((card) => this._renderCard(card))}
|
|
189
|
+
</div>
|
|
190
|
+
`
|
|
191
|
+
: nothing}
|
|
192
|
+
|
|
178
193
|
${this._renderPromptPanel()}
|
|
179
194
|
</div>
|
|
180
195
|
`;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import "../compatibility-table/compatibility-table.js";
|
|
3
|
+
import "../heading/heading.js";
|
|
4
|
+
import "../paragraph/paragraph.js";
|
|
5
|
+
import "./install-options.js";
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
title : "Components/InstallOptions",
|
|
9
|
+
tags : ["autodocs"],
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: "fullscreen",
|
|
12
|
+
docs : {
|
|
13
|
+
description: {
|
|
14
|
+
component: `
|
|
15
|
+
Section container (eyebrow + title + description on a light blue background)
|
|
16
|
+
for "how to get this" style pages. Three ways to fill it, usable on their
|
|
17
|
+
own or combined:
|
|
18
|
+
|
|
19
|
+
1. **Cards grid** (\`.cards\`) — a row of install-method cards, each with an
|
|
20
|
+
optional badge, a copyable code block, and a link.
|
|
21
|
+
2. **"Then just ask" prompt panel** (\`prompt-panel\` + slots) — a copyable
|
|
22
|
+
prompt block with a label, a warning-style note, and a footer line.
|
|
23
|
+
3. **Generic default slot** — anything else (a table, a checklist, a note)
|
|
24
|
+
that doesn't fit the cards grid, while still getting the shared
|
|
25
|
+
eyebrow/title/description header.
|
|
26
|
+
|
|
27
|
+
\`\`\`html
|
|
28
|
+
<!-- Cards + Works in pills -->
|
|
29
|
+
<bd-install-options
|
|
30
|
+
eyebrow="Get it"
|
|
31
|
+
title="Three ways to install"
|
|
32
|
+
description="..."
|
|
33
|
+
></bd-install-options>
|
|
34
|
+
|
|
35
|
+
<!-- Generic slot instead of cards -->
|
|
36
|
+
<bd-install-options eyebrow="Trust & transparency" title="What leaves your machine">
|
|
37
|
+
<bd-compatibility-table variant="risk" .columns=\${["Mode", "Transmitted"]} .rows=\${[...]}></bd-compatibility-table>
|
|
38
|
+
</bd-install-options>
|
|
39
|
+
\`\`\`
|
|
40
|
+
|
|
41
|
+
### Attribute / property table
|
|
42
|
+
| Name | Type | Default | Description |
|
|
43
|
+
|---|---|---|---|
|
|
44
|
+
| \`eyebrow\` | String | — | Small blue label above the title |
|
|
45
|
+
| \`title\` | String | — | Section heading |
|
|
46
|
+
| \`description\` | String | — | Paragraph under the title |
|
|
47
|
+
| \`.worksIn\` | Array | \`[]\` | \`[{ label, initial, color }]\` — pill row. \`initial\`/\`color\` are placeholders (a letter on a solid circle) until real brand marks are available |
|
|
48
|
+
| \`.cards\` | Array | \`[]\` | \`[{ title, badge, description, code, linkLabel, linkHref }]\` — rendered only if non-empty |
|
|
49
|
+
| *(default slot)* | — | — | Anything else — renders between the description/works-in row and the cards grid |
|
|
50
|
+
| \`prompt-panel\` | Boolean | \`false\` | Shows the "Then just ask" sub-panel below the cards |
|
|
51
|
+
| \`prompt-label\` | String | — | Small mono label above the prompt code block |
|
|
52
|
+
|
|
53
|
+
### Slots (only used when \`prompt-panel\` is set)
|
|
54
|
+
| Slot | Description |
|
|
55
|
+
|---|---|
|
|
56
|
+
| \`prompt-title\` | Heading of the sub-panel |
|
|
57
|
+
| \`prompt-description\` | Paragraph under that heading |
|
|
58
|
+
| \`prompt\` | The copyable prompt text — the Copy button reads this slot's \`textContent\` |
|
|
59
|
+
| \`prompt-note\` | Warning-style note box |
|
|
60
|
+
| \`prompt-footer\` | Closing line (e.g. uninstall instructions) |
|
|
61
|
+
`
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// ─── Carduri + Works in (cazul principal: "Three ways to install") ────────
|
|
68
|
+
|
|
69
|
+
export const CardsWithWorksIn = {
|
|
70
|
+
name : "Carduri + Works in",
|
|
71
|
+
render: () => html`
|
|
72
|
+
<bd-install-options
|
|
73
|
+
eyebrow="Get it"
|
|
74
|
+
title="Three ways to install"
|
|
75
|
+
description="Agent Skill Scanner installs in under a minute with npx via skills.sh, by cloning the GitHub repository, or as a zip archive you drop into your skills directory. It runs inside your agent harness and installs its own Python dependencies on first use."
|
|
76
|
+
.worksIn=${[
|
|
77
|
+
{ label: "Claude Code", initial: "✻", color: "#d97706" },
|
|
78
|
+
{ label: "Codex", initial: "◒", color: "#111827" },
|
|
79
|
+
{ label: "OpenClaw", initial: "▣", color: "#dc2626" },
|
|
80
|
+
{ label: "opencode", initial: "▪", color: "#111827" },
|
|
81
|
+
{ label: "Hermes Agent", initial: "H", color: "#111827" }
|
|
82
|
+
]}
|
|
83
|
+
.cards=${[
|
|
84
|
+
{
|
|
85
|
+
title : "With npx, from skills.sh",
|
|
86
|
+
badge : "Recommended",
|
|
87
|
+
description: "One command via the skills.sh installer. Works for any supported harness.",
|
|
88
|
+
code : "npx skills add bitdefender/agent-skill-scanner",
|
|
89
|
+
linkLabel : "View on skills.sh",
|
|
90
|
+
linkHref : "#"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
title : "From GitHub",
|
|
94
|
+
description: "Clone the repository and copy the inner agent-skill-scanner/ folder into your skills directory.",
|
|
95
|
+
code : "git clone https://github.com/bitdefender/agent-skill-scanner.git\ncp -r agent-skill-scanner/agent-skill-scanner ~/.claude/skills/",
|
|
96
|
+
linkLabel : "View the repository",
|
|
97
|
+
linkHref : "#"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
title : "As a zip archive",
|
|
101
|
+
description: "Download the archive, unzip it, and move the inner agent-skill-scanner/ folder into your skills directory.",
|
|
102
|
+
linkLabel : "Download the zip",
|
|
103
|
+
linkHref : "#"
|
|
104
|
+
}
|
|
105
|
+
]}
|
|
106
|
+
></bd-install-options>
|
|
107
|
+
`
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// ─── Carduri + prompt panel ("Then just ask") ──────────────────────────────
|
|
111
|
+
|
|
112
|
+
export const WithPromptPanel = {
|
|
113
|
+
name : "Carduri + prompt panel",
|
|
114
|
+
parameters: {
|
|
115
|
+
docs: {
|
|
116
|
+
description: {
|
|
117
|
+
story: "Aceleași carduri, plus sub-panelul \"Then just ask\" activat prin `prompt-panel`. Butonul Copy citește textul din `slot=\"prompt\"` la click, indiferent de markup."
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
render: () => html`
|
|
122
|
+
<bd-install-options
|
|
123
|
+
eyebrow="Get it"
|
|
124
|
+
title="Three ways to install"
|
|
125
|
+
description="Agent Skill Scanner installs in under a minute with npx via skills.sh, by cloning the GitHub repository, or as a zip archive you drop into your skills directory."
|
|
126
|
+
.cards=${[
|
|
127
|
+
{
|
|
128
|
+
title : "With npx, from skills.sh",
|
|
129
|
+
badge : "Recommended",
|
|
130
|
+
description: "One command via the skills.sh installer.",
|
|
131
|
+
code : "npx skills add bitdefender/agent-skill-scanner",
|
|
132
|
+
linkLabel : "View on skills.sh",
|
|
133
|
+
linkHref : "#"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
title : "From GitHub",
|
|
137
|
+
description: "Clone the repository into your skills directory.",
|
|
138
|
+
code : "git clone https://github.com/bitdefender/agent-skill-scanner.git",
|
|
139
|
+
linkLabel : "View the repository",
|
|
140
|
+
linkHref : "#"
|
|
141
|
+
}
|
|
142
|
+
]}
|
|
143
|
+
prompt-panel
|
|
144
|
+
prompt-label="Prompt for your agent"
|
|
145
|
+
>
|
|
146
|
+
<span slot="prompt-title">Then just ask</span>
|
|
147
|
+
<span slot="prompt-description">There is no command to learn. Once installed, paste this prompt into your agent; it handles invocation, target resolution and update checks.</span>
|
|
148
|
+
<span slot="prompt">Scan the <skill name or path> skill for security risks and show me the report.</span>
|
|
149
|
+
<bd-p slot="prompt-note" kind="small">Requires Python 3.12 or 3.13 (3.13 recommended). Dependencies install on first use.</bd-p>
|
|
150
|
+
<span slot="prompt-footer">To uninstall, delete the agent-skill-scanner folder from your skills directory.</span>
|
|
151
|
+
</bd-install-options>
|
|
152
|
+
`
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// ─── Slot generic in loc de carduri (tabel + checklist + nota) ────────────
|
|
156
|
+
|
|
157
|
+
export const GenericSlotContent = {
|
|
158
|
+
name : "Slot generic (tabel + checklist + notă)",
|
|
159
|
+
parameters: {
|
|
160
|
+
docs: {
|
|
161
|
+
description: {
|
|
162
|
+
story: "Aceeași secțiune (eyebrow + titlu + descriere pe fundal albastru), dar fără `.cards` deloc — în schimb, orice conținut arbitrar prin slotul generic: aici un `bd-compatibility-table` (variant `risk`, cu antete custom prin `.columns`), o casetă cu checklist, și o notă de avertizare, scrise ca markup simplu în locul consumator."
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
render: () => html`
|
|
167
|
+
<bd-install-options
|
|
168
|
+
eyebrow="Trust & transparency"
|
|
169
|
+
title="What leaves your machine"
|
|
170
|
+
description="Two modes, both agent-driven. Scan is the default: only hashes, extracted indicators and metadata are transmitted, and full files are never uploaded. Submit is opt-in: the complete skill archive is uploaded for deeper analysis, and only when you explicitly ask for it."
|
|
171
|
+
>
|
|
172
|
+
<bd-compatibility-table
|
|
173
|
+
variant="risk"
|
|
174
|
+
.columns=${["Mode", "Transmitted"]}
|
|
175
|
+
.rows=${[
|
|
176
|
+
{
|
|
177
|
+
category: "Scan (default)",
|
|
178
|
+
coverage: "File hashes (SHA-256/MD5), extracted indicators and pattern matches. Full file contents are not transmitted."
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
category: "Submit (opt-in)",
|
|
182
|
+
coverage: "The complete skill archive, uploaded for deep analysis. The agent will not do this unless you explicitly ask."
|
|
183
|
+
}
|
|
184
|
+
]}
|
|
185
|
+
></bd-compatibility-table>
|
|
186
|
+
|
|
187
|
+
<div style="background: var(--color-neutral-0); border: 1px solid var(--color-neutral-100); border-radius: var(--radius-xl); padding: 24px; box-sizing: border-box; margin-top: 24px;">
|
|
188
|
+
<bd-h as="h3" style="margin: 0 0 16px;">The contract the agent is held to</bd-h>
|
|
189
|
+
<ul style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 16px;">
|
|
190
|
+
<li style="display: flex; align-items: flex-start; gap: 12px;">
|
|
191
|
+
<span aria-hidden="true" style="flex-shrink: 0; width: 20px; height: 20px; border-radius: 999px; background: var(--color-green-600, #16a34a); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; margin-top: 2px;">✓</span>
|
|
192
|
+
<bd-p kind="regular" style="margin:0;">Scan mode is the default: a casual request never uploads your files.</bd-p>
|
|
193
|
+
</li>
|
|
194
|
+
<li style="display: flex; align-items: flex-start; gap: 12px;">
|
|
195
|
+
<span aria-hidden="true" style="flex-shrink: 0; width: 20px; height: 20px; border-radius: 999px; background: var(--color-green-600, #16a34a); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; margin-top: 2px;">✓</span>
|
|
196
|
+
<bd-p kind="regular" style="margin:0;">Submit is opt-in only: the agent won't upload the full archive unless you explicitly ask.</bd-p>
|
|
197
|
+
</li>
|
|
198
|
+
</ul>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<div style="background: var(--color-yellow-50, #fffbeb); border: 1px solid var(--color-yellow-300, #fcd34d); border-radius: var(--radius-md); padding: 16px 20px; color: var(--color-yellow-800, #92400e); margin-top: 24px;">
|
|
202
|
+
<bd-p kind="small" style="margin:0;">A scan that returns no findings means no known threat patterns were detected — not that the skill is safe.</bd-p>
|
|
203
|
+
</div>
|
|
204
|
+
</bd-install-options>
|
|
205
|
+
`
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// ─── Doar header, fara carduri si fara slot ────────────────────────────────
|
|
209
|
+
|
|
210
|
+
export const HeaderOnly = {
|
|
211
|
+
name : "Doar header (fără carduri, fără slot)",
|
|
212
|
+
render: () => html`
|
|
213
|
+
<bd-install-options
|
|
214
|
+
eyebrow="Get it"
|
|
215
|
+
title="Coming soon"
|
|
216
|
+
description="Installation instructions will appear here once the package is published."
|
|
217
|
+
></bd-install-options>
|
|
218
|
+
`
|
|
219
|
+
};
|
|
@@ -5,8 +5,9 @@ export default css`
|
|
|
5
5
|
display: block;
|
|
6
6
|
width: 100%;
|
|
7
7
|
box-sizing: border-box;
|
|
8
|
-
max-width: 1290px;
|
|
9
|
-
|
|
8
|
+
max-width: var(--layout-ensemble-max-width, 1290px);
|
|
9
|
+
margin: 0 auto;
|
|
10
|
+
padding-inline: var(--layout-ensemble-inline-padding, var(--spacing-24));
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
.bd-reviewgrid-title {
|