sqlite-hub 0.12.0 → 0.17.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/README.md +118 -23
- package/bin/sqlite-hub.js +1041 -224
- package/frontend/index.html +1 -0
- package/frontend/js/api.js +32 -2
- package/frontend/js/app.js +989 -13
- package/frontend/js/components/modal.js +644 -15
- package/frontend/js/components/pageHeader.js +14 -16
- package/frontend/js/components/queryEditor.js +9 -1
- package/frontend/js/components/queryHistoryPanel.js +126 -131
- package/frontend/js/components/queryResults.js +79 -17
- package/frontend/js/components/rowEditorPanel.js +204 -10
- package/frontend/js/components/sidebar.js +102 -18
- package/frontend/js/components/tableDesignerEditor.js +69 -11
- package/frontend/js/router.js +8 -0
- package/frontend/js/store.js +868 -9
- package/frontend/js/utils/copyColumnExport.js +117 -0
- package/frontend/js/utils/exportFilenames.js +32 -0
- package/frontend/js/utils/filePathPreview.js +315 -0
- package/frontend/js/utils/format.js +1 -1
- package/frontend/js/utils/markdownDocuments.js +248 -0
- package/frontend/js/utils/rowEditorJson.js +65 -0
- package/frontend/js/utils/sqlFormatter.js +691 -0
- package/frontend/js/utils/tableDesigner.js +178 -6
- package/frontend/js/utils/textCellStats.js +20 -0
- package/frontend/js/utils/timestampPreview.js +264 -0
- package/frontend/js/views/charts.js +3 -1
- package/frontend/js/views/data.js +34 -2
- package/frontend/js/views/documents.js +300 -0
- package/frontend/js/views/editor.js +48 -1
- package/frontend/js/views/settings.js +39 -6
- package/frontend/js/views/structure.js +154 -212
- package/frontend/styles/base.css +6 -0
- package/frontend/styles/components.css +476 -2
- package/frontend/styles/structure-graph.css +0 -3
- package/frontend/styles/tailwind.generated.css +1 -1
- package/frontend/styles/tokens.css +1 -1
- package/frontend/styles/views.css +422 -0
- package/package.json +3 -3
- package/server/routes/documents.js +163 -0
- package/server/routes/settings.js +22 -6
- package/server/server.js +6 -0
- package/server/services/sqlite/introspection.js +10 -0
- package/server/services/sqlite/sqlExecutor.js +29 -0
- package/server/services/sqlite/tableDesigner/changeAnalysis.js +25 -1
- package/server/services/sqlite/tableDesigner/schemaMapping.js +105 -10
- package/server/services/sqlite/tableDesigner/validation.js +60 -2
- package/server/services/sqlite/tableDesignerService.js +1 -1
- package/server/services/storage/appStateStore.js +313 -0
- package/tests/check-constraint-options.test.js +14 -0
- package/tests/cli-args.test.js +100 -0
- package/tests/copy-column-modal.test.js +83 -0
- package/tests/database-documents.test.js +85 -0
- package/tests/export-filenames.test.js +34 -0
- package/tests/file-path-preview.test.js +165 -0
- package/tests/markdown-documents.test.js +79 -0
- package/tests/row-editor-json.test.js +82 -0
- package/tests/row-editor-timestamp-preview.test.js +192 -0
- package/tests/settings-metadata.test.js +16 -0
- package/tests/settings-view.test.js +32 -0
- package/tests/sql-formatter.test.js +173 -0
- package/tests/sql-highlight.test.js +38 -0
- package/tests/table-designer-v2-unique-constraints.test.js +78 -0
- package/tests/text-cell-stats.test.js +38 -0
- package/fill.js +0 -526
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
export const COPY_COLUMN_MODE_VALUES = ["column", "column-with-header", "first-10", "markdown-todo"];
|
|
2
|
+
|
|
3
|
+
export function normalizeCopyColumnMode(mode) {
|
|
4
|
+
const normalizedMode = String(mode ?? "").trim();
|
|
5
|
+
return COPY_COLUMN_MODE_VALUES.includes(normalizedMode) ? normalizedMode : "column";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function isMarkdownTodoCopyColumnMode(mode) {
|
|
9
|
+
return normalizeCopyColumnMode(mode) === "markdown-todo";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getCopyColumnActionLabel(copyMode) {
|
|
13
|
+
const normalizedMode = normalizeCopyColumnMode(copyMode);
|
|
14
|
+
|
|
15
|
+
if (normalizedMode === "column-with-header") {
|
|
16
|
+
return "Copy column with header";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (normalizedMode === "first-10") {
|
|
20
|
+
return "Copy first 10";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (normalizedMode === "markdown-todo") {
|
|
24
|
+
return "Export as Markdown Todo";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return "Copy column";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getCopyColumnExportMetadata(copyMode) {
|
|
31
|
+
return isMarkdownTodoCopyColumnMode(copyMode)
|
|
32
|
+
? {
|
|
33
|
+
extension: "md",
|
|
34
|
+
label: "Markdown",
|
|
35
|
+
mimeType: "text/markdown;charset=utf-8",
|
|
36
|
+
suffix: "markdown-todo",
|
|
37
|
+
}
|
|
38
|
+
: {
|
|
39
|
+
extension: "txt",
|
|
40
|
+
label: "TXT",
|
|
41
|
+
mimeType: "text/plain;charset=utf-8",
|
|
42
|
+
suffix: normalizeCopyColumnMode(copyMode).replaceAll("-", "_"),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function stringifyCopyColumnValue(value) {
|
|
47
|
+
return value === null || value === undefined ? "" : String(value);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function formatDelimitedCopyColumnValue(value, wrapper) {
|
|
51
|
+
const text = stringifyCopyColumnValue(value);
|
|
52
|
+
const normalizedWrapper = String(wrapper ?? "");
|
|
53
|
+
|
|
54
|
+
if (!normalizedWrapper) {
|
|
55
|
+
return text;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return `${normalizedWrapper}${text
|
|
59
|
+
.split(normalizedWrapper)
|
|
60
|
+
.join(`${normalizedWrapper}${normalizedWrapper}`)}${normalizedWrapper}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function formatMarkdownTodoValue(value) {
|
|
64
|
+
return stringifyCopyColumnValue(value).replace(/\r\n|\r|\n/g, " ");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getCopyColumnSourceRows(result, copyMode) {
|
|
68
|
+
const rows = result?.rows ?? [];
|
|
69
|
+
return normalizeCopyColumnMode(copyMode) === "first-10" ? rows.slice(0, 10) : rows;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function buildCopyColumnText({
|
|
73
|
+
result,
|
|
74
|
+
columnName,
|
|
75
|
+
copyMode = "column",
|
|
76
|
+
separator = ",",
|
|
77
|
+
wrapper = "",
|
|
78
|
+
} = {}) {
|
|
79
|
+
const normalizedMode = normalizeCopyColumnMode(copyMode);
|
|
80
|
+
const sourceRows = getCopyColumnSourceRows(result, normalizedMode);
|
|
81
|
+
const values = sourceRows.map((row) => row?.[columnName]);
|
|
82
|
+
|
|
83
|
+
if (normalizedMode === "markdown-todo") {
|
|
84
|
+
return {
|
|
85
|
+
text: values.map((value) => `- [ ] ${formatMarkdownTodoValue(value)}`).join("\n"),
|
|
86
|
+
valueCount: values.length,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const outputValues = normalizedMode === "column-with-header" ? [columnName, ...values] : values;
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
text: outputValues.map((value) => formatDelimitedCopyColumnValue(value, wrapper)).join(separator),
|
|
94
|
+
valueCount: values.length,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function buildCopyColumnPreviewText({
|
|
99
|
+
result,
|
|
100
|
+
columnName,
|
|
101
|
+
copyMode = "column",
|
|
102
|
+
separator = ",",
|
|
103
|
+
wrapper = "",
|
|
104
|
+
maxRows = 4,
|
|
105
|
+
} = {}) {
|
|
106
|
+
const limitedRows = {
|
|
107
|
+
rows: getCopyColumnSourceRows(result, copyMode).slice(0, Math.max(0, Number(maxRows) || 0)),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
return buildCopyColumnText({
|
|
111
|
+
result: limitedRows,
|
|
112
|
+
columnName,
|
|
113
|
+
copyMode: normalizeCopyColumnMode(copyMode) === "first-10" ? "column" : copyMode,
|
|
114
|
+
separator,
|
|
115
|
+
wrapper,
|
|
116
|
+
}).text;
|
|
117
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const TEXT_EXPORT_EXTENSIONS = {
|
|
2
|
+
csv: "csv",
|
|
3
|
+
tsv: "tsv",
|
|
4
|
+
md: "md",
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export function normalizeTextExportFormat(format = "csv") {
|
|
8
|
+
const normalized = String(format ?? "csv").toLowerCase();
|
|
9
|
+
return TEXT_EXPORT_EXTENSIONS[normalized] ? normalized : "csv";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function stripKnownTextExportExtension(value = "") {
|
|
13
|
+
return String(value).replace(/\.(csv|tsv|md)$/i, "");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function sanitizeExportFilenameBase(value, fallback = "export") {
|
|
17
|
+
const sanitized = stripKnownTextExportExtension(value)
|
|
18
|
+
.replace(/[<>:"/\\|?*\u0000-\u001f]/g, " ")
|
|
19
|
+
.replace(/\s+/g, " ")
|
|
20
|
+
.trim()
|
|
21
|
+
.replace(/^[. ]+|[. ]+$/g, "");
|
|
22
|
+
|
|
23
|
+
return (sanitized || fallback).slice(0, 120);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function buildTextExportFilename(value, { format = "csv", fallback = "export" } = {}) {
|
|
27
|
+
const normalizedFormat = normalizeTextExportFormat(format);
|
|
28
|
+
const extension = TEXT_EXPORT_EXTENSIONS[normalizedFormat];
|
|
29
|
+
const base = sanitizeExportFilenameBase(value, fallback);
|
|
30
|
+
|
|
31
|
+
return `${base}.${extension}`;
|
|
32
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { isProtectedKeyColumn } from "./timestampPreview.js";
|
|
2
|
+
|
|
3
|
+
const MAX_FILEPATH_LENGTH = 2048;
|
|
4
|
+
const URL_SCHEME_PATTERN = /^(?:https?:\/\/|ftp:\/\/|mailto:|tel:)/i;
|
|
5
|
+
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
6
|
+
const NUMERIC_PATTERN = /^[+-]?\d+(?:\.\d+)?$/;
|
|
7
|
+
const WINDOWS_ABSOLUTE_PATTERN = /^[A-Za-z]:[\\/]/;
|
|
8
|
+
const HOME_PATH_PATTERN = /^~[\\/]/;
|
|
9
|
+
const UNIX_ABSOLUTE_PATTERN = /^\//;
|
|
10
|
+
const EXPLICIT_RELATIVE_PATTERN = /^(?:\.\.?[\\/])/;
|
|
11
|
+
const FILEPATH_COLUMN_PATTERN = /\b(?:path|filepath|file_path|filename|file|dir|directory|folder|location)\b/i;
|
|
12
|
+
const STRONG_FILENAME_COLUMN_PATTERN = /\b(?:filename|file_name|filepath|file_path|file)\b/i;
|
|
13
|
+
const KNOWN_EXTENSIONS = new Set([
|
|
14
|
+
"sqlite",
|
|
15
|
+
"sqlite3",
|
|
16
|
+
"db",
|
|
17
|
+
"json",
|
|
18
|
+
"csv",
|
|
19
|
+
"tsv",
|
|
20
|
+
"txt",
|
|
21
|
+
"log",
|
|
22
|
+
"png",
|
|
23
|
+
"jpg",
|
|
24
|
+
"jpeg",
|
|
25
|
+
"webp",
|
|
26
|
+
"gif",
|
|
27
|
+
"pdf",
|
|
28
|
+
"md",
|
|
29
|
+
"html",
|
|
30
|
+
"htm",
|
|
31
|
+
"xml",
|
|
32
|
+
"zip",
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
function normalizeValue(value) {
|
|
36
|
+
return typeof value === "string" ? value.trim() : "";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function hasSeparator(value) {
|
|
40
|
+
return /[\\/]/.test(value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function countSeparators(value) {
|
|
44
|
+
return (value.match(/[\\/]/g) ?? []).length;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getSeparator(value) {
|
|
48
|
+
return value.includes("\\") && !value.includes("/") ? "\\" : "/";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function stripTrailingSeparators(value) {
|
|
52
|
+
if (WINDOWS_ABSOLUTE_PATTERN.test(value) && value.length <= 3) {
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (value === "/" || value === "~/" || value === "~\\") {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return value.replace(/[\\/]+$/, "");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isJsonString(value) {
|
|
64
|
+
const text = normalizeValue(value);
|
|
65
|
+
|
|
66
|
+
if (!text || !["{", "["].includes(text[0])) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(text);
|
|
72
|
+
return Boolean(parsed && typeof parsed === "object");
|
|
73
|
+
} catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getSegments(value) {
|
|
79
|
+
return stripTrailingSeparators(value)
|
|
80
|
+
.split(/[\\/]+/)
|
|
81
|
+
.filter(Boolean);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getColumnNameConfidence(columnName) {
|
|
85
|
+
const normalized = String(columnName ?? "").trim();
|
|
86
|
+
|
|
87
|
+
if (!normalized) {
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (FILEPATH_COLUMN_PATTERN.test(normalized.replaceAll("_", " "))) {
|
|
92
|
+
return 0.25;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function hasStrongFilenameColumnName(columnName) {
|
|
99
|
+
return STRONG_FILENAME_COLUMN_PATTERN.test(String(columnName ?? "").replaceAll("_", " "));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function getPathType(value) {
|
|
103
|
+
const text = normalizeValue(value);
|
|
104
|
+
|
|
105
|
+
if (HOME_PATH_PATTERN.test(text)) {
|
|
106
|
+
return "home";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (WINDOWS_ABSOLUTE_PATTERN.test(text)) {
|
|
110
|
+
return "windows";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (UNIX_ABSOLUTE_PATTERN.test(text)) {
|
|
114
|
+
return "unix";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (EXPLICIT_RELATIVE_PATTERN.test(text) || hasSeparator(text)) {
|
|
118
|
+
return "relative";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function extractFileName(value) {
|
|
125
|
+
const text = normalizeValue(value);
|
|
126
|
+
|
|
127
|
+
if (!text || /[\\/]$/.test(text)) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const segments = getSegments(text);
|
|
132
|
+
return segments.at(-1) ?? null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function extractDirectory(value) {
|
|
136
|
+
const text = normalizeValue(value);
|
|
137
|
+
const fileName = extractFileName(text);
|
|
138
|
+
|
|
139
|
+
if (!text || !fileName) {
|
|
140
|
+
return stripTrailingSeparators(text) || null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const separator = getSeparator(text);
|
|
144
|
+
const directory = stripTrailingSeparators(text).slice(0, -fileName.length).replace(/[\\/]+$/, "");
|
|
145
|
+
|
|
146
|
+
if (!directory) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (directory === "~") {
|
|
151
|
+
return `~${separator}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return directory;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function extractExtension(value) {
|
|
158
|
+
const fileName = extractFileName(value);
|
|
159
|
+
|
|
160
|
+
if (!fileName || fileName.startsWith(".") && fileName.indexOf(".", 1) === -1) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const dotIndex = fileName.lastIndexOf(".");
|
|
165
|
+
|
|
166
|
+
if (dotIndex <= 0 || dotIndex === fileName.length - 1) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return fileName.slice(dotIndex + 1).toLowerCase();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function hasKnownExtension(value) {
|
|
174
|
+
const extension = extractExtension(value);
|
|
175
|
+
return Boolean(extension && KNOWN_EXTENSIONS.has(extension));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function looksLikeExcludedValue(value) {
|
|
179
|
+
const text = normalizeValue(value);
|
|
180
|
+
|
|
181
|
+
return (
|
|
182
|
+
!text ||
|
|
183
|
+
text.length > MAX_FILEPATH_LENGTH ||
|
|
184
|
+
URL_SCHEME_PATTERN.test(text) ||
|
|
185
|
+
EMAIL_PATTERN.test(text) ||
|
|
186
|
+
NUMERIC_PATTERN.test(text) ||
|
|
187
|
+
["true", "false"].includes(text.toLowerCase()) ||
|
|
188
|
+
isJsonString(text)
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function isLikelyFilePath(value) {
|
|
193
|
+
const text = normalizeValue(value);
|
|
194
|
+
|
|
195
|
+
if (looksLikeExcludedValue(text)) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return Boolean(getPathType(text));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function getFilePathConfidence(value, columnName) {
|
|
203
|
+
const text = normalizeValue(value);
|
|
204
|
+
const pathType = getPathType(text);
|
|
205
|
+
const extension = extractExtension(text);
|
|
206
|
+
const separatorCount = countSeparators(text);
|
|
207
|
+
let confidence = 0;
|
|
208
|
+
|
|
209
|
+
if (pathType === "unix" || pathType === "windows" || pathType === "home") {
|
|
210
|
+
confidence += 0.55;
|
|
211
|
+
} else if (EXPLICIT_RELATIVE_PATTERN.test(text)) {
|
|
212
|
+
confidence += 0.45;
|
|
213
|
+
} else if (pathType === "relative") {
|
|
214
|
+
confidence += 0.4;
|
|
215
|
+
} else if (extension && hasStrongFilenameColumnName(columnName)) {
|
|
216
|
+
confidence += 0.25;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
confidence += getColumnNameConfidence(columnName);
|
|
220
|
+
|
|
221
|
+
if (extension && KNOWN_EXTENSIONS.has(extension)) {
|
|
222
|
+
confidence += 0.25;
|
|
223
|
+
} else if (extension) {
|
|
224
|
+
confidence += 0.15;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (separatorCount >= 2) {
|
|
228
|
+
confidence += 0.15;
|
|
229
|
+
} else if (separatorCount === 1) {
|
|
230
|
+
confidence += 0.05;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (!extension && separatorCount >= 2) {
|
|
234
|
+
confidence += 0.05;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return Math.min(1, Number(confidence.toFixed(2)));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function detectFilePathValue(value, columnName, tableMeta = {}) {
|
|
241
|
+
if (isProtectedKeyColumn(columnName, tableMeta)) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const rawValue = normalizeValue(value);
|
|
246
|
+
|
|
247
|
+
if (looksLikeExcludedValue(rawValue)) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const pathType = getPathType(rawValue);
|
|
252
|
+
const extension = extractExtension(rawValue);
|
|
253
|
+
|
|
254
|
+
if (!pathType && !(extension && hasStrongFilenameColumnName(columnName))) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (!hasSeparator(rawValue) && !(extension && hasStrongFilenameColumnName(columnName))) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const confidence = getFilePathConfidence(rawValue, columnName);
|
|
263
|
+
|
|
264
|
+
if (confidence < 0.7) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
type: "filepath",
|
|
270
|
+
pathType: pathType ?? "relative",
|
|
271
|
+
rawValue,
|
|
272
|
+
fileName: extractFileName(rawValue),
|
|
273
|
+
directory: extractDirectory(rawValue),
|
|
274
|
+
extension,
|
|
275
|
+
confidence,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function compactPathForDisplay(value, maxLength = 42) {
|
|
280
|
+
const text = normalizeValue(value);
|
|
281
|
+
|
|
282
|
+
if (text.length <= maxLength) {
|
|
283
|
+
return text;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const separator = getSeparator(text);
|
|
287
|
+
const fileName = extractFileName(text);
|
|
288
|
+
const directory = extractDirectory(text);
|
|
289
|
+
const directorySegments = directory ? getSegments(directory) : [];
|
|
290
|
+
const parent = directorySegments.at(-1);
|
|
291
|
+
const compact = [parent, fileName].filter(Boolean).join(separator);
|
|
292
|
+
const candidate = compact ? `...${separator}${compact}` : `...${text.slice(-(maxLength - 3))}`;
|
|
293
|
+
|
|
294
|
+
if (candidate.length <= maxLength) {
|
|
295
|
+
return candidate;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return `...${candidate.slice(-(maxLength - 3))}`;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function getPathTypeLabel(pathType) {
|
|
302
|
+
if (pathType === "unix") {
|
|
303
|
+
return "unix path";
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (pathType === "windows") {
|
|
307
|
+
return "windows path";
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (pathType === "home") {
|
|
311
|
+
return "home path";
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return "relative path";
|
|
315
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const KEYWORD_PATTERN =
|
|
2
|
-
/\b(WITH|SELECT|FROM|WHERE|AND|OR|ORDER|BY|ASC|DESC|AS|COUNT|FILTER|JOIN|LEFT|RIGHT|INNER|OUTER|ON|GROUP|LIMIT|OFFSET|AVG|SUM|MIN|MAX|
|
|
2
|
+
/\b(WITH|SELECT|FROM|WHERE|AND|OR|ORDER|BY|ASC|DESC|AS|CASE|WHEN|THEN|ELSE|END|IS|NULL|CAST|TEXT|INTEGER|REAL|NUMERIC|BLOB|COUNT|FILTER|ROW_NUMBER|RANK|DENSE_RANK|LAG|LEAD|OVER|PARTITION|JOIN|LEFT|RIGHT|INNER|OUTER|ON|GROUP|LIMIT|OFFSET|AVG|SUM|MIN|MAX|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP|PRAGMA|VALUES|INTO|SET|BEGIN|COMMIT|ROLLBACK)\b/gi;
|
|
3
3
|
|
|
4
4
|
const DATE_TIME_FORMATTER = new Intl.DateTimeFormat("en-GB", {
|
|
5
5
|
dateStyle: "medium",
|