@striae-org/striae 5.2.0 → 5.3.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/.env.example +36 -33
- package/README.md +5 -46
- package/app/components/actions/case-export/core-export.ts +2 -174
- package/app/components/actions/case-export/download-handlers.ts +83 -750
- package/app/components/actions/case-export/index.ts +6 -30
- package/app/components/actions/case-export/metadata-helpers.ts +0 -78
- package/app/components/actions/case-export/types-constants.ts +0 -43
- package/app/components/actions/case-import/confirmation-import.ts +13 -14
- package/app/components/actions/case-import/zip-processing.ts +92 -12
- package/app/components/actions/generate-pdf.ts +3 -2
- package/app/components/audit/user-audit-viewer.tsx +0 -19
- package/app/components/audit/viewer/audit-viewer-header.tsx +0 -33
- package/app/components/navbar/case-modals/archive-case-modal.tsx +1 -1
- package/app/components/navbar/navbar.tsx +1 -1
- package/app/components/sidebar/case-import/case-import.module.css +35 -0
- package/app/components/sidebar/case-import/components/CasePreviewSection.tsx +59 -3
- package/app/components/sidebar/case-import/components/ConfirmationDialog.tsx +2 -4
- package/app/components/sidebar/case-import/components/ConfirmationPreviewSection.tsx +1 -1
- package/app/components/sidebar/notes/class-details-shared.ts +2 -2
- package/app/components/toast/toast.module.css +36 -0
- package/app/components/toast/toast.tsx +6 -2
- package/app/components/user/manage-profile.tsx +4 -3
- package/app/config-example/config.json +1 -2
- package/app/root.tsx +0 -7
- package/app/routes/_index.tsx +1 -1
- package/app/routes/auth/login.example.tsx +22 -103
- package/app/routes/auth/route.ts +1 -1
- package/app/routes/striae/striae.tsx +53 -59
- package/app/services/firebase/index.ts +0 -3
- package/app/types/export.ts +1 -2
- package/app/utils/auth/index.ts +0 -1
- package/app/utils/data/permissions.ts +3 -2
- package/package.json +10 -17
- package/public/_headers +0 -4
- package/public/_routes.json +0 -1
- package/worker-configuration.d.ts +20 -17
- package/workers/audit-worker/src/audit-worker.example.ts +9 -806
- package/workers/audit-worker/src/config.ts +7 -0
- package/workers/audit-worker/src/crypto/data-at-rest.ts +410 -0
- package/workers/audit-worker/src/handlers/audit-routes.ts +125 -0
- package/workers/audit-worker/src/storage/audit-storage.ts +99 -0
- package/workers/audit-worker/src/types.ts +56 -0
- package/workers/audit-worker/worker-configuration.d.ts +1 -1
- package/workers/audit-worker/wrangler.jsonc.example +1 -1
- package/workers/data-worker/src/config.ts +11 -0
- package/workers/data-worker/src/data-worker.example.ts +21 -942
- package/workers/data-worker/src/handlers/decrypt-export.ts +118 -0
- package/workers/data-worker/src/handlers/signing.ts +174 -0
- package/workers/data-worker/src/handlers/storage-routes.ts +129 -0
- package/workers/data-worker/src/registry/key-registry.ts +368 -0
- package/workers/data-worker/src/types.ts +46 -0
- package/workers/data-worker/worker-configuration.d.ts +1 -1
- package/workers/data-worker/wrangler.jsonc.example +1 -1
- package/workers/image-worker/worker-configuration.d.ts +1 -1
- package/workers/image-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/worker-configuration.d.ts +2 -3
- package/workers/pdf-worker/wrangler.jsonc.example +1 -1
- package/workers/user-worker/src/auth.ts +30 -0
- package/workers/user-worker/src/cleanup/account-deletion.ts +337 -0
- package/workers/user-worker/src/config.ts +4 -0
- package/workers/user-worker/src/encryption-utils.ts +25 -0
- package/workers/user-worker/src/firebase/admin.ts +152 -0
- package/workers/user-worker/src/handlers/user-routes.ts +242 -0
- package/workers/user-worker/src/registry/user-kv.ts +172 -0
- package/workers/user-worker/src/storage/user-records.ts +34 -0
- package/workers/user-worker/src/types.ts +106 -0
- package/workers/user-worker/src/user-worker.example.ts +18 -964
- package/workers/user-worker/worker-configuration.d.ts +4 -2
- package/workers/user-worker/wrangler.jsonc.example +12 -1
- package/wrangler.toml.example +1 -1
- package/app/components/actions/case-export/data-processing.ts +0 -223
- package/app/components/sidebar/case-export/case-export.module.css +0 -418
- package/app/components/sidebar/case-export/case-export.tsx +0 -310
- package/app/types/exceljs-bare.d.ts +0 -9
- package/app/utils/auth/auth.ts +0 -11
- package/public/.well-known/security.txt +0 -6
- package/public/favicon.ico +0 -0
- package/public/icon-256.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/manifest.json +0 -39
- package/public/shortcut.png +0 -0
- package/public/social-image.png +0 -0
- package/public/vendor/exceljs.LICENSE +0 -22
- package/public/vendor/exceljs.bare.min.js +0 -45
- package/scripts/deploy-all.sh +0 -166
- package/scripts/deploy-config/modules/env-utils.sh +0 -322
- package/scripts/deploy-config/modules/keys.sh +0 -404
- package/scripts/deploy-config/modules/prompt.sh +0 -372
- package/scripts/deploy-config/modules/scaffolding.sh +0 -336
- package/scripts/deploy-config/modules/validation.sh +0 -365
- package/scripts/deploy-config.sh +0 -236
- package/scripts/deploy-pages-secrets.sh +0 -231
- package/scripts/deploy-pages.sh +0 -34
- package/scripts/deploy-primershear-emails.sh +0 -167
- package/scripts/deploy-worker-secrets.sh +0 -374
- package/scripts/dev.cjs +0 -23
- package/scripts/install-workers.sh +0 -88
- package/scripts/run-eslint.cjs +0 -43
- package/scripts/update-compatibility-dates.cjs +0 -124
- package/scripts/update-markdown-versions.cjs +0 -43
- package/workers/keys-worker/package.json +0 -18
- package/workers/keys-worker/src/keys.example.ts +0 -67
- package/workers/keys-worker/src/keys.ts +0 -67
- package/workers/keys-worker/worker-configuration.d.ts +0 -7447
- package/workers/keys-worker/wrangler.jsonc.example +0 -15
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
|
-
// Generated by Wrangler by running `wrangler types` (hash:
|
|
3
|
-
// Runtime types generated with workerd@1.20250823.0 2026-03-
|
|
2
|
+
// Generated by Wrangler by running `wrangler types` (hash: e49bcad627d7e39b486bd9867a1c400d)
|
|
3
|
+
// Runtime types generated with workerd@1.20250823.0 2026-03-26 nodejs_compat
|
|
4
4
|
declare namespace Cloudflare {
|
|
5
5
|
interface Env {
|
|
6
6
|
USER_DB: KVNamespace;
|
|
7
|
+
STRIAE_DATA: R2Bucket;
|
|
8
|
+
STRIAE_FILES: R2Bucket;
|
|
7
9
|
}
|
|
8
10
|
}
|
|
9
11
|
interface Env extends Cloudflare.Env {}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "USER_WORKER_NAME",
|
|
3
3
|
"account_id": "ACCOUNT_ID",
|
|
4
4
|
"main": "src/user-worker.ts",
|
|
5
|
-
"compatibility_date": "2026-03-
|
|
5
|
+
"compatibility_date": "2026-03-26",
|
|
6
6
|
"compatibility_flags": [
|
|
7
7
|
"nodejs_compat"
|
|
8
8
|
],
|
|
@@ -18,5 +18,16 @@
|
|
|
18
18
|
}
|
|
19
19
|
],
|
|
20
20
|
|
|
21
|
+
"r2_buckets": [
|
|
22
|
+
{
|
|
23
|
+
"binding": "STRIAE_DATA",
|
|
24
|
+
"bucket_name": "DATA_BUCKET_NAME"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"binding": "STRIAE_FILES",
|
|
28
|
+
"bucket_name": "FILES_BUCKET_NAME"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
|
|
21
32
|
"placement": { "mode": "smart" }
|
|
22
33
|
}
|
package/wrangler.toml.example
CHANGED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import { type CaseExportData } from '~/types';
|
|
2
|
-
import { calculateSHA256Secure } from '~/utils/forensics';
|
|
3
|
-
import { CSV_HEADERS } from './types-constants';
|
|
4
|
-
import { addForensicDataWarning } from './metadata-helpers';
|
|
5
|
-
|
|
6
|
-
export type TabularCell = string | number | boolean | null;
|
|
7
|
-
|
|
8
|
-
const MAX_SPREADSHEET_CELL_LENGTH = 32767;
|
|
9
|
-
const DANGEROUS_FORMULA_PREFIX_PATTERN = /^\s*[=+\-@]/;
|
|
10
|
-
|
|
11
|
-
function stripUnsafeControlChars(input: string): string {
|
|
12
|
-
let output = '';
|
|
13
|
-
|
|
14
|
-
for (let index = 0; index < input.length; index += 1) {
|
|
15
|
-
const code = input.charCodeAt(index);
|
|
16
|
-
const isControlChar = code <= 0x1f || code === 0x7f;
|
|
17
|
-
const isAllowedWhitespace = code === 0x09 || code === 0x0a || code === 0x0d;
|
|
18
|
-
|
|
19
|
-
if (!isControlChar || isAllowedWhitespace) {
|
|
20
|
-
output += input[index];
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return output;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Sanitize cell values before CSV/XLSX export.
|
|
29
|
-
* - Strips non-printable control characters (except tab/newline/carriage return)
|
|
30
|
-
* - Prevents formula injection when files are opened in spreadsheet tools
|
|
31
|
-
* - Caps content length to Excel's per-cell limit
|
|
32
|
-
*/
|
|
33
|
-
export function sanitizeTabularCell(value: unknown): TabularCell {
|
|
34
|
-
if (value === null || value === undefined) {
|
|
35
|
-
return '';
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
39
|
-
return value;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let normalized = stripUnsafeControlChars(String(value));
|
|
43
|
-
|
|
44
|
-
if (normalized.length > MAX_SPREADSHEET_CELL_LENGTH) {
|
|
45
|
-
normalized = normalized.slice(0, MAX_SPREADSHEET_CELL_LENGTH);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (DANGEROUS_FORMULA_PREFIX_PATTERN.test(normalized)) {
|
|
49
|
-
return `'${normalized}`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return normalized;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function sanitizeTabularMatrix(
|
|
56
|
-
rows: Array<Array<string | number | boolean | null | undefined>>
|
|
57
|
-
): TabularCell[][] {
|
|
58
|
-
return rows.map((row) => row.map((cell) => sanitizeTabularCell(cell)));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Generate metadata rows for tabular format
|
|
63
|
-
*/
|
|
64
|
-
export function generateMetadataRows(exportData: CaseExportData): TabularCell[][] {
|
|
65
|
-
return sanitizeTabularMatrix([
|
|
66
|
-
['Case Export Report'],
|
|
67
|
-
[''],
|
|
68
|
-
['Case Number', exportData.metadata.caseNumber],
|
|
69
|
-
['Case Created Date', exportData.metadata.caseCreatedDate],
|
|
70
|
-
['Export Date', exportData.metadata.exportDate],
|
|
71
|
-
['Exported By (Email)', exportData.metadata.exportedBy || 'N/A'],
|
|
72
|
-
['Exported By (UID)', exportData.metadata.exportedByUid || 'N/A'],
|
|
73
|
-
['Exported By (Name)', exportData.metadata.exportedByName || 'N/A'],
|
|
74
|
-
['Exported By (Company)', exportData.metadata.exportedByCompany || 'N/A'],
|
|
75
|
-
['Exported By (Badge/ID)', exportData.metadata.exportedByBadgeId || 'N/A'],
|
|
76
|
-
['Striae Export Schema Version', exportData.metadata.striaeExportSchemaVersion],
|
|
77
|
-
['Total Files', exportData.metadata.totalFiles.toString()],
|
|
78
|
-
[''],
|
|
79
|
-
['Summary'],
|
|
80
|
-
['Files with Annotations', (exportData.summary?.filesWithAnnotations || 0).toString()],
|
|
81
|
-
['Files without Annotations', (exportData.summary?.filesWithoutAnnotations || 0).toString()],
|
|
82
|
-
['Total Box Annotations', (exportData.summary?.totalBoxAnnotations || 0).toString()],
|
|
83
|
-
['Files with Confirmations', (exportData.summary?.filesWithConfirmations || 0).toString()],
|
|
84
|
-
['Files with Confirmations Requested', (exportData.summary?.filesWithConfirmationsRequested || 0).toString()],
|
|
85
|
-
['Last Modified', exportData.summary?.lastModified || 'N/A'],
|
|
86
|
-
['Earliest Annotation Date', exportData.summary?.earliestAnnotationDate || 'N/A'],
|
|
87
|
-
['Latest Annotation Date', exportData.summary?.latestAnnotationDate || 'N/A'],
|
|
88
|
-
[''],
|
|
89
|
-
['File Details']
|
|
90
|
-
]);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Process file data for tabular format (CSV/Excel)
|
|
95
|
-
*/
|
|
96
|
-
export function processFileDataForTabular(fileEntry: CaseExportData['files'][0]): TabularCell[][] {
|
|
97
|
-
// Full file data for the first row (excluding Additional Notes and Last Updated)
|
|
98
|
-
const fullFileData = [
|
|
99
|
-
fileEntry.fileData.id,
|
|
100
|
-
fileEntry.fileData.originalFilename,
|
|
101
|
-
fileEntry.fileData.uploadedAt,
|
|
102
|
-
fileEntry.hasAnnotations ? 'Yes' : 'No',
|
|
103
|
-
fileEntry.annotations?.leftCase || '',
|
|
104
|
-
fileEntry.annotations?.rightCase || '',
|
|
105
|
-
fileEntry.annotations?.leftItem || '',
|
|
106
|
-
fileEntry.annotations?.rightItem || '',
|
|
107
|
-
fileEntry.annotations?.caseFontColor || '',
|
|
108
|
-
fileEntry.annotations?.classType || '',
|
|
109
|
-
fileEntry.annotations?.customClass || '',
|
|
110
|
-
fileEntry.annotations?.classNote || '',
|
|
111
|
-
fileEntry.annotations?.indexType || '',
|
|
112
|
-
fileEntry.annotations?.indexNumber || '',
|
|
113
|
-
fileEntry.annotations?.indexColor || '',
|
|
114
|
-
fileEntry.annotations?.supportLevel || '',
|
|
115
|
-
fileEntry.annotations?.hasSubclass ? 'Yes' : 'No',
|
|
116
|
-
fileEntry.annotations?.includeConfirmation ? 'Yes' : 'No',
|
|
117
|
-
fileEntry.annotations?.confirmationData ? 'Confirmed' : (fileEntry.annotations?.includeConfirmation ? 'Requested' : 'Not Requested'),
|
|
118
|
-
fileEntry.annotations?.confirmationData?.fullName || '',
|
|
119
|
-
fileEntry.annotations?.confirmationData?.badgeId || '',
|
|
120
|
-
fileEntry.annotations?.confirmationData?.confirmedByEmail || '',
|
|
121
|
-
fileEntry.annotations?.confirmationData?.confirmedByCompany || '',
|
|
122
|
-
fileEntry.annotations?.confirmationData?.confirmationId || '',
|
|
123
|
-
fileEntry.annotations?.confirmationData?.timestamp || '',
|
|
124
|
-
fileEntry.annotations?.confirmationData?.confirmedAt || '',
|
|
125
|
-
(fileEntry.annotations?.boxAnnotations?.length || 0).toString()
|
|
126
|
-
];
|
|
127
|
-
|
|
128
|
-
// Additional Notes and Last Updated (at the end)
|
|
129
|
-
const additionalFileData = [
|
|
130
|
-
fileEntry.annotations?.additionalNotes || '',
|
|
131
|
-
fileEntry.annotations?.updatedAt || ''
|
|
132
|
-
];
|
|
133
|
-
|
|
134
|
-
// Calculate array sizes programmatically from CSV_HEADERS
|
|
135
|
-
const fileDataColumnCount = fullFileData.length; // Dynamic count based on actual data
|
|
136
|
-
const additionalDataColumnCount = additionalFileData.length; // Dynamic count based on actual data
|
|
137
|
-
|
|
138
|
-
// Empty row template for subsequent box annotations (file info columns empty)
|
|
139
|
-
const emptyFileData = Array(fileDataColumnCount).fill('');
|
|
140
|
-
const emptyAdditionalData = Array(additionalDataColumnCount).fill('');
|
|
141
|
-
|
|
142
|
-
const rows: Array<Array<string | number | boolean | null | undefined>> = [];
|
|
143
|
-
|
|
144
|
-
// If there are box annotations, create a row for each one
|
|
145
|
-
if (fileEntry.annotations?.boxAnnotations && fileEntry.annotations.boxAnnotations.length > 0) {
|
|
146
|
-
fileEntry.annotations.boxAnnotations.forEach((box, index) => {
|
|
147
|
-
const rowData = index === 0 ? fullFileData : emptyFileData;
|
|
148
|
-
const additionalData = index === 0 ? additionalFileData : emptyAdditionalData;
|
|
149
|
-
|
|
150
|
-
rows.push([
|
|
151
|
-
...rowData,
|
|
152
|
-
box.id,
|
|
153
|
-
box.x.toString(),
|
|
154
|
-
box.y.toString(),
|
|
155
|
-
box.width.toString(),
|
|
156
|
-
box.height.toString(),
|
|
157
|
-
box.color || '',
|
|
158
|
-
box.label || '',
|
|
159
|
-
box.timestamp || '',
|
|
160
|
-
...additionalData
|
|
161
|
-
]);
|
|
162
|
-
});
|
|
163
|
-
} else {
|
|
164
|
-
// If no box annotations, still include one row with empty box data
|
|
165
|
-
rows.push([
|
|
166
|
-
...fullFileData,
|
|
167
|
-
'', // Box ID
|
|
168
|
-
'', // Box X
|
|
169
|
-
'', // Box Y
|
|
170
|
-
'', // Box Width
|
|
171
|
-
'', // Box Height
|
|
172
|
-
'', // Box Color
|
|
173
|
-
'', // Box Label
|
|
174
|
-
'', // Box Timestamp
|
|
175
|
-
...additionalFileData
|
|
176
|
-
]);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return sanitizeTabularMatrix(rows);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Generate CSV content from export data
|
|
184
|
-
*/
|
|
185
|
-
export async function generateCSVContent(exportData: CaseExportData, protectForensicData: boolean = true): Promise<string> {
|
|
186
|
-
// Case metadata section
|
|
187
|
-
const metadataRows = generateMetadataRows(exportData);
|
|
188
|
-
|
|
189
|
-
// File data rows
|
|
190
|
-
const fileRows: TabularCell[][] = [];
|
|
191
|
-
exportData.files.forEach(fileEntry => {
|
|
192
|
-
const processedRows = processFileDataForTabular(fileEntry);
|
|
193
|
-
fileRows.push(...processedRows);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Combine data rows for hash calculation (excluding header comments)
|
|
197
|
-
const dataRows: TabularCell[][] = [
|
|
198
|
-
...metadataRows,
|
|
199
|
-
...sanitizeTabularMatrix([CSV_HEADERS]),
|
|
200
|
-
...fileRows
|
|
201
|
-
];
|
|
202
|
-
|
|
203
|
-
const csvDataContent = dataRows.map(row =>
|
|
204
|
-
row.map(field => `"${String(field).replace(/"/g, '""')}"`).join(',')
|
|
205
|
-
).join('\n');
|
|
206
|
-
|
|
207
|
-
// Calculate hash for integrity verification
|
|
208
|
-
const hash = await calculateSHA256Secure(csvDataContent);
|
|
209
|
-
|
|
210
|
-
// Create final CSV with hash header
|
|
211
|
-
const csvWithHash = [
|
|
212
|
-
`# Striae Case Export - Generated: ${new Date().toISOString()}`,
|
|
213
|
-
`# Case: ${exportData.metadata.caseNumber}`,
|
|
214
|
-
`# Total Files: ${exportData.metadata.totalFiles}`,
|
|
215
|
-
`# SHA-256 Hash: ${hash.toUpperCase()}`,
|
|
216
|
-
'# Verification: Recalculate SHA-256 of data rows only (excluding these comment lines)',
|
|
217
|
-
'',
|
|
218
|
-
csvDataContent
|
|
219
|
-
].join('\n');
|
|
220
|
-
|
|
221
|
-
// Add forensic protection warning if enabled
|
|
222
|
-
return protectForensicData ? addForensicDataWarning(csvWithHash, 'csv') : csvWithHash;
|
|
223
|
-
}
|
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
.overlay {
|
|
2
|
-
position: fixed;
|
|
3
|
-
inset: 0;
|
|
4
|
-
background-color: color-mix(in lab, var(--background) 50%, transparent);
|
|
5
|
-
display: flex;
|
|
6
|
-
justify-content: center;
|
|
7
|
-
align-items: center;
|
|
8
|
-
z-index: var(--zIndex5);
|
|
9
|
-
cursor: default;
|
|
10
|
-
transition: background-color var(--durationM) var(--bezierFastoutSlowin);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
.modal {
|
|
14
|
-
position: relative;
|
|
15
|
-
background: var(--backgroundLight);
|
|
16
|
-
border-radius: var(--spaceXS);
|
|
17
|
-
width: 90%;
|
|
18
|
-
max-width: 480px;
|
|
19
|
-
max-height: 90vh;
|
|
20
|
-
display: flex;
|
|
21
|
-
flex-direction: column;
|
|
22
|
-
overflow: hidden;
|
|
23
|
-
box-shadow: 0 var(--spaceXS) var(--spaceL)
|
|
24
|
-
color-mix(in lab, var(--black) 10%, transparent);
|
|
25
|
-
cursor: default;
|
|
26
|
-
transition: background-color var(--durationM) var(--bezierFastoutSlowin);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.header {
|
|
30
|
-
display: flex;
|
|
31
|
-
font-size: var(--fontSizeBodyL);
|
|
32
|
-
justify-content: space-between;
|
|
33
|
-
align-items: center;
|
|
34
|
-
padding: var(--spaceL);
|
|
35
|
-
border-bottom: 1px solid color-mix(in lab, var(--text) 10%, transparent);
|
|
36
|
-
color: var(--textTitle);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.title {
|
|
40
|
-
margin: 0;
|
|
41
|
-
font-size: var(--fontSizeBodyL);
|
|
42
|
-
font-weight: 600;
|
|
43
|
-
color: var(--textTitle);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.closeButton {
|
|
47
|
-
background: none;
|
|
48
|
-
border: none;
|
|
49
|
-
font-size: var(--fontSizeH5);
|
|
50
|
-
cursor: pointer;
|
|
51
|
-
padding: var(--spaceS);
|
|
52
|
-
color: var(--textLight);
|
|
53
|
-
transition: color var(--durationS) var(--bezierFastoutSlowin);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.closeButton:hover {
|
|
57
|
-
color: var(--text);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.content {
|
|
61
|
-
padding: var(--spaceL);
|
|
62
|
-
flex: 1 1 auto;
|
|
63
|
-
min-height: 0;
|
|
64
|
-
overflow-y: auto;
|
|
65
|
-
overflow-x: hidden;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.formatSelector {
|
|
69
|
-
display: flex;
|
|
70
|
-
align-items: center;
|
|
71
|
-
justify-content: space-between;
|
|
72
|
-
padding: var(--spaceM);
|
|
73
|
-
background: var(--backgroundLight);
|
|
74
|
-
border-radius: var(--spaceXS);
|
|
75
|
-
border: 1px solid color-mix(in lab, var(--text) 10%, transparent);
|
|
76
|
-
margin-top: var(--spaceM);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.formatLabel {
|
|
80
|
-
font-size: var(--fontSizeBodyS);
|
|
81
|
-
font-weight: var(--fontWeightMedium);
|
|
82
|
-
color: var(--textTitle);
|
|
83
|
-
margin: 0;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.formatToggle {
|
|
87
|
-
display: flex;
|
|
88
|
-
background: color-mix(in lab, var(--primary) 10%, transparent);
|
|
89
|
-
border: 1px solid color-mix(in lab, var(--primary) 20%, transparent);
|
|
90
|
-
border-radius: var(--spaceXS);
|
|
91
|
-
overflow: hidden;
|
|
92
|
-
box-shadow: 0 1px 3px color-mix(in lab, var(--primary) 15%, transparent);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.formatOption {
|
|
96
|
-
background: transparent;
|
|
97
|
-
border: none;
|
|
98
|
-
padding: var(--spaceS) var(--spaceM);
|
|
99
|
-
font-size: var(--fontSizeBodyS);
|
|
100
|
-
font-weight: var(--fontWeightMedium);
|
|
101
|
-
color: var(--primary);
|
|
102
|
-
cursor: pointer;
|
|
103
|
-
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
104
|
-
position: relative;
|
|
105
|
-
min-width: 60px;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.formatOption:hover:not(:disabled) {
|
|
109
|
-
background: color-mix(in lab, var(--primary) 15%, transparent);
|
|
110
|
-
color: var(--primary);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
.formatOption:disabled {
|
|
114
|
-
opacity: 0.5;
|
|
115
|
-
cursor: not-allowed;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.formatOptionActive {
|
|
119
|
-
background: var(--primary) !important;
|
|
120
|
-
color: white !important;
|
|
121
|
-
box-shadow: 0 1px 3px color-mix(in lab, var(--primary) 30%, transparent);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
.formatOptionActive:hover:not(:disabled) {
|
|
125
|
-
background: color-mix(in lab, var(--primary) 85%, var(--black)) !important;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.imageOption {
|
|
129
|
-
display: flex;
|
|
130
|
-
align-items: center;
|
|
131
|
-
justify-content: space-between;
|
|
132
|
-
padding: var(--spaceM);
|
|
133
|
-
background: var(--backgroundLight);
|
|
134
|
-
border-radius: var(--spaceXS);
|
|
135
|
-
border: 1px solid color-mix(in lab, var(--text) 10%, transparent);
|
|
136
|
-
margin-top: var(--spaceM);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
.checkboxLabel {
|
|
140
|
-
display: flex;
|
|
141
|
-
align-items: center;
|
|
142
|
-
gap: var(--spaceS);
|
|
143
|
-
cursor: pointer;
|
|
144
|
-
user-select: none;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
.checkbox {
|
|
148
|
-
appearance: none;
|
|
149
|
-
width: 18px;
|
|
150
|
-
height: 18px;
|
|
151
|
-
border: 2px solid color-mix(in lab, var(--primary) 40%, transparent);
|
|
152
|
-
border-radius: 4px;
|
|
153
|
-
background: transparent;
|
|
154
|
-
cursor: pointer;
|
|
155
|
-
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
156
|
-
position: relative;
|
|
157
|
-
flex-shrink: 0;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.checkbox:checked {
|
|
161
|
-
background: var(--primary);
|
|
162
|
-
border-color: var(--primary);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
.checkbox:checked::after {
|
|
166
|
-
content: "";
|
|
167
|
-
position: absolute;
|
|
168
|
-
left: 3px;
|
|
169
|
-
top: 0px;
|
|
170
|
-
width: 6px;
|
|
171
|
-
height: 10px;
|
|
172
|
-
border: solid white;
|
|
173
|
-
border-width: 0 2px 2px 0;
|
|
174
|
-
transform: rotate(45deg);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
.checkbox:hover:not(:disabled) {
|
|
178
|
-
border-color: var(--primary);
|
|
179
|
-
box-shadow: 0 0 0 2px color-mix(in lab, var(--primary) 20%, transparent);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
.checkbox:disabled {
|
|
183
|
-
opacity: 0.5;
|
|
184
|
-
cursor: not-allowed;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
.imageOption:has(.checkbox:disabled) {
|
|
188
|
-
opacity: 0.6;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
.imageOption:has(.checkbox:disabled) .checkboxLabel {
|
|
192
|
-
cursor: not-allowed;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
.checkboxText {
|
|
196
|
-
font-size: var(--fontSizeBodyS);
|
|
197
|
-
font-weight: var(--fontWeightMedium);
|
|
198
|
-
color: var(--textTitle);
|
|
199
|
-
margin: 0;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
.checkboxTooltip {
|
|
203
|
-
display: block;
|
|
204
|
-
font-size: var(--fontSizeBodyXS);
|
|
205
|
-
color: var(--textBody);
|
|
206
|
-
margin: 0;
|
|
207
|
-
opacity: 0.8;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
.fieldGroup {
|
|
211
|
-
display: flex;
|
|
212
|
-
flex-direction: column;
|
|
213
|
-
gap: var(--spaceM);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
.inputGroup {
|
|
217
|
-
display: flex;
|
|
218
|
-
gap: var(--spaceM);
|
|
219
|
-
align-items: center;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
.input {
|
|
223
|
-
flex: 1;
|
|
224
|
-
padding: var(--spaceM);
|
|
225
|
-
border: 1px solid color-mix(in lab, var(--text) 10%, transparent);
|
|
226
|
-
border-radius: var(--spaceXS);
|
|
227
|
-
font-size: var(--fontSizeBodyS);
|
|
228
|
-
background: var(--backgroundLight);
|
|
229
|
-
color: var(--textBody);
|
|
230
|
-
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
.input:focus {
|
|
234
|
-
outline: none;
|
|
235
|
-
border-color: color-mix(in lab, var(--text) 30%, transparent);
|
|
236
|
-
box-shadow: 0 0 0 2px color-mix(in lab, var(--text) 10%, transparent);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
.input:disabled {
|
|
240
|
-
background: color-mix(in lab, var(--background) 95%, transparent);
|
|
241
|
-
color: var(--textLight);
|
|
242
|
-
cursor: not-allowed;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
.exportButton {
|
|
246
|
-
background: var(--success);
|
|
247
|
-
color: white;
|
|
248
|
-
border: none;
|
|
249
|
-
border-radius: var(--spaceXS);
|
|
250
|
-
padding: var(--spaceM) var(--spaceL);
|
|
251
|
-
font-size: var(--fontSizeBodyS);
|
|
252
|
-
font-weight: var(--fontWeightMedium);
|
|
253
|
-
cursor: pointer;
|
|
254
|
-
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
255
|
-
white-space: nowrap;
|
|
256
|
-
min-width: 140px;
|
|
257
|
-
box-shadow: 0 1px 3px color-mix(in lab, var(--success) 30%, transparent);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
.exportButton:hover:not(:disabled) {
|
|
261
|
-
background: color-mix(in lab, var(--success) 85%, var(--black));
|
|
262
|
-
box-shadow: 0 2px 6px color-mix(in lab, var(--success) 40%, transparent);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
.exportButton:disabled {
|
|
266
|
-
background: color-mix(in lab, var(--background) 95%, transparent);
|
|
267
|
-
color: var(--textLight);
|
|
268
|
-
cursor: not-allowed;
|
|
269
|
-
box-shadow: none;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
.confirmationExportButton {
|
|
273
|
-
background: var(--accent);
|
|
274
|
-
color: white;
|
|
275
|
-
border: none;
|
|
276
|
-
border-radius: var(--spaceXS);
|
|
277
|
-
padding: var(--spaceM) var(--spaceL);
|
|
278
|
-
font-size: var(--fontSizeBodyS);
|
|
279
|
-
font-weight: var(--fontWeightMedium);
|
|
280
|
-
cursor: pointer;
|
|
281
|
-
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
282
|
-
white-space: nowrap;
|
|
283
|
-
min-width: 140px;
|
|
284
|
-
box-shadow: 0 1px 3px color-mix(in lab, var(--accent) 30%, transparent);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
.confirmationExportButton:hover:not(:disabled) {
|
|
288
|
-
background: color-mix(in lab, var(--accent) 85%, var(--black));
|
|
289
|
-
box-shadow: 0 2px 6px color-mix(in lab, var(--accent) 40%, transparent);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
.confirmationExportButton:disabled {
|
|
293
|
-
background: color-mix(in lab, var(--background) 95%, transparent);
|
|
294
|
-
color: var(--textLight);
|
|
295
|
-
cursor: not-allowed;
|
|
296
|
-
box-shadow: none;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
.publicKeySection {
|
|
300
|
-
margin-top: var(--spaceS);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
.publicKeyButton {
|
|
304
|
-
width: 100%;
|
|
305
|
-
background: transparent;
|
|
306
|
-
color: var(--primary);
|
|
307
|
-
border: 1px solid color-mix(in lab, var(--primary) 35%, transparent);
|
|
308
|
-
border-radius: var(--spaceXS);
|
|
309
|
-
padding: var(--spaceS) var(--spaceM);
|
|
310
|
-
font-size: var(--fontSizeBodyS);
|
|
311
|
-
font-weight: var(--fontWeightMedium);
|
|
312
|
-
cursor: pointer;
|
|
313
|
-
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
.publicKeyButton:hover {
|
|
317
|
-
background: color-mix(in lab, var(--primary) 10%, transparent);
|
|
318
|
-
border-color: color-mix(in lab, var(--primary) 55%, transparent);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
.divider {
|
|
322
|
-
margin: var(--spaceL) 0;
|
|
323
|
-
text-align: center;
|
|
324
|
-
position: relative;
|
|
325
|
-
color: var(--textLight);
|
|
326
|
-
font-size: var(--fontSizeBodyS);
|
|
327
|
-
font-weight: var(--fontWeightMedium);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
.divider::before {
|
|
331
|
-
content: "";
|
|
332
|
-
position: absolute;
|
|
333
|
-
top: 50%;
|
|
334
|
-
left: 0;
|
|
335
|
-
right: 0;
|
|
336
|
-
height: 1px;
|
|
337
|
-
background: color-mix(in lab, var(--text) 10%, transparent);
|
|
338
|
-
z-index: 1;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
.divider span {
|
|
342
|
-
background: var(--backgroundLight);
|
|
343
|
-
padding: 0 var(--spaceM);
|
|
344
|
-
position: relative;
|
|
345
|
-
z-index: 2;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
.exportAllSection {
|
|
349
|
-
text-align: center;
|
|
350
|
-
margin-bottom: var(--spaceM);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
.exportAllButton {
|
|
354
|
-
background: var(--primary);
|
|
355
|
-
color: white;
|
|
356
|
-
border: none;
|
|
357
|
-
border-radius: var(--spaceXS);
|
|
358
|
-
padding: var(--spaceM) var(--spaceL);
|
|
359
|
-
font-size: var(--fontSizeBodyS);
|
|
360
|
-
font-weight: var(--fontWeightMedium);
|
|
361
|
-
cursor: pointer;
|
|
362
|
-
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
363
|
-
width: 100%;
|
|
364
|
-
box-shadow: 0 1px 3px color-mix(in lab, var(--primary) 30%, transparent);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
.exportAllButton:hover:not(:disabled) {
|
|
368
|
-
background: color-mix(in lab, var(--primary) 85%, var(--black));
|
|
369
|
-
box-shadow: 0 2px 6px color-mix(in lab, var(--primary) 40%, transparent);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
.exportAllButton:disabled {
|
|
373
|
-
background: color-mix(in lab, var(--background) 95%, transparent);
|
|
374
|
-
color: var(--textLight);
|
|
375
|
-
cursor: not-allowed;
|
|
376
|
-
box-shadow: none;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
.progressSection {
|
|
380
|
-
margin: var(--spaceM) 0;
|
|
381
|
-
padding: var(--spaceM);
|
|
382
|
-
background: var(--backgroundLight);
|
|
383
|
-
border-radius: var(--spaceXS);
|
|
384
|
-
border: 1px solid color-mix(in lab, var(--text) 10%, transparent);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
.progressText {
|
|
388
|
-
font-size: var(--fontSizeBodyS);
|
|
389
|
-
color: var(--textBody);
|
|
390
|
-
margin-bottom: var(--spaceS);
|
|
391
|
-
font-weight: var(--fontWeightMedium);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
.progressBar {
|
|
395
|
-
width: 100%;
|
|
396
|
-
height: 8px;
|
|
397
|
-
background: color-mix(in lab, var(--text) 10%, transparent);
|
|
398
|
-
border-radius: var(--spaceXS);
|
|
399
|
-
overflow: hidden;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
.progressFill {
|
|
403
|
-
height: 100%;
|
|
404
|
-
background: color-mix(in lab, var(--text) 60%, transparent);
|
|
405
|
-
border-radius: var(--spaceXS);
|
|
406
|
-
transition: width var(--durationS) var(--bezierFastoutSlowin);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
.error {
|
|
410
|
-
margin-top: var(--spaceM);
|
|
411
|
-
padding: var(--spaceM);
|
|
412
|
-
background: color-mix(in lab, var(--error) 10%, transparent);
|
|
413
|
-
border: 1px solid color-mix(in lab, var(--error) 20%, transparent);
|
|
414
|
-
border-radius: var(--spaceXS);
|
|
415
|
-
color: var(--error);
|
|
416
|
-
font-size: var(--fontSizeBodyS);
|
|
417
|
-
font-weight: var(--fontWeightMedium);
|
|
418
|
-
}
|