@matdata/yasgui 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/README.md +128 -144
- package/build/ts/src/ConfigExportImport.d.ts +16 -0
- package/build/ts/src/ConfigExportImport.js +304 -0
- package/build/ts/src/ConfigExportImport.js.map +1 -0
- package/build/ts/src/PersistentConfig.d.ts +4 -0
- package/build/ts/src/PersistentConfig.js +7 -0
- package/build/ts/src/PersistentConfig.js.map +1 -1
- package/build/ts/src/Tab.d.ts +1 -0
- package/build/ts/src/Tab.js +1 -1
- package/build/ts/src/Tab.js.map +1 -1
- package/build/ts/src/TabSettingsModal.d.ts +4 -0
- package/build/ts/src/TabSettingsModal.js +308 -1
- package/build/ts/src/TabSettingsModal.js.map +1 -1
- package/build/yasgui.min.css +1 -1
- package/build/yasgui.min.css.map +3 -3
- package/build/yasgui.min.js +152 -106
- package/build/yasgui.min.js.map +4 -4
- package/package.json +1 -1
- package/src/ConfigExportImport.ts +391 -0
- package/src/PersistentConfig.ts +17 -0
- package/src/Tab.ts +3 -2
- package/src/TabSettingsModal.scss +78 -0
- package/src/TabSettingsModal.ts +369 -3
- package/src/sortablejs.d.ts +36 -0
package/package.json
CHANGED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export/Import Configuration Module
|
|
3
|
+
* Handles serialization and deserialization of YASGUI configuration to/from RDF Turtle format
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { PersistedJson } from "./PersistentConfig";
|
|
7
|
+
|
|
8
|
+
// YASGUI Configuration Ontology
|
|
9
|
+
export const YASGUI_NS = "https://yasgui.matdata.eu/ontology#";
|
|
10
|
+
export const RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
|
|
11
|
+
export const RDFS_NS = "http://www.w3.org/2000/01/rdf-schema#";
|
|
12
|
+
export const XSD_NS = "http://www.w3.org/2001/XMLSchema#";
|
|
13
|
+
export const DCTERMS_NS = "http://purl.org/dc/terms/";
|
|
14
|
+
export const SD_NS = "http://www.w3.org/ns/sparql-service-description#";
|
|
15
|
+
export const SP_NS = "http://spinrdf.org/sp#";
|
|
16
|
+
export const HTTP_NS = "http://www.w3.org/2011/http#";
|
|
17
|
+
export const SCHEMA_NS = "https://schema.org/";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Serialize configuration to Turtle format
|
|
21
|
+
*/
|
|
22
|
+
export function serializeToTurtle(config: PersistedJson): string {
|
|
23
|
+
const lines: string[] = [];
|
|
24
|
+
|
|
25
|
+
// Prefixes
|
|
26
|
+
lines.push(`@prefix yasgui: <${YASGUI_NS}> .`);
|
|
27
|
+
lines.push(`@prefix rdf: <${RDF_NS}> .`);
|
|
28
|
+
lines.push(`@prefix rdfs: <${RDFS_NS}> .`);
|
|
29
|
+
lines.push(`@prefix xsd: <${XSD_NS}> .`);
|
|
30
|
+
lines.push(`@prefix dcterms: <${DCTERMS_NS}> .`);
|
|
31
|
+
lines.push(`@prefix sd: <${SD_NS}> .`);
|
|
32
|
+
lines.push(`@prefix sp: <${SP_NS}> .`);
|
|
33
|
+
lines.push(`@prefix http: <${HTTP_NS}> .`);
|
|
34
|
+
lines.push(`@prefix schema: <${SCHEMA_NS}> .`);
|
|
35
|
+
lines.push("");
|
|
36
|
+
|
|
37
|
+
// Main configuration node
|
|
38
|
+
const now = new Date().toISOString();
|
|
39
|
+
lines.push(`[] a yasgui:Configuration ;`);
|
|
40
|
+
lines.push(` dcterms:created "${now}"^^xsd:dateTime ;`);
|
|
41
|
+
|
|
42
|
+
// Theme
|
|
43
|
+
if (config.theme) {
|
|
44
|
+
lines.push(` yasgui:theme "${config.theme}" ;`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Orientation
|
|
48
|
+
if (config.orientation) {
|
|
49
|
+
lines.push(` yasgui:orientation "${config.orientation}" ;`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Endpoint history
|
|
53
|
+
if (config.endpointHistory && config.endpointHistory.length > 0) {
|
|
54
|
+
lines.push(` yasgui:endpointHistory (`);
|
|
55
|
+
config.endpointHistory.forEach((endpoint) => {
|
|
56
|
+
lines.push(` "${escapeTurtleString(endpoint)}"`);
|
|
57
|
+
});
|
|
58
|
+
lines.push(` ) ;`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Active tab
|
|
62
|
+
if (config.active) {
|
|
63
|
+
lines.push(` yasgui:activeTab "${escapeTurtleString(config.active)}" ;`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Prefixes
|
|
67
|
+
if (config.prefixes) {
|
|
68
|
+
lines.push(` yasgui:prefixesValue """${escapeTurtleString(config.prefixes)}""" ;`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Auto capture enabled
|
|
72
|
+
if (config.autoCaptureEnabled !== undefined) {
|
|
73
|
+
lines.push(` yasgui:autoCaptureEnabled "${config.autoCaptureEnabled}"^^xsd:boolean ;`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Custom endpoint buttons
|
|
77
|
+
if (config.customEndpointButtons && config.customEndpointButtons.length > 0) {
|
|
78
|
+
lines.push(` yasgui:customEndpointButton [`);
|
|
79
|
+
config.customEndpointButtons.forEach((button, index) => {
|
|
80
|
+
const isLast = index === config.customEndpointButtons!.length - 1;
|
|
81
|
+
lines.push(` rdfs:label "${escapeTurtleString(button.label)}" ;`);
|
|
82
|
+
lines.push(` sd:endpoint "${escapeTurtleString(button.endpoint)}"${isLast ? "" : " ;"}`);
|
|
83
|
+
if (!isLast) {
|
|
84
|
+
lines.push(` ] , [`);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
lines.push(` ] ;`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Tabs
|
|
91
|
+
if (config.tabs && config.tabs.length > 0) {
|
|
92
|
+
lines.push(` yasgui:tab [`);
|
|
93
|
+
config.tabs.forEach((tabId, tabIndex) => {
|
|
94
|
+
const tabConfig = config.tabConfig[tabId];
|
|
95
|
+
const isLastTab = tabIndex === config.tabs.length - 1;
|
|
96
|
+
|
|
97
|
+
lines.push(` dcterms:identifier "${escapeTurtleString(tabId)}" ;`);
|
|
98
|
+
lines.push(` rdfs:label "${escapeTurtleString(tabConfig.name)}" ;`);
|
|
99
|
+
|
|
100
|
+
// Orientation (if different from default)
|
|
101
|
+
if (tabConfig.orientation) {
|
|
102
|
+
lines.push(` yasgui:orientation "${escapeTurtleString(tabConfig.orientation)}" ;`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Query
|
|
106
|
+
if (tabConfig.yasqe?.value) {
|
|
107
|
+
lines.push(` sp:text """${escapeTurtleString(tabConfig.yasqe.value)}""" ;`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Editor height
|
|
111
|
+
if (tabConfig.yasqe?.editorHeight) {
|
|
112
|
+
lines.push(` schema:height "${escapeTurtleString(tabConfig.yasqe.editorHeight)}" ;`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Request config
|
|
116
|
+
if (tabConfig.requestConfig) {
|
|
117
|
+
const reqConfig = tabConfig.requestConfig;
|
|
118
|
+
if (reqConfig.endpoint && typeof reqConfig.endpoint === "string") {
|
|
119
|
+
lines.push(` sd:endpoint "${escapeTurtleString(reqConfig.endpoint)}" ;`);
|
|
120
|
+
}
|
|
121
|
+
if (reqConfig.method && typeof reqConfig.method === "string") {
|
|
122
|
+
lines.push(` yasgui:requestMethod "${escapeTurtleString(reqConfig.method)}" ;`);
|
|
123
|
+
}
|
|
124
|
+
if (reqConfig.acceptHeaderSelect && typeof reqConfig.acceptHeaderSelect === "string") {
|
|
125
|
+
lines.push(` yasgui:acceptHeaderSelect [`);
|
|
126
|
+
lines.push(` http:headerName "Accept" ;`);
|
|
127
|
+
lines.push(` http:headerValue "${escapeTurtleString(reqConfig.acceptHeaderSelect)}"`);
|
|
128
|
+
lines.push(` ] ;`);
|
|
129
|
+
}
|
|
130
|
+
if (reqConfig.acceptHeaderGraph && typeof reqConfig.acceptHeaderGraph === "string") {
|
|
131
|
+
lines.push(` yasgui:acceptHeaderGraph [`);
|
|
132
|
+
lines.push(` http:headerName "Accept" ;`);
|
|
133
|
+
lines.push(` http:headerValue "${escapeTurtleString(reqConfig.acceptHeaderGraph)}"`);
|
|
134
|
+
lines.push(` ] ;`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// YASR settings
|
|
139
|
+
if (tabConfig.yasr?.settings) {
|
|
140
|
+
const yasrSettings = tabConfig.yasr.settings;
|
|
141
|
+
if (yasrSettings.selectedPlugin) {
|
|
142
|
+
lines.push(` yasgui:selectedPlugin "${escapeTurtleString(yasrSettings.selectedPlugin)}" ;`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Remove trailing semicolon from last property
|
|
147
|
+
const lastLine = lines[lines.length - 1];
|
|
148
|
+
if (lastLine.endsWith(" ;")) {
|
|
149
|
+
lines[lines.length - 1] = lastLine.slice(0, -2);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!isLastTab) {
|
|
153
|
+
lines.push(` ] , [`);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
lines.push(` ] .`);
|
|
157
|
+
} else {
|
|
158
|
+
// Remove trailing semicolon if no tabs
|
|
159
|
+
const lastLine = lines[lines.length - 1];
|
|
160
|
+
if (lastLine.endsWith(" ;")) {
|
|
161
|
+
lines[lines.length - 1] = lastLine.slice(0, -2) + " .";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return lines.join("\n");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Escape special characters in Turtle strings
|
|
170
|
+
*/
|
|
171
|
+
function escapeTurtleString(str: string): string {
|
|
172
|
+
return str
|
|
173
|
+
.replace(/\\/g, "\\\\")
|
|
174
|
+
.replace(/"/g, '\\"')
|
|
175
|
+
.replace(/\n/g, "\\n")
|
|
176
|
+
.replace(/\r/g, "\\r")
|
|
177
|
+
.replace(/\t/g, "\\t");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Unescape Turtle string
|
|
182
|
+
*/
|
|
183
|
+
function unescapeTurtleString(str: string): string {
|
|
184
|
+
return str
|
|
185
|
+
.replace(/\\n/g, "\n")
|
|
186
|
+
.replace(/\\r/g, "\r")
|
|
187
|
+
.replace(/\\t/g, "\t")
|
|
188
|
+
.replace(/\\"/g, '"')
|
|
189
|
+
.replace(/\\\\/g, "\\");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Parse Turtle format back to configuration
|
|
194
|
+
* This is a simplified parser focused on the structure we generate.
|
|
195
|
+
*
|
|
196
|
+
* Note: This parser is designed to handle the specific Turtle format
|
|
197
|
+
* produced by serializeToTurtle(). For production use with arbitrary
|
|
198
|
+
* Turtle input, consider using a robust RDF library like N3.js.
|
|
199
|
+
* The current implementation uses regex patterns that work well for
|
|
200
|
+
* our serialization output but may not handle all valid Turtle syntax.
|
|
201
|
+
*/
|
|
202
|
+
export function parseFromTurtle(turtle: string): Partial<PersistedJson> {
|
|
203
|
+
const config: Partial<PersistedJson> = {
|
|
204
|
+
endpointHistory: [],
|
|
205
|
+
tabs: [],
|
|
206
|
+
tabConfig: {},
|
|
207
|
+
customEndpointButtons: [],
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
// Extract endpoint history
|
|
212
|
+
const endpointHistoryMatch = turtle.match(/yasgui:endpointHistory\s*\(([\s\S]*?)\)/);
|
|
213
|
+
if (endpointHistoryMatch) {
|
|
214
|
+
const endpoints = endpointHistoryMatch[1].match(/"([^"]*)"/g);
|
|
215
|
+
if (endpoints) {
|
|
216
|
+
config.endpointHistory = endpoints.map((e) => unescapeTurtleString(e.slice(1, -1)));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Extract active tab
|
|
221
|
+
const activeTabMatch = turtle.match(/yasgui:activeTab\s+"([^"]*)"/);
|
|
222
|
+
if (activeTabMatch) {
|
|
223
|
+
config.active = unescapeTurtleString(activeTabMatch[1]);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Extract prefixes
|
|
227
|
+
const prefixesMatch = turtle.match(/yasgui:prefixesValue\s+"""([\s\S]*?)"""/);
|
|
228
|
+
if (prefixesMatch) {
|
|
229
|
+
config.prefixes = unescapeTurtleString(prefixesMatch[1]);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Extract auto capture enabled
|
|
233
|
+
const autoCaptureMatch = turtle.match(/yasgui:autoCaptureEnabled\s+"([^"]*)"/);
|
|
234
|
+
if (autoCaptureMatch) {
|
|
235
|
+
config.autoCaptureEnabled = autoCaptureMatch[1] === "true";
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Extract theme
|
|
239
|
+
const themeMatch = turtle.match(/yasgui:theme\s+"([^"]*)"/);
|
|
240
|
+
if (themeMatch && (themeMatch[1] === "light" || themeMatch[1] === "dark")) {
|
|
241
|
+
config.theme = themeMatch[1] as "light" | "dark";
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Extract orientation
|
|
245
|
+
const orientationMatch = turtle.match(/yasgui:orientation\s+"([^"]*)"/);
|
|
246
|
+
if (orientationMatch && (orientationMatch[1] === "vertical" || orientationMatch[1] === "horizontal")) {
|
|
247
|
+
config.orientation = orientationMatch[1] as "vertical" | "horizontal";
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Extract custom endpoint buttons
|
|
251
|
+
const buttonPattern =
|
|
252
|
+
/yasgui:customEndpointButton\s+\[([\s\S]*?)rdfs:label\s+"([^"]*)"\s*;\s*sd:endpoint\s+"([^"]*)"/g;
|
|
253
|
+
let buttonMatch;
|
|
254
|
+
while ((buttonMatch = buttonPattern.exec(turtle)) !== null) {
|
|
255
|
+
config.customEndpointButtons!.push({
|
|
256
|
+
label: unescapeTurtleString(buttonMatch[2]),
|
|
257
|
+
endpoint: unescapeTurtleString(buttonMatch[3]),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Extract tabs - simplified parsing
|
|
262
|
+
const tabPattern =
|
|
263
|
+
/dcterms:identifier\s+"([^"]*)"\s*;\s*rdfs:label\s+"([^"]*)"\s*;[\s\S]*?(?=dcterms:identifier|yasgui:customEndpointButton|\]\.|\]\s*,\s*\[)/g;
|
|
264
|
+
const tabBlocks = turtle.match(/yasgui:tab\s+\[([\s\S]*)\]\s*\./);
|
|
265
|
+
|
|
266
|
+
if (tabBlocks) {
|
|
267
|
+
const tabsContent = tabBlocks[1];
|
|
268
|
+
const tabIdMatches = [...tabsContent.matchAll(/dcterms:identifier\s+"([^"]*)"/g)];
|
|
269
|
+
const tabNameMatches = [...tabsContent.matchAll(/rdfs:label\s+"([^"]*)"/g)];
|
|
270
|
+
const queryMatches = [...tabsContent.matchAll(/sp:text\s+"""([\s\S]*?)"""/g)];
|
|
271
|
+
const endpointMatches = [...tabsContent.matchAll(/sd:endpoint\s+"([^"]*)"/g)];
|
|
272
|
+
const methodMatches = [...tabsContent.matchAll(/yasgui:requestMethod\s+"([^"]*)"/g)];
|
|
273
|
+
|
|
274
|
+
// Extract tab-level orientation
|
|
275
|
+
const tabOrientationMatches = [...tabsContent.matchAll(/yasgui:orientation\s+"([^"]*)"/g)];
|
|
276
|
+
|
|
277
|
+
tabIdMatches.forEach((match, index) => {
|
|
278
|
+
const tabId = unescapeTurtleString(match[1]);
|
|
279
|
+
const tabName = tabNameMatches[index] ? unescapeTurtleString(tabNameMatches[index][1]) : "Query";
|
|
280
|
+
const query = queryMatches[index] ? unescapeTurtleString(queryMatches[index][1]) : "";
|
|
281
|
+
const endpoint = endpointMatches[index] ? unescapeTurtleString(endpointMatches[index][1]) : "";
|
|
282
|
+
const method = methodMatches[index] ? unescapeTurtleString(methodMatches[index][1]) : "POST";
|
|
283
|
+
const orientation = tabOrientationMatches[index]
|
|
284
|
+
? (unescapeTurtleString(tabOrientationMatches[index][1]) as "vertical" | "horizontal")
|
|
285
|
+
: undefined;
|
|
286
|
+
|
|
287
|
+
config.tabs!.push(tabId);
|
|
288
|
+
config.tabConfig![tabId] = {
|
|
289
|
+
id: tabId,
|
|
290
|
+
name: tabName,
|
|
291
|
+
yasqe: {
|
|
292
|
+
value: query,
|
|
293
|
+
},
|
|
294
|
+
requestConfig: {
|
|
295
|
+
queryArgument: undefined,
|
|
296
|
+
endpoint: endpoint,
|
|
297
|
+
method: method as "GET" | "POST",
|
|
298
|
+
acceptHeaderGraph: "text/turtle",
|
|
299
|
+
acceptHeaderSelect: "application/sparql-results+json",
|
|
300
|
+
acceptHeaderUpdate: "application/sparql-results+json",
|
|
301
|
+
namedGraphs: [],
|
|
302
|
+
defaultGraphs: [],
|
|
303
|
+
args: [],
|
|
304
|
+
headers: {},
|
|
305
|
+
withCredentials: false,
|
|
306
|
+
adjustQueryBeforeRequest: false,
|
|
307
|
+
},
|
|
308
|
+
yasr: {
|
|
309
|
+
settings: {},
|
|
310
|
+
response: undefined,
|
|
311
|
+
},
|
|
312
|
+
orientation: orientation,
|
|
313
|
+
};
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
} catch (error) {
|
|
317
|
+
console.error("Error parsing Turtle configuration:", error);
|
|
318
|
+
throw new Error("Failed to parse configuration. Please check the format.");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return config;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Download configuration as a file
|
|
326
|
+
*/
|
|
327
|
+
export function downloadConfigAsFile(config: PersistedJson, filename: string = "yasgui-config.ttl") {
|
|
328
|
+
const turtle = serializeToTurtle(config);
|
|
329
|
+
const blob = new Blob([turtle], { type: "text/turtle;charset=utf-8" });
|
|
330
|
+
const url = URL.createObjectURL(blob);
|
|
331
|
+
|
|
332
|
+
const link = document.createElement("a");
|
|
333
|
+
link.href = url;
|
|
334
|
+
link.download = filename;
|
|
335
|
+
document.body.appendChild(link);
|
|
336
|
+
link.click();
|
|
337
|
+
document.body.removeChild(link);
|
|
338
|
+
URL.revokeObjectURL(url);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Copy configuration to clipboard
|
|
343
|
+
*/
|
|
344
|
+
export async function copyConfigToClipboard(config: PersistedJson): Promise<void> {
|
|
345
|
+
const turtle = serializeToTurtle(config);
|
|
346
|
+
try {
|
|
347
|
+
await navigator.clipboard.writeText(turtle);
|
|
348
|
+
} catch (error) {
|
|
349
|
+
// Fallback for older browsers
|
|
350
|
+
const textArea = document.createElement("textarea");
|
|
351
|
+
textArea.value = turtle;
|
|
352
|
+
textArea.style.position = "fixed";
|
|
353
|
+
textArea.style.left = "-999999px";
|
|
354
|
+
document.body.appendChild(textArea);
|
|
355
|
+
textArea.select();
|
|
356
|
+
try {
|
|
357
|
+
document.execCommand("copy");
|
|
358
|
+
} finally {
|
|
359
|
+
document.body.removeChild(textArea);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Read configuration from file
|
|
366
|
+
*/
|
|
367
|
+
export function readConfigFromFile(file: File): Promise<string> {
|
|
368
|
+
return new Promise((resolve, reject) => {
|
|
369
|
+
const reader = new FileReader();
|
|
370
|
+
reader.onload = (e) => {
|
|
371
|
+
if (e.target?.result) {
|
|
372
|
+
resolve(e.target.result as string);
|
|
373
|
+
} else {
|
|
374
|
+
reject(new Error("Failed to read file"));
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
reader.onerror = () => reject(new Error("Failed to read file"));
|
|
378
|
+
reader.readAsText(file);
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Read configuration from clipboard
|
|
384
|
+
*/
|
|
385
|
+
export async function readConfigFromClipboard(): Promise<string> {
|
|
386
|
+
try {
|
|
387
|
+
return await navigator.clipboard.readText();
|
|
388
|
+
} catch (error) {
|
|
389
|
+
throw new Error("Failed to read from clipboard. Please paste the content manually.");
|
|
390
|
+
}
|
|
391
|
+
}
|
package/src/PersistentConfig.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface PersistedJson {
|
|
|
11
11
|
prefixes?: string;
|
|
12
12
|
autoCaptureEnabled?: boolean;
|
|
13
13
|
customEndpointButtons?: EndpointButton[];
|
|
14
|
+
theme?: "light" | "dark";
|
|
15
|
+
orientation?: "vertical" | "horizontal";
|
|
14
16
|
}
|
|
15
17
|
function getDefaults(): PersistedJson {
|
|
16
18
|
return {
|
|
@@ -165,4 +167,19 @@ export default class PersistentConfig {
|
|
|
165
167
|
const storage = new YStorage(storageNamespace);
|
|
166
168
|
storage.removeNamespace();
|
|
167
169
|
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get the current persisted configuration (for export purposes)
|
|
173
|
+
*/
|
|
174
|
+
public getPersistedConfig(): PersistedJson {
|
|
175
|
+
return this.persistedJson;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Update the persisted configuration (for import purposes)
|
|
180
|
+
*/
|
|
181
|
+
public updatePersistedConfig(config: Partial<PersistedJson>) {
|
|
182
|
+
Object.assign(this.persistedJson, config);
|
|
183
|
+
this.toStorage();
|
|
184
|
+
}
|
|
168
185
|
}
|
package/src/Tab.ts
CHANGED
|
@@ -35,6 +35,7 @@ export interface PersistedJson {
|
|
|
35
35
|
response: Parser.ResponseSummary | undefined;
|
|
36
36
|
};
|
|
37
37
|
requestConfig: YasguiRequestConfig;
|
|
38
|
+
orientation?: "vertical" | "horizontal";
|
|
38
39
|
}
|
|
39
40
|
export interface Tab {
|
|
40
41
|
on(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
@@ -170,8 +171,8 @@ export class Tab extends EventEmitter {
|
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
|
-
//
|
|
174
|
-
else if (event.
|
|
174
|
+
// F9 - Switch between fullscreen modes
|
|
175
|
+
else if (event.key === "F9") {
|
|
175
176
|
event.preventDefault();
|
|
176
177
|
const yasqeFullscreen = this.yasqe?.getIsFullscreen();
|
|
177
178
|
const yasrFullscreen = this.yasr?.getIsFullscreen();
|
|
@@ -358,3 +358,81 @@
|
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
|
+
|
|
362
|
+
// Import/Export styles
|
|
363
|
+
.exportButtons,
|
|
364
|
+
.importButtons {
|
|
365
|
+
display: flex;
|
|
366
|
+
gap: 10px;
|
|
367
|
+
margin-top: 15px;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.dropZone {
|
|
371
|
+
border: 2px dashed var(--yasgui-border-color, #ccc);
|
|
372
|
+
border-radius: 8px;
|
|
373
|
+
padding: 40px 20px;
|
|
374
|
+
text-align: center;
|
|
375
|
+
background: var(--yasgui-bg-secondary, #f9f9f9);
|
|
376
|
+
margin-top: 15px;
|
|
377
|
+
transition: all 0.3s;
|
|
378
|
+
|
|
379
|
+
&.dragover {
|
|
380
|
+
border-color: var(--yasgui-accent-color, #337ab7);
|
|
381
|
+
background: var(--yasgui-accent-light, #e8f4fc);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.dropZoneContent {
|
|
385
|
+
margin-bottom: 20px;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.dropZoneIcon {
|
|
389
|
+
font-size: 48px;
|
|
390
|
+
margin-bottom: 10px;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.dropZoneText {
|
|
394
|
+
font-size: 16px;
|
|
395
|
+
font-weight: 500;
|
|
396
|
+
color: var(--yasgui-text-primary, #333);
|
|
397
|
+
margin-bottom: 8px;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.dropZoneOr {
|
|
401
|
+
font-size: 14px;
|
|
402
|
+
color: var(--yasgui-text-secondary, #666);
|
|
403
|
+
margin: 15px 0;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.importExportNotification {
|
|
408
|
+
position: fixed;
|
|
409
|
+
top: 20px;
|
|
410
|
+
right: 20px;
|
|
411
|
+
padding: 15px 20px;
|
|
412
|
+
border-radius: 4px;
|
|
413
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
414
|
+
z-index: 10001;
|
|
415
|
+
font-size: 14px;
|
|
416
|
+
animation: slideIn 0.3s ease-out;
|
|
417
|
+
|
|
418
|
+
&.success {
|
|
419
|
+
background: #4caf50;
|
|
420
|
+
color: white;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
&.error {
|
|
424
|
+
background: #f44336;
|
|
425
|
+
color: white;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
@keyframes slideIn {
|
|
430
|
+
from {
|
|
431
|
+
transform: translateX(400px);
|
|
432
|
+
opacity: 0;
|
|
433
|
+
}
|
|
434
|
+
to {
|
|
435
|
+
transform: translateX(0);
|
|
436
|
+
opacity: 1;
|
|
437
|
+
}
|
|
438
|
+
}
|