artifacty 0.15.0 → 0.16.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "artifacty",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Local artifact exchange for heterogeneous LLM agents via HTTP and MCP.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -29,6 +29,7 @@
29
29
  "mcp": "node src/mcp-server.js",
30
30
  "test": "node --test",
31
31
  "lint": "node scripts/lint.mjs",
32
+ "build:ui": "node scripts/build-ui.mjs",
32
33
  "smoke": "bash scripts/smoke.sh",
33
34
  "release:check": "npm run lint && npm test && npm run smoke"
34
35
  },
@@ -38,15 +39,18 @@
38
39
  "docs/assets/artifacty.png",
39
40
  "docs/central-team-deployment-design.md",
40
41
  "docs/integrations.md",
42
+ "docs/language-packs.md",
41
43
  "docs/mcp-public-api.md",
42
44
  "docs/network-sharing.md",
43
45
  "docs/release-checklist.md",
44
46
  "docs/roadmap-design.md",
45
47
  "docs/sarif-csv-artifact-plan.md",
48
+ "docs/smtp.md",
46
49
  "docs/threat-model.md",
47
50
  "docs/upgrade-backups.md",
48
51
  "docs/writing-style.md",
49
52
  "scripts/lint.mjs",
53
+ "scripts/build-ui.mjs",
50
54
  "scripts/smoke.sh",
51
55
  "README.md",
52
56
  "LICENSE",
@@ -76,5 +80,10 @@
76
80
  "@babel/standalone": "^8.0.4",
77
81
  "react": "^18.3.1",
78
82
  "react-dom": "^18.3.1"
83
+ },
84
+ "devDependencies": {
85
+ "@tabler/icons": "^3.49.0",
86
+ "bootstrap": "^5.3.8",
87
+ "sass": "^1.105.1"
79
88
  }
80
89
  }
