artifacty 0.15.0 → 0.16.1
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/README.ko.md +50 -8
- package/README.md +51 -9
- package/THIRD_PARTY_NOTICES.md +53 -0
- package/docs/README.md +6 -0
- package/docs/ko/README.md +6 -0
- package/docs/ko/language-packs.md +40 -0
- package/docs/ko/network-sharing.md +17 -0
- package/docs/ko/smtp.md +27 -0
- package/docs/language-packs.md +38 -0
- package/docs/network-sharing.md +18 -0
- package/docs/roadmap-design.md +31 -2
- package/docs/smtp.md +25 -0
- package/package.json +10 -1
- package/scripts/build-ui.mjs +16 -0
- package/src/cli.js +4 -4
- package/src/client/api-docs.js +32 -0
- package/src/client/auto-apply.js +20 -0
- package/src/client/sidebar.js +31 -0
- package/src/client/theme.js +42 -0
- package/src/client/ui-controls.css +2 -0
- package/src/client/viewer.js +17 -6
- package/src/lib/events.js +4 -4
- package/src/lib/i18n.js +501 -11
- package/src/lib/language-settings.js +114 -0
- package/src/lib/network-policy.js +7 -6
- package/src/lib/openapi.js +93 -74
- package/src/lib/render.js +553 -397
- package/src/lib/smtp-client.js +206 -0
- package/src/lib/smtp-settings.js +109 -0
- package/src/lib/storage.js +71 -17
- package/src/lib/ui-icons.js +32 -0
- package/src/mcp-server.js +3 -3
- package/src/server.js +224 -19
- package/src/styles/ui-controls.scss +63 -0
package/docs/smtp.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# SMTP and password reset
|
|
2
|
+
|
|
3
|
+
English | [한국어](ko/smtp.md)
|
|
4
|
+
|
|
5
|
+
## Configure email
|
|
6
|
+
|
|
7
|
+
As an administrator, open **Administration → Email / SMTP** (`/admin/email`). Enter the SMTP host, port, sender address, and TLS mode. Enable delivery when the configuration is ready. Use your own mail service settings; this guide supplies no provider credentials.
|
|
8
|
+
|
|
9
|
+
TLS or STARTTLS is required. Artifacty validates the server certificate and does not fall back to an insecure connection. If the username is empty, SMTP authentication is disabled and the stored password is cleared. With a username, an empty password field keeps the existing password.
|
|
10
|
+
|
|
11
|
+
## Reset a user's password
|
|
12
|
+
|
|
13
|
+
At `/admin/users`, an administrator can reset a user's password. Artifacty generates a temporary password, requires a password change at the next login, and revokes the user's sessions. Existing API tokens are retained.
|
|
14
|
+
|
|
15
|
+
If SMTP delivery is enabled, Artifacty sends the temporary password to the user's email address. SMTP acceptance does not guarantee inbox delivery. A failed or uncertain send is not retried automatically; the message may already have been accepted. Use the temporary password shown once in the administrator result for manual delivery. Do not repeat the reset solely to retry email.
|
|
16
|
+
|
|
17
|
+
## Protect the encryption key
|
|
18
|
+
|
|
19
|
+
Artifacty encrypts the complete SMTP configuration in SQLite with AES-256-GCM. The separate key is `ARTIFACTY_HOME/secrets/smtp.key`. On POSIX systems, the directory requires mode `700` and the key file mode `600`.
|
|
20
|
+
|
|
21
|
+
Full JSON backups carry the encrypted configuration, but not its key. Upgrade snapshots also exclude the key. A restore into the same home leaves the existing key untouched. When moving or restoring into a new home, back up and transfer the original key separately with its private permissions.
|
|
22
|
+
|
|
23
|
+
If the key is lost, the encrypted configuration cannot be recovered. Restore the original key before changing settings. Do not generate a replacement key to try to decrypt the old configuration. Keep the key backup private and separate from shared backup bundles.
|
|
24
|
+
|
|
25
|
+
See [upgrade snapshots and recovery](upgrade-backups.md) for store restore procedures.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "artifacty",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
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
|
|
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}
|
package/src/client/viewer.js
CHANGED
|
@@ -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 = "
|
|
239
|
-
input.setAttribute("aria-label",
|
|
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 =
|
|
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 = "
|
|
339
|
-
ruleInput.setAttribute("aria-label", "
|
|
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 =
|
|
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
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
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;
|