@@ -0,0 +1,16 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import * as sass from 'sass';
3
+ const root = new URL('../', import.meta.url);
4
+ const result = sass.compile(new URL('src/styles/ui-controls.scss', root).pathname, {
5
+ loadPaths: [new URL('node_modules', root).pathname], style: 'compressed',
6
+ quietDeps: true, silenceDeprecations: ['import', 'global-builtin', 'color-functions', 'if-function'],
7
+ });
8
+ await writeFile(new URL('src/client/ui-controls.css', root), '/*! Bootstrap v5.3.8 | MIT | https://getbootstrap.com/ | Generated by npm run build:ui */\n' + result.css + '\n');
9
+ const names = ['layout-sidebar-left-collapse','layout-sidebar-left-expand','files','inbox','plus','upload','settings','users','database-export','archive','webhook','refresh','language','mail','user-circle','key','logout','login','code','chevron-down','device-desktop','sun','moon'];
10
+ const icons = {};
11
+ for (const name of names) {
12
+ const svg = await readFile(new URL(`node_modules/@tabler/icons/icons/outline/${name}.svg`, root), 'utf8');
13
+ icons[name] = svg.slice(svg.indexOf('>') + 1, svg.lastIndexOf('</svg>')).trim().replace(/\s+/g, ' ');
14
+ }
15
+ await writeFile(new URL('src/lib/ui-icons.js', root), `// Generated from @tabler/icons v3.49.0 (MIT), by npm run build:ui.\nconst paths = ${JSON.stringify(icons, null, 2)};\n\nexport function uiIcon(name, { size = 18 } = {}) {\n if (!Object.hasOwn(paths, name)) throw new Error('Unknown UI icon');\n const dimension = [16, 18, 20, 24].includes(size) ? size : 18;\n return '<svg class="ui-icon" width="' + dimension + '" height="' + dimension + '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">' + paths[name] + '</svg>';\n}\n`);
16
+ console.log('Built local Bootstrap controls and 23 Tabler icons.');
package/src/cli.js CHANGED
@@ -503,7 +503,7 @@ async function main() {
503
503
  const id = options._[0];
504
504
  const visibility = options._[1];
505
505
  if (!id || !visibility) {
506
- throw new Error("visibility requires an artifact id and a value: private or team");
506
+ throw new Error("visibility requires an artifact id and a value: private, team, or public");
507
507
  }
508
508
  const artifact = await setArtifactVisibility(store, id, visibility, { audit: cliAuditContext() });
509
509
  printJson(await withUrls(store, artifact));
@@ -1006,17 +1006,17 @@ Usage:
1006
1006
  artifacty status [--home ~/.artifacty]
1007
1007
  artifacty stop [--home ~/.artifacty] [--timeout 30000] [--force]
1008
1008
  artifacty doctor [--home ~/.artifacty] [--skip-mcp] [--timeout 5000]
1009
- artifacty publish --title <title> (--file <path> | --content <text>) [--format html|markdown|text|json|code|svg|mermaid|react|notebook] [--source agent] [--tag tag] [--visibility private|team]
1009
+ artifacty publish --title <title> (--file <path> | --content <text>) [--format html|markdown|text|json|code|svg|mermaid|react|notebook] [--source agent] [--tag tag] [--visibility private|team|public]
1010
1010
  artifacty import --agent claude|codex|gemini|copilot|cursor|auto (--file <path> | --content <text>) [--title <title>] [--format html|markdown|text|json|code|svg|mermaid|react|notebook] [--tag tag]
1011
1011
  artifacty install claude|codex|gemini|copilot|cursor|all [--dry-run] [--config <path>] [--server-path <path>] [--url http://127.0.0.1:8787] [--mcp-url http://127.0.0.1:8787/mcp] [--api-token token] [--transport local|bridge] [--timeout 30000]
1012
1012
  artifacty check [--server-path <path>] [--timeout 5000]
1013
1013
  artifacty update [--check] [--target-version <version>] [--home <path>]
1014
1014
  artifacty upgrade [--check] [--target-version <version>] [--home <path>]
1015
- artifacty update <id> (--file <path> | --content <text>) [--title <title>] [--format html|markdown|text|json|code|svg|mermaid|react|notebook] [--expected-version N] [--visibility private|team]
1015
+ artifacty update <id> (--file <path> | --content <text>) [--title <title>] [--format html|markdown|text|json|code|svg|mermaid|react|notebook] [--expected-version N] [--visibility private|team|public]
1016
1016
  artifacty samples install|remove
1017
1017
  artifacty archive <id>
1018
1018
  artifacty restore <id>
1019
- artifacty visibility <id> private|team
1019
+ artifacty visibility <id> private|team|public
1020
1020
  artifacty audit [--artifact <id>] [--limit 100]
1021
1021
  artifacty index rebuild [--embeddings]
1022
1022
  artifacty integrity
@@ -0,0 +1,32 @@
1
+ // Progressive enhancement only: native disclosures and examples work without JavaScript.
2
+ export function matchesOperation(searchText, query) {
3
+ return query.trim().toLowerCase().split(/\s+/).every((word) => searchText.toLowerCase().includes(word));
4
+ }
5
+
6
+ export function enhanceApiDocs(document, clipboard) {
7
+ const search = document.querySelector('#api-search');
8
+ const operations = [...document.querySelectorAll('[data-operation]')];
9
+ if (search) {
10
+ document.querySelector('[data-search-controls]').hidden = false;
11
+ search.addEventListener('input', () => {
12
+ for (const operation of operations) operation.hidden = !matchesOperation(operation.dataset.search, search.value);
13
+ for (const group of document.querySelectorAll('[data-tag-group]')) group.hidden = ![...group.querySelectorAll('[data-operation]')].some((operation) => !operation.hidden);
14
+ document.querySelector('[data-no-results]').hidden = operations.some((operation) => !operation.hidden);
15
+ });
16
+ }
17
+ if (!clipboard?.writeText) return;
18
+ for (const button of document.querySelectorAll('[data-copy]')) {
19
+ button.hidden = false;
20
+ button.addEventListener('click', async () => {
21
+ const status = document.querySelector('[data-copy-status]');
22
+ try {
23
+ await clipboard.writeText(button.parentElement.querySelector('code').textContent);
24
+ status.textContent = document.body.dataset.copySuccess;
25
+ } catch {
26
+ status.textContent = document.body.dataset.copyFailure;
27
+ }
28
+ });
29
+ }
30
+ }
31
+
32
+ if (typeof document !== 'undefined') enhanceApiDocs(document, navigator.clipboard);
@@ -0,0 +1,20 @@
1
+ // Only opt-in GET forms auto-apply. Account and server mutations stay explicit.
2
+ export function enhanceAutoApply(document) {
3
+ for (const form of document.querySelectorAll('form[data-auto-apply]')) {
4
+ if (form.method.toLowerCase() !== 'get' || typeof form.requestSubmit !== 'function') continue;
5
+ const controls = form.dataset.autoApply === 'filters'
6
+ ? form.querySelectorAll('.dashboard-advanced select, .dashboard-advanced input')
7
+ : form.querySelectorAll('select');
8
+ for (const control of controls) {
9
+ control.addEventListener('change', () => {
10
+ if (!form.reportValidity()) return;
11
+ form.requestSubmit();
12
+ });
13
+ }
14
+ const buttons = form.dataset.autoApply === 'filters'
15
+ ? form.querySelectorAll('.dashboard-filter-actions')
16
+ : form.querySelectorAll('button[type="submit"]');
17
+ for (const button of buttons) button.hidden = true;
18
+ }
19
+ }
20
+ if (typeof document !== 'undefined') enhanceAutoApply(document);
@@ -0,0 +1,31 @@
1
+ let sidebarCollapsed = false;
2
+ try { sidebarCollapsed = localStorage.getItem("artifacty_sidebar") === "collapsed"; } catch { /* Default expanded. */ }
3
+
4
+ function applySidebar(collapsed) {
5
+ sidebarCollapsed = collapsed;
6
+ if (collapsed) document.documentElement.setAttribute("data-sidebar", "collapsed");
7
+ else document.documentElement.removeAttribute("data-sidebar");
8
+ for (const button of document.querySelectorAll("[data-sidebar-toggle]")) {
9
+ button.setAttribute("aria-expanded", String(!collapsed));
10
+ const label = collapsed ? button.dataset.expandLabel : button.dataset.collapseLabel;
11
+ button.setAttribute("aria-label", label);
12
+ button.title = label;
13
+ button.querySelector("[data-sidebar-collapse-icon]").hidden = collapsed;
14
+ button.querySelector("[data-sidebar-expand-icon]").hidden = !collapsed;
15
+ }
16
+ }
17
+ applySidebar(sidebarCollapsed);
18
+ document.addEventListener("DOMContentLoaded", () => {
19
+ for (const button of document.querySelectorAll("[data-sidebar-toggle]")) {
20
+ button.hidden = false;
21
+ button.addEventListener("click", () => {
22
+ applySidebar(!sidebarCollapsed);
23
+ try { localStorage.setItem("artifacty_sidebar", sidebarCollapsed ? "collapsed" : "expanded"); } catch { /* Current page remains usable. */ }
24
+ });
25
+ }
26
+ applySidebar(sidebarCollapsed);
27
+ });
28
+ window.addEventListener("storage", (event) => {
29
+ if (event.key !== "artifacty_sidebar" && event.key !== null) return;
30
+ try { applySidebar(localStorage.getItem("artifacty_sidebar") === "collapsed"); } catch { /* Retain the current page choice. */ }
31
+ });
@@ -0,0 +1,42 @@
1
+ const themeChoices = new Set(["system", "light", "dark"]);
2
+ let selectedTheme = "system";
3
+ const systemTheme = window.matchMedia?.("(prefers-color-scheme: dark)");
4
+
5
+ function readSavedTheme() {
6
+ try {
7
+ const saved = localStorage.getItem("artifacty_theme");
8
+ return themeChoices.has(saved) ? saved : "system";
9
+ } catch {
10
+ return "system";
11
+ }
12
+ }
13
+
14
+ function applyTheme(theme) {
15
+ selectedTheme = themeChoices.has(theme) ? theme : "system";
16
+ if (selectedTheme === "system") document.documentElement.removeAttribute("data-theme");
17
+ else document.documentElement.setAttribute("data-theme", selectedTheme);
18
+ document.documentElement.setAttribute("data-bs-theme", selectedTheme === "system" ? (systemTheme?.matches ? "dark" : "light") : selectedTheme);
19
+ for (const button of document.querySelectorAll("[data-theme-choice]")) {
20
+ button.setAttribute("aria-pressed", String(button.dataset.themeChoice === selectedTheme));
21
+ }
22
+ }
23
+
24
+ applyTheme(readSavedTheme());
25
+ systemTheme?.addEventListener("change", () => { if (selectedTheme === "system") applyTheme("system"); });
26
+
27
+ document.addEventListener("DOMContentLoaded", () => {
28
+ for (const control of document.querySelectorAll("[data-theme-controls]")) control.hidden = false;
29
+ for (const button of document.querySelectorAll("[data-theme-choice]")) {
30
+ button.addEventListener("click", () => {
31
+ const choice = button.dataset.themeChoice;
32
+ if (!themeChoices.has(choice)) return;
33
+ applyTheme(choice);
34
+ try { localStorage.setItem("artifacty_theme", choice); } catch { /* Keep the current page usable when storage is blocked. */ }
35
+ });
36
+ }
37
+ applyTheme(selectedTheme);
38
+ });
39
+
40
+ window.addEventListener("storage", (event) => {
41
+ if (event.key === "artifacty_theme" || event.key === null) applyTheme(readSavedTheme());
42
+ });
@@ -0,0 +1,2 @@
1
+ /*! Bootstrap v5.3.8 | MIT | https://getbootstrap.com/ | Generated by npm run build:ui */
2
+ :root,[data-bs-theme=light]{--bs-blue: #0d6efd;--bs-indigo: #6610f2;--bs-purple: #6f42c1;--bs-pink: #d63384;--bs-red: #dc3545;--bs-orange: #fd7e14;--bs-yellow: #ffc107;--bs-green: #198754;--bs-teal: #20c997;--bs-cyan: #0dcaf0;--bs-black: #000;--bs-white: #fff;--bs-gray: #6c757d;--bs-gray-dark: #343a40;--bs-gray-100: #f8f9fa;--bs-gray-200: #e9ecef;--bs-gray-300: #dee2e6;--bs-gray-400: #ced4da;--bs-gray-500: #adb5bd;--bs-gray-600: #6c757d;--bs-gray-700: #495057;--bs-gray-800: #343a40;--bs-gray-900: #212529;--bs-primary: #315ca8;--bs-secondary: #58616e;--bs-success: #198754;--bs-info: #0dcaf0;--bs-warning: #ffc107;--bs-danger: #b42332;--bs-light: #f8f9fa;--bs-dark: #212529;--bs-primary-rgb: 49, 92, 168;--bs-secondary-rgb: 88, 97, 110;--bs-success-rgb: 25, 135, 84;--bs-info-rgb: 13, 202, 240;--bs-warning-rgb: 255, 193, 7;--bs-danger-rgb: 180, 35, 50;--bs-light-rgb: 248, 249, 250;--bs-dark-rgb: 33, 37, 41;--bs-primary-text-emphasis: rgb(7.6862745098%, 14.431372549%, 26.3529411765%);--bs-secondary-text-emphasis: rgb(13.8039215686%, 15.2156862745%, 17.2549019608%);--bs-success-text-emphasis: rgb(3.9215686275%, 21.1764705882%, 13.1764705882%);--bs-info-text-emphasis: rgb(2.0392156863%, 31.6862745098%, 37.6470588235%);--bs-warning-text-emphasis: rgb(40%, 30.2745098039%, 1.0980392157%);--bs-danger-text-emphasis: #480e14;--bs-light-text-emphasis: #495057;--bs-dark-text-emphasis: #495057;--bs-primary-bg-subtle: rgb(83.8431372549%, 87.2156862745%, 93.1764705882%);--bs-secondary-bg-subtle: rgb(86.9019607843%, 87.6078431373%, 88.6274509804%);--bs-success-bg-subtle: rgb(81.9607843137%, 90.5882352941%, 86.5882352941%);--bs-info-bg-subtle: rgb(81.0196078431%, 95.8431372549%, 98.8235294118%);--bs-warning-bg-subtle: rgb(100%, 95.137254902%, 80.5490196078%);--bs-danger-bg-subtle: #f0d3d6;--bs-light-bg-subtle: rgb(98.6274509804%, 98.8235294118%, 99.0196078431%);--bs-dark-bg-subtle: #ced4da;--bs-primary-border-subtle: rgb(67.6862745098%, 74.431372549%, 86.3529411765%);--bs-secondary-border-subtle: rgb(73.8039215686%, 75.2156862745%, 77.2549019608%);--bs-success-border-subtle: rgb(63.9215686275%, 81.1764705882%, 73.1764705882%);--bs-info-border-subtle: rgb(62.0392156863%, 91.6862745098%, 97.6470588235%);--bs-warning-border-subtle: rgb(100%, 90.2745098039%, 61.0980392157%);--bs-danger-border-subtle: #e1a7ad;--bs-light-border-subtle: #e9ecef;--bs-dark-border-subtle: #adb5bd;--bs-white-rgb: 255, 255, 255;--bs-black-rgb: 0, 0, 0;--bs-font-sans-serif: ui-sans-serif, system-ui, -apple-system, "Segoe UI", "Apple SD Gothic Neo", "Malgun Gothic", sans-serif;--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family: var(--bs-font-sans-serif);--bs-body-font-size:0.875rem;--bs-body-font-weight: 400;--bs-body-line-height: 1.5;--bs-body-color: #212529;--bs-body-color-rgb: 33, 37, 41;--bs-body-bg: #fff;--bs-body-bg-rgb: 255, 255, 255;--bs-emphasis-color: #000;--bs-emphasis-color-rgb: 0, 0, 0;--bs-secondary-color: rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb: 33, 37, 41;--bs-secondary-bg: #e9ecef;--bs-secondary-bg-rgb: 233, 236, 239;--bs-tertiary-color: rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb: 33, 37, 41;--bs-tertiary-bg: #f8f9fa;--bs-tertiary-bg-rgb: 248, 249, 250;--bs-heading-color: inherit;--bs-link-color: #315ca8;--bs-link-color-rgb: 49, 92, 168;--bs-link-decoration: underline;--bs-link-hover-color: rgb(15.3725490196%, 28.862745098%, 52.7058823529%);--bs-link-hover-color-rgb: 39, 74, 134;--bs-code-color: #d63384;--bs-highlight-color: #212529;--bs-highlight-bg: rgb(100%, 95.137254902%, 80.5490196078%);--bs-border-width: 1px;--bs-border-style: solid;--bs-border-color: #dee2e6;--bs-border-color-translucent: rgba(0, 0, 0, 0.175);--bs-border-radius: 0.375rem;--bs-border-radius-sm: 0.25rem;--bs-border-radius-lg: 0.5rem;--bs-border-radius-xl: 1rem;--bs-border-radius-xxl: 2rem;--bs-border-radius-2xl: var(--bs-border-radius-xxl);--bs-border-radius-pill: 50rem;--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width: 0.25rem;--bs-focus-ring-opacity: 0.25;--bs-focus-ring-color: rgba(49, 92, 168, 0.25);--bs-form-valid-color: #198754;--bs-form-valid-border-color: #198754;--bs-form-invalid-color: #b42332;--bs-form-invalid-border-color: #b42332}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color: #dee2e6;--bs-body-color-rgb: 222, 226, 230;--bs-body-bg: #212529;--bs-body-bg-rgb: 33, 37, 41;--bs-emphasis-color: #fff;--bs-emphasis-color-rgb: 255, 255, 255;--bs-secondary-color: rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb: 222, 226, 230;--bs-secondary-bg: #343a40;--bs-secondary-bg-rgb: 52, 58, 64;--bs-tertiary-color: rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb: 222, 226, 230;--bs-tertiary-bg: rgb(16.6666666667%, 18.6274509804%, 20.5882352941%);--bs-tertiary-bg-rgb: 43, 48, 53;--bs-primary-text-emphasis: rgb(51.5294117647%, 61.6470588235%, 79.5294117647%);--bs-secondary-text-emphasis: rgb(60.7058823529%, 62.8235294118%, 65.8823529412%);--bs-success-text-emphasis: rgb(45.8823529412%, 71.7647058824%, 59.7647058824%);--bs-info-text-emphasis: rgb(43.0588235294%, 87.5294117647%, 96.4705882353%);--bs-warning-text-emphasis: rgb(100%, 85.4117647059%, 41.6470588235%);--bs-danger-text-emphasis: #d27b84;--bs-light-text-emphasis: #f8f9fa;--bs-dark-text-emphasis: #dee2e6;--bs-primary-bg-subtle: rgb(3.8431372549%, 7.2156862745%, 13.1764705882%);--bs-secondary-bg-subtle: rgb(6.9019607843%, 7.6078431373%, 8.6274509804%);--bs-success-bg-subtle: rgb(1.9607843137%, 10.5882352941%, 6.5882352941%);--bs-info-bg-subtle: rgb(1.0196078431%, 15.8431372549%, 18.8235294118%);--bs-warning-bg-subtle: rgb(20%, 15.137254902%, 0.5490196078%);--bs-danger-bg-subtle: #24070a;--bs-light-bg-subtle: #343a40;--bs-dark-bg-subtle: #1a1d20;--bs-primary-border-subtle: rgb(11.5294117647%, 21.6470588235%, 39.5294117647%);--bs-secondary-border-subtle: rgb(20.7058823529%, 22.8235294118%, 25.8823529412%);--bs-success-border-subtle: rgb(5.8823529412%, 31.7647058824%, 19.7647058824%);--bs-info-border-subtle: rgb(3.0588235294%, 47.5294117647%, 56.4705882353%);--bs-warning-border-subtle: rgb(60%, 45.4117647059%, 1.6470588235%);--bs-danger-border-subtle: #6c151e;--bs-light-border-subtle: #495057;--bs-dark-border-subtle: #343a40;--bs-heading-color: inherit;--bs-link-color: rgb(51.5294117647%, 61.6470588235%, 79.5294117647%);--bs-link-hover-color: rgb(61.2235294118%, 69.3176470588%, 83.6235294118%);--bs-link-color-rgb: 131, 157, 203;--bs-link-hover-color-rgb: 156, 177, 213;--bs-code-color: rgb(90.3529411765%, 52%, 71.0588235294%);--bs-highlight-color: #dee2e6;--bs-highlight-bg: rgb(40%, 30.2745098039%, 1.0980392157%);--bs-border-color: #495057;--bs-border-color-translucent: rgba(255, 255, 255, 0.15);--bs-form-valid-color: rgb(45.8823529412%, 71.7647058824%, 59.7647058824%);--bs-form-valid-border-color: rgb(45.8823529412%, 71.7647058824%, 59.7647058824%);--bs-form-invalid-color: rgb(91.7647058824%, 52.4705882353%, 56.2352941176%);--bs-form-invalid-border-color: rgb(91.7647058824%, 52.4705882353%, 56.2352941176%)}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(0.4375rem + var(--bs-border-width));padding-bottom:calc(0.4375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(0.5rem + var(--bs-border-width));padding-bottom:calc(0.5rem + var(--bs-border-width));font-size:1.09375rem}.col-form-label-sm{padding-top:calc(0.25rem + var(--bs-border-width));padding-bottom:calc(0.25rem + var(--bs-border-width));font-size:0.765625rem}.form-text{margin-top:.25rem;font-size:0.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.4375rem .75rem;font-size:0.8125rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);appearance:none;background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:rgb(59.6078431373%,68.0392156863%,82.9411764706%);outline:0;box-shadow:0 0 0 .25rem rgba(49,92,168,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::file-selector-button{padding:.4375rem .75rem;margin:-0.4375rem -0.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.4375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:rgba(0,0,0,0);border:solid rgba(0,0,0,0);border-width:var(--bs-border-width) 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2));padding:.25rem .5rem;font-size:0.765625rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-0.25rem -0.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));padding:.5rem 1rem;font-size:1.09375rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-0.5rem -1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + 0.875rem + calc(var(--bs-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.5em + 0.875rem + calc(var(--bs-border-width) * 2));padding:.4375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0 !important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0 !important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-select{--bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.4375rem 2.25rem .4375rem .75rem;font-size:0.8125rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);appearance:none;background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon, none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-select{transition:none}}.form-select:focus{border-color:rgb(59.6078431373%,68.0392156863%,82.9411764706%);outline:0;box-shadow:0 0 0 .1875rem rgba(49,92,168,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:var(--bs-secondary-bg)}.form-select:-moz-focusring{color:rgba(0,0,0,0);text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:0.765625rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.09375rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.3125rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg: var(--bs-body-bg);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--bs-border-width) solid var(--bs-border-color);print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:rgb(59.6078431373%,68.0392156863%,82.9411764706%);outline:0;box-shadow:0 0 0 .25rem rgba(49,92,168,.25)}.form-check-input:checked{background-color:#315ca8;border-color:#315ca8}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#315ca8;border-color:#315ca8;--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input[disabled]~.form-check-label,.form-check-input:disabled~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgb%2859.6078431373%, 68.0392156863%, 82.9411764706%%29'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.btn-check[disabled]+.btn,.btn-check:disabled+.btn{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.375rem;padding:0;appearance:none;background-color:rgba(0,0,0,0)}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(49,92,168,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(49,92,168,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-0.25rem;appearance:none;background-color:#315ca8;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-range::-webkit-slider-thumb{transition:none}}.form-range::-webkit-slider-thumb:active{background-color:rgb(75.7647058824%,80.8235294118%,89.7647058824%)}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:rgba(0,0,0,0);cursor:pointer;background-color:var(--bs-secondary-bg);border-color:rgba(0,0,0,0);border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;appearance:none;background-color:#315ca8;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-range::-moz-range-thumb{transition:none}}.form-range::-moz-range-thumb:active{background-color:rgb(75.7647058824%,80.8235294118%,89.7647058824%)}.form-range::-moz-range-track{width:100%;height:.5rem;color:rgba(0,0,0,0);cursor:pointer;background-color:var(--bs-secondary-bg);border-color:rgba(0,0,0,0);border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width) * 2));min-height:calc(3.5rem + calc(var(--bs-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;max-width:100%;height:100%;padding:1rem .75rem;overflow:hidden;color:rgba(var(--bs-body-color-rgb), 0.65);text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width) solid rgba(0,0,0,0);transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media(prefers-reduced-motion: reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control::placeholder,.form-floating>.form-control-plaintext::placeholder{color:rgba(0,0,0,0)}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown),.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill,.form-floating>.form-control-plaintext:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem;padding-left:.75rem}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-control-plaintext~label,.form-floating>.form-select~label{transform:scale(0.85) translateY(-0.5rem) translateX(0.15rem)}.form-floating>.form-control:-webkit-autofill~label{transform:scale(0.85) translateY(-0.5rem) translateX(0.15rem)}.form-floating>textarea:focus~label::after,.form-floating>textarea:not(:placeholder-shown)~label::after{position:absolute;inset:1rem .375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>textarea:disabled~label::after{background-color:var(--bs-secondary-bg)}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width) 0}.form-floating>:disabled~label,.form-floating>.form-control:disabled~label{color:#6c757d}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select,.input-group>.form-floating{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus,.input-group>.form-floating:focus-within{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.4375rem .75rem;font-size:0.8125rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius)}.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text,.input-group-lg>.btn{padding:.5rem 1rem;font-size:1.09375rem;border-radius:var(--bs-border-radius-lg)}.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text,.input-group-sm>.btn{padding:.25rem .5rem;font-size:0.765625rem;border-radius:var(--bs-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(-1*var(--bs-border-width));border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:0.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:0.765625rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip,.is-valid~.valid-feedback,.is-valid~.valid-tooltip{display:block}.was-validated .form-control:valid,.form-control.is-valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + 0.875rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(0.375em + 0.21875rem) center;background-size:calc(0.75em + 0.4375rem) calc(0.75em + 0.4375rem)}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .1875rem rgba(var(--bs-success-rgb), 0.2)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + 0.875rem);background-position:top calc(0.375em + 0.21875rem) right calc(0.375em + 0.21875rem)}.was-validated .form-select:valid,.form-select.is-valid{border-color:var(--bs-form-valid-border-color)}.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"],.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"]{--bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(0.75em + 0.4375rem) calc(0.75em + 0.4375rem)}.was-validated .form-select:valid:focus,.form-select.is-valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .1875rem rgba(var(--bs-success-rgb), 0.2)}.was-validated .form-control-color:valid,.form-control-color.is-valid{width:calc(3rem + calc(1.5em + 0.875rem))}.was-validated .form-check-input:valid,.form-check-input.is-valid{border-color:var(--bs-form-valid-border-color)}.was-validated .form-check-input:valid:checked,.form-check-input.is-valid:checked{background-color:var(--bs-form-valid-color)}.was-validated .form-check-input:valid:focus,.form-check-input.is-valid:focus{box-shadow:0 0 0 .1875rem rgba(var(--bs-success-rgb), 0.2)}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):valid,.input-group>.form-control:not(:focus).is-valid,.was-validated .input-group>.form-select:not(:focus):valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.input-group>.form-floating:not(:focus-within).is-valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:0.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:0.765625rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip,.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + 0.875rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23b42332'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23b42332' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(0.375em + 0.21875rem) center;background-size:calc(0.75em + 0.4375rem) calc(0.75em + 0.4375rem)}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .1875rem rgba(var(--bs-danger-rgb), 0.2)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + 0.875rem);background-position:top calc(0.375em + 0.21875rem) right calc(0.375em + 0.21875rem)}.was-validated .form-select:invalid,.form-select.is-invalid{border-color:var(--bs-form-invalid-border-color)}.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"],.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23b42332'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23b42332' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(0.75em + 0.4375rem) calc(0.75em + 0.4375rem)}.was-validated .form-select:invalid:focus,.form-select.is-invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .1875rem rgba(var(--bs-danger-rgb), 0.2)}.was-validated .form-control-color:invalid,.form-control-color.is-invalid{width:calc(3rem + calc(1.5em + 0.875rem))}.was-validated .form-check-input:invalid,.form-check-input.is-invalid{border-color:var(--bs-form-invalid-border-color)}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked{background-color:var(--bs-form-invalid-color)}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid:focus{box-shadow:0 0 0 .1875rem rgba(var(--bs-danger-rgb), 0.2)}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):invalid,.input-group>.form-control:not(:focus).is-invalid,.was-validated .input-group>.form-select:not(:focus):invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.input-group>.form-floating:not(:focus-within).is-invalid{z-index:4}.btn{--bs-btn-padding-x: 0.75rem;--bs-btn-padding-y: 0.4375rem;--bs-btn-font-family: ;--bs-btn-font-size:0.8125rem;--bs-btn-font-weight: 500;--bs-btn-line-height: 1.5;--bs-btn-color: var(--bs-body-color);--bs-btn-bg: transparent;--bs-btn-border-width: var(--bs-border-width);--bs-btn-border-color: transparent;--bs-btn-border-radius: var(--bs-border-radius);--bs-btn-hover-border-color: transparent;--bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity: 0.65;--bs-btn-focus-box-shadow: 0 0 0 0.1875rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;white-space:nowrap;vertical-align:middle;cursor:pointer;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,:not(.btn-check)+.btn:active,.btn:first-child:active,.btn.active,.btn.show{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,:not(.btn-check)+.btn:active:focus-visible,.btn:first-child:active:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn{box-shadow:var(--bs-btn-focus-box-shadow)}.btn:disabled,.btn.disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color: #fff;--bs-btn-bg: #315ca8;--bs-btn-border-color: #315ca8;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: rgb(16.3333333333%, 30.6666666667%, 56%);--bs-btn-hover-border-color: rgb(15.3725490196%, 28.862745098%, 52.7058823529%);--bs-btn-focus-shadow-rgb: 80, 116, 181;--bs-btn-active-color: #fff;--bs-btn-active-bg: rgb(15.3725490196%, 28.862745098%, 52.7058823529%);--bs-btn-active-border-color: rgb(14.4117647059%, 27.0588235294%, 49.4117647059%);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #315ca8;--bs-btn-disabled-border-color: #315ca8}.btn-secondary{--bs-btn-color: #fff;--bs-btn-bg: #58616e;--bs-btn-border-color: #58616e;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: rgb(29.3333333333%, 32.3333333333%, 36.6666666667%);--bs-btn-hover-border-color: rgb(27.6078431373%, 30.431372549%, 34.5098039216%);--bs-btn-focus-shadow-rgb: 113, 121, 132;--bs-btn-active-color: #fff;--bs-btn-active-bg: rgb(27.6078431373%, 30.431372549%, 34.5098039216%);--bs-btn-active-border-color: rgb(25.8823529412%, 28.5294117647%, 32.3529411765%);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #58616e;--bs-btn-disabled-border-color: #58616e}.btn-success{--bs-btn-color: #fff;--bs-btn-bg: #198754;--bs-btn-border-color: #198754;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: rgb(8.3333333333%, 45%, 28%);--bs-btn-hover-border-color: rgb(7.8431372549%, 42.3529411765%, 26.3529411765%);--bs-btn-focus-shadow-rgb: 60, 153, 110;--bs-btn-active-color: #fff;--bs-btn-active-bg: rgb(7.8431372549%, 42.3529411765%, 26.3529411765%);--bs-btn-active-border-color: rgb(7.3529411765%, 39.7058823529%, 24.7058823529%);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #198754;--bs-btn-disabled-border-color: #198754}.btn-info{--bs-btn-color: #000;--bs-btn-bg: #0dcaf0;--bs-btn-border-color: #0dcaf0;--bs-btn-hover-color: #000;--bs-btn-hover-bg: rgb(19.3333333333%, 82.3333333333%, 95%);--bs-btn-hover-border-color: rgb(14.5882352941%, 81.2941176471%, 94.7058823529%);--bs-btn-focus-shadow-rgb: 11, 172, 204;--bs-btn-active-color: #000;--bs-btn-active-bg: rgb(24.0784313725%, 83.3725490196%, 95.2941176471%);--bs-btn-active-border-color: rgb(14.5882352941%, 81.2941176471%, 94.7058823529%);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #0dcaf0;--bs-btn-disabled-border-color: #0dcaf0}.btn-warning{--bs-btn-color: #000;--bs-btn-bg: #ffc107;--bs-btn-border-color: #ffc107;--bs-btn-hover-color: #000;--bs-btn-hover-bg: rgb(100%, 79.3333333333%, 17.3333333333%);--bs-btn-hover-border-color: rgb(100%, 78.1176470588%, 12.4705882353%);--bs-btn-focus-shadow-rgb: 217, 164, 6;--bs-btn-active-color: #000;--bs-btn-active-bg: rgb(100%, 80.5490196078%, 22.1960784314%);--bs-btn-active-border-color: rgb(100%, 78.1176470588%, 12.4705882353%);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #ffc107;--bs-btn-disabled-border-color: #ffc107}.btn-danger{--bs-btn-color: #fff;--bs-btn-bg: #b42332;--bs-btn-border-color: #b42332;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: rgb(60%, 11.6666666667%, 16.6666666667%);--bs-btn-hover-border-color: #901c28;--bs-btn-focus-shadow-rgb: 191, 68, 81;--bs-btn-active-color: #fff;--bs-btn-active-bg: #901c28;--bs-btn-active-border-color: rgb(52.9411764706%, 10.2941176471%, 14.7058823529%);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #b42332;--bs-btn-disabled-border-color: #b42332}.btn-light{--bs-btn-color: #000;--bs-btn-bg: #f8f9fa;--bs-btn-border-color: #f8f9fa;--bs-btn-hover-color: #000;--bs-btn-hover-bg: rgb(82.6666666667%, 83%, 83.3333333333%);--bs-btn-hover-border-color: rgb(77.8039215686%, 78.1176470588%, 78.431372549%);--bs-btn-focus-shadow-rgb: 211, 212, 213;--bs-btn-active-color: #000;--bs-btn-active-bg: rgb(77.8039215686%, 78.1176470588%, 78.431372549%);--bs-btn-active-border-color: rgb(72.9411764706%, 73.2352941176%, 73.5294117647%);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #f8f9fa;--bs-btn-disabled-border-color: #f8f9fa}.btn-dark{--bs-btn-color: #fff;--bs-btn-bg: #212529;--bs-btn-border-color: #212529;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: rgb(26%, 27.3333333333%, 28.6666666667%);--bs-btn-hover-border-color: rgb(21.6470588235%, 23.0588235294%, 24.4705882353%);--bs-btn-focus-shadow-rgb: 66, 70, 73;--bs-btn-active-color: #fff;--bs-btn-active-bg: rgb(30.3529411765%, 31.6078431373%, 32.862745098%);--bs-btn-active-border-color: rgb(21.6470588235%, 23.0588235294%, 24.4705882353%);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #212529;--bs-btn-disabled-border-color: #212529}.btn-outline-primary{--bs-btn-color: #315ca8;--bs-btn-border-color: #315ca8;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #315ca8;--bs-btn-hover-border-color: #315ca8;--bs-btn-focus-shadow-rgb: 49, 92, 168;--bs-btn-active-color: #fff;--bs-btn-active-bg: #315ca8;--bs-btn-active-border-color: #315ca8;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #315ca8;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #315ca8;--bs-gradient: none}.btn-outline-secondary{--bs-btn-color: #58616e;--bs-btn-border-color: #58616e;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #58616e;--bs-btn-hover-border-color: #58616e;--bs-btn-focus-shadow-rgb: 88, 97, 110;--bs-btn-active-color: #fff;--bs-btn-active-bg: #58616e;--bs-btn-active-border-color: #58616e;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #58616e;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #58616e;--bs-gradient: none}.btn-outline-success{--bs-btn-color: #198754;--bs-btn-border-color: #198754;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #198754;--bs-btn-hover-border-color: #198754;--bs-btn-focus-shadow-rgb: 25, 135, 84;--bs-btn-active-color: #fff;--bs-btn-active-bg: #198754;--bs-btn-active-border-color: #198754;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #198754;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #198754;--bs-gradient: none}.btn-outline-info{--bs-btn-color: #0dcaf0;--bs-btn-border-color: #0dcaf0;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #0dcaf0;--bs-btn-hover-border-color: #0dcaf0;--bs-btn-focus-shadow-rgb: 13, 202, 240;--bs-btn-active-color: #000;--bs-btn-active-bg: #0dcaf0;--bs-btn-active-border-color: #0dcaf0;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #0dcaf0;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #0dcaf0;--bs-gradient: none}.btn-outline-warning{--bs-btn-color: #ffc107;--bs-btn-border-color: #ffc107;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #ffc107;--bs-btn-hover-border-color: #ffc107;--bs-btn-focus-shadow-rgb: 255, 193, 7;--bs-btn-active-color: #000;--bs-btn-active-bg: #ffc107;--bs-btn-active-border-color: #ffc107;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #ffc107;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #ffc107;--bs-gradient: none}.btn-outline-danger{--bs-btn-color: #b42332;--bs-btn-border-color: #b42332;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #b42332;--bs-btn-hover-border-color: #b42332;--bs-btn-focus-shadow-rgb: 180, 35, 50;--bs-btn-active-color: #fff;--bs-btn-active-bg: #b42332;--bs-btn-active-border-color: #b42332;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #b42332;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #b42332;--bs-gradient: none}.btn-outline-light{--bs-btn-color: #f8f9fa;--bs-btn-border-color: #f8f9fa;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #f8f9fa;--bs-btn-hover-border-color: #f8f9fa;--bs-btn-focus-shadow-rgb: 248, 249, 250;--bs-btn-active-color: #000;--bs-btn-active-bg: #f8f9fa;--bs-btn-active-border-color: #f8f9fa;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #f8f9fa;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #f8f9fa;--bs-gradient: none}.btn-outline-dark{--bs-btn-color: #212529;--bs-btn-border-color: #212529;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #212529;--bs-btn-hover-border-color: #212529;--bs-btn-focus-shadow-rgb: 33, 37, 41;--bs-btn-active-color: #fff;--bs-btn-active-bg: #212529;--bs-btn-active-border-color: #212529;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #212529;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #212529;--bs-gradient: none}.btn-link{--bs-btn-font-weight: 400;--bs-btn-color: var(--bs-link-color);--bs-btn-bg: transparent;--bs-btn-border-color: transparent;--bs-btn-hover-color: var(--bs-link-hover-color);--bs-btn-hover-border-color: transparent;--bs-btn-active-color: var(--bs-link-hover-color);--bs-btn-active-border-color: transparent;--bs-btn-disabled-color: #6c757d;--bs-btn-disabled-border-color: transparent;--bs-btn-box-shadow: 0 0 0 #000;--bs-btn-focus-shadow-rgb: 80, 116, 181;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-lg,.btn-group-lg>.btn{--bs-btn-padding-y: 0.5rem;--bs-btn-padding-x: 1rem;--bs-btn-font-size:1.09375rem;--bs-btn-border-radius: var(--bs-border-radius-lg)}.btn-sm,.btn-group-sm>.btn{--bs-btn-padding-y: 0.25rem;--bs-btn-padding-x: 0.5rem;--bs-btn-font-size:0.765625rem;--bs-btn-border-radius: var(--bs-border-radius-sm)}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;flex:1 1 auto}.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--bs-border-radius)}.btn-group>:not(.btn-check:first-child)+.btn,.btn-group>.btn-group:not(:first-child){margin-left:calc(-1*var(--bs-border-width))}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn,.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:calc(-1*var(--bs-border-width))}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:nth-child(n+3),.btn-group-vertical>:not(.btn-check)+.btn,.btn-group-vertical>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-top-right-radius:0}:root,[data-bs-theme=dark]{--bs-body-font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Apple SD Gothic Neo, Malgun Gothic, sans-serif;--bs-body-color: var(--text, #20242b);--bs-body-bg: var(--panel, #fff);--bs-secondary-color: var(--muted, #58616e);--bs-border-color: var(--line-2, #b9c1cc);--bs-secondary-bg: var(--panel-2, #eef0f3)}.btn{min-height:36px;display:inline-flex;align-items:center;justify-content:center;gap:.375rem}.btn[hidden]{display:none !important}.form-control,.form-select{min-height:38px;font-family:var(--bs-body-font-family)}.form-control::placeholder{color:var(--muted, #58616e);opacity:.85}.form-control:focus,.form-select:focus{border-color:var(--accent, #315ca8)}.btn-outline-secondary,.btn-ghost{--bs-btn-color: var(--text, #20242b);--bs-btn-border-color: var(--line, #dfe3e8);--bs-btn-hover-color: var(--text, #20242b);--bs-btn-hover-bg: var(--panel-2, #eef0f3);--bs-btn-hover-border-color: var(--line-2, #b9c1cc);--bs-btn-active-color: var(--text, #20242b);--bs-btn-active-bg: var(--panel-2, #eef0f3);--bs-btn-active-border-color: var(--line-2, #b9c1cc);--bs-btn-disabled-color: var(--muted, #58616e);--bs-btn-disabled-border-color: var(--line, #dfe3e8);--bs-btn-focus-shadow-rgb: 49, 92, 168}.btn-ghost{--bs-btn-border-color: transparent}[data-bs-theme=dark] .btn-outline-danger{--bs-btn-color: #ff9ba5;--bs-btn-border-color: #73404a;--bs-btn-hover-bg: #93313e;--bs-btn-hover-border-color: #93313e}.btn:focus-visible{outline:2px solid var(--ring, #315ca8);outline-offset:2px}@media(pointer: coarse){.btn,.form-control,.form-select{min-height:44px}}form[data-auto-apply] [hidden]{display:none !important}
@@ -5,6 +5,17 @@ import { html } from "@codemirror/lang-html";
5
5
  import { json } from "@codemirror/lang-json";
6
6
  import { markdown } from "@codemirror/lang-markdown";
7
7
 
8
+ const viewerMessages = {
9
+ filter: "Filter", filterColumn: "Filter column {column}", ruleFilter: "Filter by rule id",
10
+ rowsShown: "{visible} of {total} rows shown", resultsShown: "{visible} of {total} results shown"
11
+ };
12
+ try {
13
+ Object.assign(viewerMessages, JSON.parse(document.getElementById("artifacty-viewer-i18n")?.textContent || "{}"));
14
+ } catch { /* Keep English fallback when optional configuration is unavailable. */ }
15
+ function viewerText(key, params = {}) {
16
+ return viewerMessages[key].replace(/\{(\w+)\}/g, (match, name) => String(params[name] ?? match));
17
+ }
18
+
8
19
  const viewerTheme = EditorView.theme({
9
20
  "&": {
10
21
  minHeight: "50vh"
@@ -235,8 +246,8 @@ function enhanceCsvViewer(section) {
235
246
  const input = document.createElement("input");
236
247
  input.type = "text";
237
248
  input.className = "csv-filter-input";
238
- input.placeholder = "Filter";
239
- input.setAttribute("aria-label", `Filter column ${index + 1}`);
249
+ input.placeholder = viewerText("filter");
250
+ input.setAttribute("aria-label", viewerText("filterColumn", { column: index + 1 }));
240
251
  input.addEventListener("input", apply);
241
252
  cell.append(input);
242
253
  filterRow.append(cell);
@@ -289,7 +300,7 @@ function enhanceCsvViewer(section) {
289
300
  tbody.append(fragment);
290
301
 
291
302
  status.hidden = false;
292
- status.textContent = `${visible.length} of ${rows.length} rows shown`;
303
+ status.textContent = viewerText("rowsShown", { visible: visible.length, total: rows.length });
293
304
  }
294
305
 
295
306
  apply();
@@ -335,8 +346,8 @@ function enhanceSarifViewer(section) {
335
346
  const ruleInput = document.createElement("input");
336
347
  ruleInput.type = "text";
337
348
  ruleInput.className = "sarif-rule-filter";
338
- ruleInput.placeholder = "Filter by rule id";
339
- ruleInput.setAttribute("aria-label", "Filter by rule id");
349
+ ruleInput.placeholder = viewerText("ruleFilter");
350
+ ruleInput.setAttribute("aria-label", viewerText("ruleFilter"));
340
351
  ruleInput.addEventListener("input", apply);
341
352
 
342
353
  controls.append(chipsWrap, ruleInput);
@@ -437,7 +448,7 @@ function enhanceSarifViewer(section) {
437
448
  tbody.append(fragment);
438
449
 
439
450
  status.hidden = false;
440
- status.textContent = `${visible.length} of ${rows.length} results shown`;
451
+ status.textContent = viewerText("resultsShown", { visible: visible.length, total: rows.length });
441
452
  }
442
453
 
443
454
  apply();
package/src/lib/events.js CHANGED
@@ -81,9 +81,9 @@ export function eventFromAudit(auditRow = {}) {
81
81
  * True when `event` is visible to `access`. Mirrors storage.js's
82
82
  * canReadArtifactMeta: a falsy `access` means an internal/trusted caller
83
83
  * (or single-user mode) and always sees everything; otherwise a
84
- * private-visibility event is only visible to its owner or an admin.
85
- * Events with no recorded visibility (not tied to a private artifact, or
86
- * predating this field) are always visible.
84
+ * public events allow anonymous readers, team events require a user, and
85
+ * private events require their owner or an admin. Legacy events without a
86
+ * visibility field use team semantics.
87
87
  */
88
88
  export function eventVisibleTo(event, access) {
89
89
  if (!event) {
@@ -95,7 +95,7 @@ export function eventVisibleTo(event, access) {
95
95
  if (access.role === "admin") {
96
96
  return true;
97
97
  }
98
- if (event.visibility !== "private") {
98
+ if (event.visibility === "public" || (event.visibility !== "private" && Boolean(access.userId))) {
99
99
  return true;
100
100
  }
101
101
  return Boolean(access.userId) && access.userId === event.ownerUserId;