@scalar/api-client 2.39.1 → 2.39.3
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/CHANGELOG.md +14 -0
- package/dist/hooks/useResponseBody.js +2 -2
- package/dist/hooks/useResponseBody.js.map +1 -1
- package/dist/libs/send-request/decode-buffer.js +2 -2
- package/dist/libs/send-request/decode-buffer.js.map +1 -1
- package/dist/style.css +20 -21
- package/dist/v2/blocks/operation-block/helpers/decode-buffer.js +2 -2
- package/dist/v2/blocks/operation-block/helpers/decode-buffer.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestTableTooltip.vue.d.ts.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestTableTooltip.vue.js +1 -3
- package/dist/v2/blocks/request-block/components/RequestTableTooltip.vue.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestTableTooltip.vue.script.js +2 -3
- package/dist/v2/blocks/request-block/components/RequestTableTooltip.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/helpers/get-form-body-rows.d.ts.map +1 -1
- package/dist/v2/blocks/request-block/helpers/get-form-body-rows.js +6 -1
- package/dist/v2/blocks/request-block/helpers/get-form-body-rows.js.map +1 -1
- package/dist/v2/blocks/response-block/helpers/process-response-body.js +2 -2
- package/dist/v2/blocks/response-block/helpers/process-response-body.js.map +1 -1
- package/dist/v2/constants.js +1 -1
- package/dist/v2/features/collection/components/Editor/Editor.vue.d.ts.map +1 -1
- package/dist/v2/features/collection/components/Editor/Editor.vue.js +1 -1
- package/dist/v2/features/collection/components/Editor/Editor.vue.js.map +1 -1
- package/dist/v2/features/collection/components/Editor/Editor.vue.script.js +2 -2
- package/dist/v2/features/collection/components/Editor/Editor.vue.script.js.map +1 -1
- package/dist/v2/features/editor/helpers/theme/apply-scalar-theme.js.map +1 -1
- package/dist/v2/features/editor/helpers/theme/load-css-variables.d.ts +25 -3
- package/dist/v2/features/editor/helpers/theme/load-css-variables.d.ts.map +1 -1
- package/dist/v2/features/editor/helpers/theme/load-css-variables.js +92 -16
- package/dist/v2/features/editor/helpers/theme/load-css-variables.js.map +1 -1
- package/dist/v2/posthog.d.ts.map +1 -1
- package/dist/v2/posthog.js +1 -0
- package/dist/v2/posthog.js.map +1 -1
- package/dist/views/Request/ResponseSection/ResponseEmpty.vue.script.js +1 -1
- package/package.json +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @scalar/api-client
|
|
2
2
|
|
|
3
|
+
## 2.39.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#8610](https://github.com/scalar/scalar/pull/8610): fix(api-client): improve editor background color handling
|
|
8
|
+
|
|
9
|
+
## 2.39.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#8599](https://github.com/scalar/scalar/pull/8599): fix(api-client): truncate long table tooltip content
|
|
14
|
+
- [#8590](https://github.com/scalar/scalar/pull/8590): Group analytic events by api-client product
|
|
15
|
+
- [#8601](https://github.com/scalar/scalar/pull/8601): fix: stringify nested form body example values for table rows
|
|
16
|
+
|
|
3
17
|
## 2.39.1
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { extractFilename } from "../libs/extractAttachmentFilename.js";
|
|
2
2
|
import { computed, isRef } from "vue";
|
|
3
|
-
import
|
|
3
|
+
import MimeTypeParser from "whatwg-mimetype";
|
|
4
4
|
//#region src/hooks/useResponseBody.ts
|
|
5
5
|
/**
|
|
6
6
|
* Processes the response body of an HTTP request.
|
|
@@ -11,7 +11,7 @@ function useResponseBody(props) {
|
|
|
11
11
|
const dataRef = computed(() => isRef(props.data) ? props.data.value : props.data);
|
|
12
12
|
const headersRef = computed(() => isRef(props.headers) ? props.headers.value : props.headers);
|
|
13
13
|
const mimeType = computed(() => {
|
|
14
|
-
return new
|
|
14
|
+
return new MimeTypeParser(headersRef.value.find((header) => header.name.toLowerCase() === "content-type")?.value ?? "");
|
|
15
15
|
});
|
|
16
16
|
return {
|
|
17
17
|
mimeType,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useResponseBody.js","names":[],"sources":["../../src/hooks/useResponseBody.ts"],"sourcesContent":["import type { Ref } from 'vue'\nimport { computed, isRef } from 'vue'\nimport MimeType from 'whatwg-mimetype'\n\nimport { extractFilename } from '@/libs/extractAttachmentFilename'\n\n/**\n * Processes the response body of an HTTP request.\n * Extracts MIME type, attachment filename, and generates a data URL.\n */\nexport function useResponseBody(props: {\n data: Ref<unknown>\n headers: Ref<{ name: string; value: string; required: boolean }[]>\n}) {\n const isBlob = (b: any): b is Blob => b instanceof Blob\n\n // Handle both Ref and direct values\n const dataRef = computed(() => (isRef(props.data) ? props.data.value : props.data))\n const headersRef = computed(() => (isRef(props.headers) ? props.headers.value : props.headers))\n\n const mimeType = computed(() => {\n const contentType = headersRef.value.find((header) => header.name.toLowerCase() === 'content-type')?.value ?? ''\n return new MimeType(contentType)\n })\n\n const attachmentFilename = computed(() => {\n const value = headersRef.value.find((header) => header.name.toLowerCase() === 'content-disposition')?.value ?? ''\n return extractFilename(value)\n })\n\n const dataUrl = computed<string>(() => {\n if (isBlob(dataRef.value)) {\n return URL.createObjectURL(dataRef.value)\n }\n if (typeof dataRef.value === 'string') {\n return URL.createObjectURL(new Blob([dataRef.value], { type: mimeType.value.toString() }))\n }\n if (dataRef.value instanceof Object && Object.keys(dataRef.value).length) {\n return URL.createObjectURL(\n new Blob([JSON.stringify(dataRef.value)], {\n type: mimeType.value.toString(),\n }),\n )\n }\n return ''\n })\n\n return { mimeType, attachmentFilename, dataUrl }\n}\n"],"mappings":";;;;;;;;AAUA,SAAgB,gBAAgB,OAG7B;CACD,MAAM,UAAU,MAAsB,aAAa;CAGnD,MAAM,UAAU,eAAgB,MAAM,MAAM,KAAK,GAAG,MAAM,KAAK,QAAQ,MAAM,KAAM;CACnF,MAAM,aAAa,eAAgB,MAAM,MAAM,QAAQ,GAAG,MAAM,QAAQ,QAAQ,MAAM,QAAS;CAE/F,MAAM,WAAW,eAAe;AAE9B,SAAO,IAAI,
|
|
1
|
+
{"version":3,"file":"useResponseBody.js","names":[],"sources":["../../src/hooks/useResponseBody.ts"],"sourcesContent":["import type { Ref } from 'vue'\nimport { computed, isRef } from 'vue'\nimport MimeType from 'whatwg-mimetype'\n\nimport { extractFilename } from '@/libs/extractAttachmentFilename'\n\n/**\n * Processes the response body of an HTTP request.\n * Extracts MIME type, attachment filename, and generates a data URL.\n */\nexport function useResponseBody(props: {\n data: Ref<unknown>\n headers: Ref<{ name: string; value: string; required: boolean }[]>\n}) {\n const isBlob = (b: any): b is Blob => b instanceof Blob\n\n // Handle both Ref and direct values\n const dataRef = computed(() => (isRef(props.data) ? props.data.value : props.data))\n const headersRef = computed(() => (isRef(props.headers) ? props.headers.value : props.headers))\n\n const mimeType = computed(() => {\n const contentType = headersRef.value.find((header) => header.name.toLowerCase() === 'content-type')?.value ?? ''\n return new MimeType(contentType)\n })\n\n const attachmentFilename = computed(() => {\n const value = headersRef.value.find((header) => header.name.toLowerCase() === 'content-disposition')?.value ?? ''\n return extractFilename(value)\n })\n\n const dataUrl = computed<string>(() => {\n if (isBlob(dataRef.value)) {\n return URL.createObjectURL(dataRef.value)\n }\n if (typeof dataRef.value === 'string') {\n return URL.createObjectURL(new Blob([dataRef.value], { type: mimeType.value.toString() }))\n }\n if (dataRef.value instanceof Object && Object.keys(dataRef.value).length) {\n return URL.createObjectURL(\n new Blob([JSON.stringify(dataRef.value)], {\n type: mimeType.value.toString(),\n }),\n )\n }\n return ''\n })\n\n return { mimeType, attachmentFilename, dataUrl }\n}\n"],"mappings":";;;;;;;;AAUA,SAAgB,gBAAgB,OAG7B;CACD,MAAM,UAAU,MAAsB,aAAa;CAGnD,MAAM,UAAU,eAAgB,MAAM,MAAM,KAAK,GAAG,MAAM,KAAK,QAAQ,MAAM,KAAM;CACnF,MAAM,aAAa,eAAgB,MAAM,MAAM,QAAQ,GAAG,MAAM,QAAQ,QAAQ,MAAM,QAAS;CAE/F,MAAM,WAAW,eAAe;AAE9B,SAAO,IAAI,eADS,WAAW,MAAM,MAAM,WAAW,OAAO,KAAK,aAAa,KAAK,eAAe,EAAE,SAAS,GAC9E;GAChC;AAwBF,QAAO;EAAE;EAAU,oBAtBQ,eAAe;AAExC,UAAO,gBADO,WAAW,MAAM,MAAM,WAAW,OAAO,KAAK,aAAa,KAAK,sBAAsB,EAAE,SAAS,GAClF;IAC7B;EAmBqC,SAjBvB,eAAuB;AACrC,OAAI,OAAO,QAAQ,MAAM,CACvB,QAAO,IAAI,gBAAgB,QAAQ,MAAM;AAE3C,OAAI,OAAO,QAAQ,UAAU,SAC3B,QAAO,IAAI,gBAAgB,IAAI,KAAK,CAAC,QAAQ,MAAM,EAAE,EAAE,MAAM,SAAS,MAAM,UAAU,EAAE,CAAC,CAAC;AAE5F,OAAI,QAAQ,iBAAiB,UAAU,OAAO,KAAK,QAAQ,MAAM,CAAC,OAChE,QAAO,IAAI,gBACT,IAAI,KAAK,CAAC,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,EACxC,MAAM,SAAS,MAAM,UAAU,EAChC,CAAC,CACH;AAEH,UAAO;IACP;EAE8C"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { isTextMediaType } from "../../views/Request/consts/mediaTypes.js";
|
|
2
|
-
import
|
|
2
|
+
import MimeTypeParser from "whatwg-mimetype";
|
|
3
3
|
//#region src/libs/send-request/decode-buffer.ts
|
|
4
4
|
/** Decode the buffer according to its content-type */
|
|
5
5
|
function decodeBuffer(buffer, contentType) {
|
|
6
|
-
const mimeType = new
|
|
6
|
+
const mimeType = new MimeTypeParser(contentType);
|
|
7
7
|
if (isTextMediaType(mimeType.essence)) return new TextDecoder(mimeType.parameters.get("charset")).decode(buffer);
|
|
8
8
|
return new Blob([buffer], { type: mimeType.essence });
|
|
9
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decode-buffer.js","names":[],"sources":["../../../src/libs/send-request/decode-buffer.ts"],"sourcesContent":["import MimeTypeParser from 'whatwg-mimetype'\n\nimport { isTextMediaType } from '@/views/Request/consts'\n\n// TODO: This should return `unknown` to acknowledge we don't know type, shouldn't it?\n/** Decode the buffer according to its content-type */\nexport function decodeBuffer(buffer: ArrayBuffer, contentType: string) {\n const mimeType = new MimeTypeParser(contentType)\n\n if (isTextMediaType(mimeType.essence)) {\n const decoder = new TextDecoder(mimeType.parameters.get('charset'))\n const string = decoder.decode(buffer)\n\n // Text\n return string\n }\n\n // Binary\n return new Blob([buffer], { type: mimeType.essence })\n}\n"],"mappings":";;;;AAMA,SAAgB,aAAa,QAAqB,aAAqB;CACrE,MAAM,WAAW,IAAI,
|
|
1
|
+
{"version":3,"file":"decode-buffer.js","names":[],"sources":["../../../src/libs/send-request/decode-buffer.ts"],"sourcesContent":["import MimeTypeParser from 'whatwg-mimetype'\n\nimport { isTextMediaType } from '@/views/Request/consts'\n\n// TODO: This should return `unknown` to acknowledge we don't know type, shouldn't it?\n/** Decode the buffer according to its content-type */\nexport function decodeBuffer(buffer: ArrayBuffer, contentType: string) {\n const mimeType = new MimeTypeParser(contentType)\n\n if (isTextMediaType(mimeType.essence)) {\n const decoder = new TextDecoder(mimeType.parameters.get('charset'))\n const string = decoder.decode(buffer)\n\n // Text\n return string\n }\n\n // Binary\n return new Blob([buffer], { type: mimeType.essence })\n}\n"],"mappings":";;;;AAMA,SAAgB,aAAa,QAAqB,aAAqB;CACrE,MAAM,WAAW,IAAI,eAAe,YAAY;AAEhD,KAAI,gBAAgB,SAAS,QAAQ,CAKnC,QAJgB,IAAI,YAAY,SAAS,WAAW,IAAI,UAAU,CAAC,CAC5C,OAAO,OAAO;AAOvC,QAAO,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,MAAM,SAAS,SAAS,CAAC"}
|
package/dist/style.css
CHANGED
|
@@ -5366,12 +5366,12 @@ input[data-v-c1a50a6e]::placeholder {
|
|
|
5366
5366
|
height: 256px;
|
|
5367
5367
|
}
|
|
5368
5368
|
|
|
5369
|
-
.scalar-app .h
|
|
5370
|
-
height:
|
|
5369
|
+
.scalar-app .h-125 {
|
|
5370
|
+
height: 500px;
|
|
5371
5371
|
}
|
|
5372
5372
|
|
|
5373
|
-
.scalar-app .h-\[
|
|
5374
|
-
height:
|
|
5373
|
+
.scalar-app .h-\[68px\] {
|
|
5374
|
+
height: 68px;
|
|
5375
5375
|
}
|
|
5376
5376
|
|
|
5377
5377
|
.scalar-app .h-\[calc\(100\%-273\.5px\)\] {
|
|
@@ -5666,10 +5666,6 @@ input[data-v-c1a50a6e]::placeholder {
|
|
|
5666
5666
|
max-width: 14px;
|
|
5667
5667
|
}
|
|
5668
5668
|
|
|
5669
|
-
.scalar-app .max-w-\[16rem\] {
|
|
5670
|
-
max-width: 16rem;
|
|
5671
|
-
}
|
|
5672
|
-
|
|
5673
5669
|
.scalar-app .max-w-\[37px\] {
|
|
5674
5670
|
max-width: 37px;
|
|
5675
5671
|
}
|
|
@@ -7383,6 +7379,10 @@ input[data-v-c1a50a6e]::placeholder {
|
|
|
7383
7379
|
height: 32px;
|
|
7384
7380
|
}
|
|
7385
7381
|
|
|
7382
|
+
:is(.scalar-app .\*\:max-w-64 > *) {
|
|
7383
|
+
max-width: 256px;
|
|
7384
|
+
}
|
|
7385
|
+
|
|
7386
7386
|
:is(.scalar-app .\*\:cursor-pointer > *) {
|
|
7387
7387
|
cursor: pointer;
|
|
7388
7388
|
}
|
|
@@ -7661,6 +7661,11 @@ input[data-v-c1a50a6e]::placeholder {
|
|
|
7661
7661
|
background-color: var(--scalar-border-color);
|
|
7662
7662
|
}
|
|
7663
7663
|
|
|
7664
|
+
:is(.scalar-app .\*\:not-first\:before\:content-\[\'_·_\'\] > *):not(:first-child):before {
|
|
7665
|
+
--tw-content: " · ";
|
|
7666
|
+
content: var(--tw-content);
|
|
7667
|
+
}
|
|
7668
|
+
|
|
7664
7669
|
.scalar-app .after\:content-\[\'\:\'\]:after {
|
|
7665
7670
|
--tw-content: ":";
|
|
7666
7671
|
content: var(--tw-content);
|
|
@@ -8399,6 +8404,10 @@ input[data-v-c1a50a6e]::placeholder {
|
|
|
8399
8404
|
-webkit-app-region: drag;
|
|
8400
8405
|
}
|
|
8401
8406
|
|
|
8407
|
+
.scalar-app .\[\&_\.monaco-editor\]\:rounded-lg .monaco-editor, .scalar-app .\[\&_\.overflow-guard\]\:rounded-lg .overflow-guard {
|
|
8408
|
+
border-radius: var(--scalar-radius-lg);
|
|
8409
|
+
}
|
|
8410
|
+
|
|
8402
8411
|
.scalar-app .\[\&\>label\]\:bg-transparent > label {
|
|
8403
8412
|
background-color: #0000;
|
|
8404
8413
|
}
|
|
@@ -8857,16 +8866,6 @@ input[data-v-c1a50a6e]::placeholder {
|
|
|
8857
8866
|
display: none;
|
|
8858
8867
|
}
|
|
8859
8868
|
|
|
8860
|
-
.schema > span[data-v-f2ab7aa3]:not(:first-child)::before {
|
|
8861
|
-
content: '·';
|
|
8862
|
-
display: block;
|
|
8863
|
-
margin: 0 0.5ch;
|
|
8864
|
-
}
|
|
8865
|
-
.schema > span[data-v-f2ab7aa3] {
|
|
8866
|
-
display: flex;
|
|
8867
|
-
white-space: nowrap;
|
|
8868
|
-
}
|
|
8869
|
-
|
|
8870
8869
|
[data-v-36811e28] .cm-editor {
|
|
8871
8870
|
padding: 0;
|
|
8872
8871
|
}
|
|
@@ -9773,15 +9772,15 @@ to {
|
|
|
9773
9772
|
text-overflow: ellipsis;
|
|
9774
9773
|
}
|
|
9775
9774
|
|
|
9776
|
-
.editor-container[data-v-
|
|
9775
|
+
.editor-container[data-v-e99ee660] {
|
|
9777
9776
|
width: 100%;
|
|
9778
9777
|
height: 100%;
|
|
9779
9778
|
}
|
|
9780
|
-
[data-v-
|
|
9779
|
+
[data-v-e99ee660] .json-path-highlight {
|
|
9781
9780
|
background-color: rgba(255, 200, 0, 0.35);
|
|
9782
9781
|
border-radius: 4px;
|
|
9783
9782
|
}
|
|
9784
|
-
[data-v-
|
|
9783
|
+
[data-v-e99ee660] .json-focus-highlight {
|
|
9785
9784
|
background-color: color-mix(
|
|
9786
9785
|
in srgb,
|
|
9787
9786
|
var(--scalar-color-accent, #24b47e) 18%,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isTextMediaType } from "../../../../views/Request/consts/mediaTypes.js";
|
|
2
|
-
import
|
|
2
|
+
import MimeTypeParser from "whatwg-mimetype";
|
|
3
3
|
//#region src/v2/blocks/operation-block/helpers/decode-buffer.ts
|
|
4
4
|
/**
|
|
5
5
|
* Decode the buffer according to its content-type
|
|
@@ -7,7 +7,7 @@ import MimeType from "whatwg-mimetype";
|
|
|
7
7
|
* @returns The decoded string or Blob
|
|
8
8
|
*/
|
|
9
9
|
var decodeBuffer = (buffer, contentType) => {
|
|
10
|
-
const mimeType = new
|
|
10
|
+
const mimeType = new MimeTypeParser(contentType);
|
|
11
11
|
if (isTextMediaType(mimeType.essence)) return new TextDecoder(mimeType.parameters.get("charset")).decode(buffer);
|
|
12
12
|
return new Blob([buffer], { type: mimeType.essence });
|
|
13
13
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decode-buffer.js","names":[],"sources":["../../../../../src/v2/blocks/operation-block/helpers/decode-buffer.ts"],"sourcesContent":["import MimeTypeParser from 'whatwg-mimetype'\n\nimport { isTextMediaType } from '@/views/Request/consts'\n\n/**\n * Decode the buffer according to its content-type\n *\n * @returns The decoded string or Blob\n */\nexport const decodeBuffer = (buffer: ArrayBuffer, contentType: string): string | Blob => {\n const mimeType = new MimeTypeParser(contentType)\n\n // Text\n if (isTextMediaType(mimeType.essence)) {\n const decoder = new TextDecoder(mimeType.parameters.get('charset'))\n return decoder.decode(buffer)\n }\n\n // Binary\n return new Blob([buffer], { type: mimeType.essence })\n}\n"],"mappings":";;;;;;;;AASA,IAAa,gBAAgB,QAAqB,gBAAuC;CACvF,MAAM,WAAW,IAAI,
|
|
1
|
+
{"version":3,"file":"decode-buffer.js","names":[],"sources":["../../../../../src/v2/blocks/operation-block/helpers/decode-buffer.ts"],"sourcesContent":["import MimeTypeParser from 'whatwg-mimetype'\n\nimport { isTextMediaType } from '@/views/Request/consts'\n\n/**\n * Decode the buffer according to its content-type\n *\n * @returns The decoded string or Blob\n */\nexport const decodeBuffer = (buffer: ArrayBuffer, contentType: string): string | Blob => {\n const mimeType = new MimeTypeParser(contentType)\n\n // Text\n if (isTextMediaType(mimeType.essence)) {\n const decoder = new TextDecoder(mimeType.parameters.get('charset'))\n return decoder.decode(buffer)\n }\n\n // Binary\n return new Blob([buffer], { type: mimeType.essence })\n}\n"],"mappings":";;;;;;;;AASA,IAAa,gBAAgB,QAAqB,gBAAuC;CACvF,MAAM,WAAW,IAAI,eAAe,YAAY;AAGhD,KAAI,gBAAgB,SAAS,QAAQ,CAEnC,QADgB,IAAI,YAAY,SAAS,WAAW,IAAI,UAAU,CAAC,CACpD,OAAO,OAAO;AAI/B,QAAO,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,MAAM,SAAS,SAAS,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestTableTooltip.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestTableTooltip.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RequestTableTooltip.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestTableTooltip.vue"],"names":[],"mappings":"AAsEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAA;AAKhG,KAAK,WAAW,GAAG;IACjB,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAC;AA8KF,QAAA,MAAM,YAAY,kSAEhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import _plugin_vue_export_helper_default from "../../../../_virtual/_plugin-vue_export-helper.js";
|
|
2
1
|
import RequestTableTooltip_vue_vue_type_script_setup_true_lang_default from "./RequestTableTooltip.vue.script.js";
|
|
3
|
-
/* empty css */
|
|
4
2
|
//#region src/v2/blocks/request-block/components/RequestTableTooltip.vue
|
|
5
|
-
var RequestTableTooltip_default =
|
|
3
|
+
var RequestTableTooltip_default = RequestTableTooltip_vue_vue_type_script_setup_true_lang_default;
|
|
6
4
|
//#endregion
|
|
7
5
|
export { RequestTableTooltip_default as default };
|
|
8
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestTableTooltip.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestTableTooltip.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarMarkdown, ScalarPopover } from '@scalar/components'\nimport { ScalarIconInfo, ScalarIconWarning } from '@scalar/icons'\nimport type { SchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed } from 'vue'\n\nimport { validateParameter } from '@/v2/blocks/request-block/helpers/validate-parameter'\n\nconst { schema, value, description } = defineProps<{\n schema?: SchemaObject\n value: string | File | null\n description?: string\n}>()\n\nconst invalidParameterMessage = computed(() => validateParameter(schema, value))\nconst isInvalid = computed(() => invalidParameterMessage.value.ok === false)\n</script>\n<template>\n <ScalarPopover\n :offset=\"4\"\n placement=\"left\"\n teleport>\n <button\n :aria-label=\"isInvalid ? 'Input is invalid' : 'More Information'\"\n class=\"text-c-2 hover:text-c-1 hover:bg-b-2 rounded p-1\"\n :role=\"isInvalid ? 'alert' : 'none'\"\n type=\"button\">\n <ScalarIconWarning\n v-if=\"isInvalid\"\n class=\"text-orange size-3.5 brightness-90 hover:brightness-75\" />\n <ScalarIconInfo\n v-else\n class=\"text-c-2 hover:text-c-1 size-3.5\" />\n </button>\n <template #popover>\n <div\n class=\"w-content text-xxs text-c-1 grid min-w-48 gap-1.5 rounded px-1.5 pt-2 pb-1.5 leading-none\">\n <div\n v-if=\"invalidParameterMessage.ok === false\"\n class=\"text-error-1\">\n {{ invalidParameterMessage.message }}\n </div>\n <div\n v-else-if=\"\n schema &&\n ('type' in schema ||\n 'format' in schema ||\n 'minimum' in schema ||\n 'maximum' in schema ||\n 'default' in schema)\n \"\n class=\"schema text-c-2
|
|
1
|
+
{"version":3,"file":"RequestTableTooltip.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestTableTooltip.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarMarkdown, ScalarPopover } from '@scalar/components'\nimport { ScalarIconInfo, ScalarIconWarning } from '@scalar/icons'\nimport type { SchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed } from 'vue'\n\nimport { validateParameter } from '@/v2/blocks/request-block/helpers/validate-parameter'\n\nconst { schema, value, description } = defineProps<{\n schema?: SchemaObject\n value: string | File | null\n description?: string\n}>()\n\nconst invalidParameterMessage = computed(() => validateParameter(schema, value))\nconst isInvalid = computed(() => invalidParameterMessage.value.ok === false)\n</script>\n<template>\n <ScalarPopover\n :offset=\"4\"\n placement=\"left\"\n teleport>\n <button\n :aria-label=\"isInvalid ? 'Input is invalid' : 'More Information'\"\n class=\"text-c-2 hover:text-c-1 hover:bg-b-2 rounded p-1\"\n :role=\"isInvalid ? 'alert' : 'none'\"\n type=\"button\">\n <ScalarIconWarning\n v-if=\"isInvalid\"\n class=\"text-orange size-3.5 brightness-90 hover:brightness-75\" />\n <ScalarIconInfo\n v-else\n class=\"text-c-2 hover:text-c-1 size-3.5\" />\n </button>\n <template #popover>\n <div\n class=\"w-content text-xxs text-c-1 grid min-w-48 gap-1.5 rounded px-1.5 pt-2 pb-1.5 leading-none *:max-w-64\">\n <div\n v-if=\"invalidParameterMessage.ok === false\"\n class=\"text-error-1\">\n {{ invalidParameterMessage.message }}\n </div>\n <div\n v-else-if=\"\n schema &&\n ('type' in schema ||\n 'format' in schema ||\n 'minimum' in schema ||\n 'maximum' in schema ||\n 'default' in schema)\n \"\n class=\"schema text-c-2 truncate *:not-first:before:content-['_·_']\">\n <span v-if=\"'type' in schema\">{{ schema.type }}</span>\n <span v-if=\"'format' in schema\">{{ schema.format }}</span>\n <span v-if=\"'minimum' in schema\">min: {{ schema.minimum }}</span>\n <span v-if=\"'maximum' in schema\">max: {{ schema.maximum }}</span>\n <span v-if=\"'default' in schema\">default: {{ schema.default }}</span>\n </div>\n <ScalarMarkdown\n v-if=\"description && !isInvalid\"\n :value=\"description\" />\n </div>\n </template>\n </ScalarPopover>\n</template>\n"],"mappings":""}
|
|
@@ -4,14 +4,14 @@ import { ScalarMarkdown, ScalarPopover } from "@scalar/components";
|
|
|
4
4
|
import { ScalarIconInfo, ScalarIconWarning } from "@scalar/icons";
|
|
5
5
|
//#region src/v2/blocks/request-block/components/RequestTableTooltip.vue?vue&type=script&setup=true&lang.ts
|
|
6
6
|
var _hoisted_1 = ["aria-label", "role"];
|
|
7
|
-
var _hoisted_2 = { class: "w-content text-xxs text-c-1 grid min-w-48 gap-1.5 rounded px-1.5 pt-2 pb-1.5 leading-none" };
|
|
7
|
+
var _hoisted_2 = { class: "w-content text-xxs text-c-1 grid min-w-48 gap-1.5 rounded px-1.5 pt-2 pb-1.5 leading-none *:max-w-64" };
|
|
8
8
|
var _hoisted_3 = {
|
|
9
9
|
key: 0,
|
|
10
10
|
class: "text-error-1"
|
|
11
11
|
};
|
|
12
12
|
var _hoisted_4 = {
|
|
13
13
|
key: 1,
|
|
14
|
-
class: "schema text-c-2
|
|
14
|
+
class: "schema text-c-2 truncate *:not-first:before:content-['_·_']"
|
|
15
15
|
};
|
|
16
16
|
var _hoisted_5 = { key: 0 };
|
|
17
17
|
var _hoisted_6 = { key: 1 };
|
|
@@ -42,7 +42,6 @@ var RequestTableTooltip_vue_vue_type_script_setup_true_lang_default = /* @__PURE
|
|
|
42
42
|
"default" in __props.schema ? (openBlock(), createElementBlock("span", _hoisted_9, "default: " + toDisplayString(__props.schema.default), 1)) : createCommentVNode("", true)
|
|
43
43
|
])) : createCommentVNode("", true), __props.description && !isInvalid.value ? (openBlock(), createBlock(unref(ScalarMarkdown), {
|
|
44
44
|
key: 2,
|
|
45
|
-
class: "max-w-[16rem]",
|
|
46
45
|
value: __props.description
|
|
47
46
|
}, null, 8, ["value"])) : createCommentVNode("", true)])]),
|
|
48
47
|
default: withCtx(() => [createElementVNode("button", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestTableTooltip.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestTableTooltip.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarMarkdown, ScalarPopover } from '@scalar/components'\nimport { ScalarIconInfo, ScalarIconWarning } from '@scalar/icons'\nimport type { SchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed } from 'vue'\n\nimport { validateParameter } from '@/v2/blocks/request-block/helpers/validate-parameter'\n\nconst { schema, value, description } = defineProps<{\n schema?: SchemaObject\n value: string | File | null\n description?: string\n}>()\n\nconst invalidParameterMessage = computed(() => validateParameter(schema, value))\nconst isInvalid = computed(() => invalidParameterMessage.value.ok === false)\n</script>\n<template>\n <ScalarPopover\n :offset=\"4\"\n placement=\"left\"\n teleport>\n <button\n :aria-label=\"isInvalid ? 'Input is invalid' : 'More Information'\"\n class=\"text-c-2 hover:text-c-1 hover:bg-b-2 rounded p-1\"\n :role=\"isInvalid ? 'alert' : 'none'\"\n type=\"button\">\n <ScalarIconWarning\n v-if=\"isInvalid\"\n class=\"text-orange size-3.5 brightness-90 hover:brightness-75\" />\n <ScalarIconInfo\n v-else\n class=\"text-c-2 hover:text-c-1 size-3.5\" />\n </button>\n <template #popover>\n <div\n class=\"w-content text-xxs text-c-1 grid min-w-48 gap-1.5 rounded px-1.5 pt-2 pb-1.5 leading-none\">\n <div\n v-if=\"invalidParameterMessage.ok === false\"\n class=\"text-error-1\">\n {{ invalidParameterMessage.message }}\n </div>\n <div\n v-else-if=\"\n schema &&\n ('type' in schema ||\n 'format' in schema ||\n 'minimum' in schema ||\n 'maximum' in schema ||\n 'default' in schema)\n \"\n class=\"schema text-c-2
|
|
1
|
+
{"version":3,"file":"RequestTableTooltip.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestTableTooltip.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarMarkdown, ScalarPopover } from '@scalar/components'\nimport { ScalarIconInfo, ScalarIconWarning } from '@scalar/icons'\nimport type { SchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { computed } from 'vue'\n\nimport { validateParameter } from '@/v2/blocks/request-block/helpers/validate-parameter'\n\nconst { schema, value, description } = defineProps<{\n schema?: SchemaObject\n value: string | File | null\n description?: string\n}>()\n\nconst invalidParameterMessage = computed(() => validateParameter(schema, value))\nconst isInvalid = computed(() => invalidParameterMessage.value.ok === false)\n</script>\n<template>\n <ScalarPopover\n :offset=\"4\"\n placement=\"left\"\n teleport>\n <button\n :aria-label=\"isInvalid ? 'Input is invalid' : 'More Information'\"\n class=\"text-c-2 hover:text-c-1 hover:bg-b-2 rounded p-1\"\n :role=\"isInvalid ? 'alert' : 'none'\"\n type=\"button\">\n <ScalarIconWarning\n v-if=\"isInvalid\"\n class=\"text-orange size-3.5 brightness-90 hover:brightness-75\" />\n <ScalarIconInfo\n v-else\n class=\"text-c-2 hover:text-c-1 size-3.5\" />\n </button>\n <template #popover>\n <div\n class=\"w-content text-xxs text-c-1 grid min-w-48 gap-1.5 rounded px-1.5 pt-2 pb-1.5 leading-none *:max-w-64\">\n <div\n v-if=\"invalidParameterMessage.ok === false\"\n class=\"text-error-1\">\n {{ invalidParameterMessage.message }}\n </div>\n <div\n v-else-if=\"\n schema &&\n ('type' in schema ||\n 'format' in schema ||\n 'minimum' in schema ||\n 'maximum' in schema ||\n 'default' in schema)\n \"\n class=\"schema text-c-2 truncate *:not-first:before:content-['_·_']\">\n <span v-if=\"'type' in schema\">{{ schema.type }}</span>\n <span v-if=\"'format' in schema\">{{ schema.format }}</span>\n <span v-if=\"'minimum' in schema\">min: {{ schema.minimum }}</span>\n <span v-if=\"'maximum' in schema\">max: {{ schema.maximum }}</span>\n <span v-if=\"'default' in schema\">default: {{ schema.default }}</span>\n </div>\n <ScalarMarkdown\n v-if=\"description && !isInvalid\"\n :value=\"description\" />\n </div>\n </template>\n </ScalarPopover>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcA,MAAM,0BAA0B,eAAe,kBAAkB,QAAA,QAAQ,QAAA,MAAM,CAAA;EAC/E,MAAM,YAAY,eAAe,wBAAwB,MAAM,OAAO,MAAK;;uBAGzE,YA6CgB,MAAA,cAAA,EAAA;IA5Cb,QAAQ;IACT,WAAU;IACV,UAAA;;IAaW,SAAO,cA2BV,CA1BN,mBA0BM,OA1BN,YA0BM,CAvBI,wBAAA,MAAwB,OAAE,SAAA,WAAA,EADlC,mBAIM,OAJN,YAIM,gBADD,wBAAA,MAAwB,QAAO,EAAA,EAAA,IAGV,QAAA,WAAA,UAAiC,QAAA,UAAA,YAAoC,QAAA,UAAA,aAAqC,QAAA,UAAA,aAAqC,QAAA,UAAA,aAAqC,QAAA,WAAA,WAAA,EAD9M,mBAeM,OAfN,YAeM;eALkB,QAAA,UAAA,WAAA,EAAtB,mBAAsD,QAAA,YAAA,gBAArB,QAAA,OAAO,KAAI,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;iBACpB,QAAA,UAAA,WAAA,EAAxB,mBAA0D,QAAA,YAAA,gBAAvB,QAAA,OAAO,OAAM,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;kBACvB,QAAA,UAAA,WAAA,EAAzB,mBAAiE,QAAA,YAAhC,UAAK,gBAAG,QAAA,OAAO,QAAO,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;kBAC9B,QAAA,UAAA,WAAA,EAAzB,mBAAiE,QAAA,YAAhC,UAAK,gBAAG,QAAA,OAAO,QAAO,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;kBAC9B,QAAA,UAAA,WAAA,EAAzB,mBAAqE,QAAA,YAApC,cAAS,gBAAG,QAAA,OAAO,QAAO,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;wCAGrD,QAAA,eAAW,CAAK,UAAA,SAAA,WAAA,EADxB,YAEyB,MAAA,eAAA,EAAA;;KAAtB,OAAO,QAAA;;2BA3BL,CAXT,mBAWS,UAAA;KAVN,cAAY,UAAA,QAAS,qBAAA;KACtB,OAAM;KACL,MAAM,UAAA,QAAS,UAAA;KAChB,MAAK;QAEG,UAAA,SAAA,WAAA,EADR,YAEmE,MAAA,kBAAA,EAAA;;KAAjE,OAAM;wBACR,YAE6C,MAAA,eAAA,EAAA;;KAA3C,OAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-form-body-rows.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/helpers/get-form-body-rows.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAA;AAG/G,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0DAA0D,CAAA;
|
|
1
|
+
{"version":3,"file":"get-form-body-rows.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/helpers/get-form-body-rows.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAA;AAG/G,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0DAA0D,CAAA;AAYxF,uGAAuG;AACvG,eAAO,MAAM,eAAe,GAC1B,SAAS,aAAa,GAAG,SAAS,GAAG,IAAI,EACzC,aAAa,MAAM,EACnB,iBAAiB,YAAY,KAC5B,QAAQ,EAuDV,CAAA"}
|
|
@@ -3,6 +3,11 @@ import { resolve } from "@scalar/workspace-store/resolve";
|
|
|
3
3
|
import { objectEntries } from "@scalar/helpers/object/object-entries";
|
|
4
4
|
import { isObjectSchema } from "@scalar/workspace-store/schemas/v3.1/strict/type-guards";
|
|
5
5
|
//#region src/v2/blocks/request-block/helpers/get-form-body-rows.ts
|
|
6
|
+
var stringifyValue = (value) => {
|
|
7
|
+
if (value instanceof File) return value.name;
|
|
8
|
+
if (typeof value === "object" && value !== null) return JSON.stringify(value);
|
|
9
|
+
return String(value);
|
|
10
|
+
};
|
|
6
11
|
/** Build the table rows for the form data, optionally enriched with schema (e.g. enum) per property */
|
|
7
12
|
var getFormBodyRows = (example, contentType, formBodySchema) => {
|
|
8
13
|
if (!example?.value || contentType !== "multipart/form-data" && contentType !== "application/x-www-form-urlencoded") return [];
|
|
@@ -36,7 +41,7 @@ var getFormBodyRows = (example, contentType, formBodySchema) => {
|
|
|
36
41
|
});
|
|
37
42
|
if (typeof example.value === "object" && example.value) return objectEntries(example.value).map(([key, value]) => mapRow({
|
|
38
43
|
name: String(key),
|
|
39
|
-
value
|
|
44
|
+
value: stringifyValue(value)
|
|
40
45
|
}));
|
|
41
46
|
return [];
|
|
42
47
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-form-body-rows.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/helpers/get-form-body-rows.ts"],"sourcesContent":["import { isObject } from '@scalar/helpers/object/is-object'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { ExampleObject, SchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { isObjectSchema } from '@scalar/workspace-store/schemas/v3.1/strict/type-guards'\n\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\n\n/** Build the table rows for the form data, optionally enriched with schema (e.g. enum) per property */\nexport const getFormBodyRows = (\n example: ExampleObject | undefined | null,\n contentType: string,\n formBodySchema?: SchemaObject,\n): TableRow[] => {\n // We only need the rows for formData\n if (\n !example?.value ||\n (contentType !== 'multipart/form-data' && contentType !== 'application/x-www-form-urlencoded')\n ) {\n return []\n }\n\n // Get all the schema properties if the schema is an object schema\n const schemaWithProperties = formBodySchema && isObjectSchema(formBodySchema) ? formBodySchema : undefined\n const requiredSet = schemaWithProperties ? new Set(schemaWithProperties.required ?? []) : undefined\n\n const mapRow = ({\n name,\n value,\n isDisabled = false,\n }: {\n name: string\n value: string | File\n isDisabled?: boolean\n }): TableRow => {\n const row: TableRow = { name, value, isDisabled }\n if (!schemaWithProperties || !name) {\n return row\n }\n const propSchema = resolve.schema(schemaWithProperties.properties?.[name])\n row.schema = propSchema\n row.description = propSchema?.description\n row.isRequired = requiredSet?.has(name)\n row.isDisabled = isDisabled\n return row\n }\n\n // We have form data stored as an array\n if (Array.isArray(example.value)) {\n return example.value.map((exampleValue) => {\n if (isObject(exampleValue)) {\n const name = String(exampleValue.name)\n const value = exampleValue.value instanceof File ? exampleValue.value : String(exampleValue.value)\n const isDisabled = Boolean(exampleValue.isDisabled)\n return mapRow({ name, value, isDisabled })\n }\n return { name: '', value: exampleValue, isDisabled: false }\n })\n }\n\n // We got an object try to convert it to an array of rows\n if (typeof example.value === 'object' && example.value) {\n return objectEntries(example.value).map(([key, value])
|
|
1
|
+
{"version":3,"file":"get-form-body-rows.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/helpers/get-form-body-rows.ts"],"sourcesContent":["import { isObject } from '@scalar/helpers/object/is-object'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { ExampleObject, SchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport { isObjectSchema } from '@scalar/workspace-store/schemas/v3.1/strict/type-guards'\n\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\n\nconst stringifyValue = (value: unknown) => {\n if (value instanceof File) {\n return value.name\n }\n if (typeof value === 'object' && value !== null) {\n return JSON.stringify(value)\n }\n return String(value)\n}\n\n/** Build the table rows for the form data, optionally enriched with schema (e.g. enum) per property */\nexport const getFormBodyRows = (\n example: ExampleObject | undefined | null,\n contentType: string,\n formBodySchema?: SchemaObject,\n): TableRow[] => {\n // We only need the rows for formData\n if (\n !example?.value ||\n (contentType !== 'multipart/form-data' && contentType !== 'application/x-www-form-urlencoded')\n ) {\n return []\n }\n\n // Get all the schema properties if the schema is an object schema\n const schemaWithProperties = formBodySchema && isObjectSchema(formBodySchema) ? formBodySchema : undefined\n const requiredSet = schemaWithProperties ? new Set(schemaWithProperties.required ?? []) : undefined\n\n const mapRow = ({\n name,\n value,\n isDisabled = false,\n }: {\n name: string\n value: string | File\n isDisabled?: boolean\n }): TableRow => {\n const row: TableRow = { name, value, isDisabled }\n if (!schemaWithProperties || !name) {\n return row\n }\n const propSchema = resolve.schema(schemaWithProperties.properties?.[name])\n row.schema = propSchema\n row.description = propSchema?.description\n row.isRequired = requiredSet?.has(name)\n row.isDisabled = isDisabled\n return row\n }\n\n // We have form data stored as an array\n if (Array.isArray(example.value)) {\n return example.value.map((exampleValue) => {\n if (isObject(exampleValue)) {\n const name = String(exampleValue.name)\n const value = exampleValue.value instanceof File ? exampleValue.value : String(exampleValue.value)\n const isDisabled = Boolean(exampleValue.isDisabled)\n return mapRow({ name, value, isDisabled })\n }\n return { name: '', value: exampleValue, isDisabled: false }\n })\n }\n\n // We got an object try to convert it to an array of rows\n if (typeof example.value === 'object' && example.value) {\n return objectEntries(example.value).map(([key, value]) =>\n mapRow({ name: String(key), value: stringifyValue(value) }),\n )\n }\n\n return []\n}\n"],"mappings":";;;;;AAQA,IAAM,kBAAkB,UAAmB;AACzC,KAAI,iBAAiB,KACnB,QAAO,MAAM;AAEf,KAAI,OAAO,UAAU,YAAY,UAAU,KACzC,QAAO,KAAK,UAAU,MAAM;AAE9B,QAAO,OAAO,MAAM;;;AAItB,IAAa,mBACX,SACA,aACA,mBACe;AAEf,KACE,CAAC,SAAS,SACT,gBAAgB,yBAAyB,gBAAgB,oCAE1D,QAAO,EAAE;CAIX,MAAM,uBAAuB,kBAAkB,eAAe,eAAe,GAAG,iBAAiB,KAAA;CACjG,MAAM,cAAc,uBAAuB,IAAI,IAAI,qBAAqB,YAAY,EAAE,CAAC,GAAG,KAAA;CAE1F,MAAM,UAAU,EACd,MACA,OACA,aAAa,YAKC;EACd,MAAM,MAAgB;GAAE;GAAM;GAAO;GAAY;AACjD,MAAI,CAAC,wBAAwB,CAAC,KAC5B,QAAO;EAET,MAAM,aAAa,QAAQ,OAAO,qBAAqB,aAAa,MAAM;AAC1E,MAAI,SAAS;AACb,MAAI,cAAc,YAAY;AAC9B,MAAI,aAAa,aAAa,IAAI,KAAK;AACvC,MAAI,aAAa;AACjB,SAAO;;AAIT,KAAI,MAAM,QAAQ,QAAQ,MAAM,CAC9B,QAAO,QAAQ,MAAM,KAAK,iBAAiB;AACzC,MAAI,SAAS,aAAa,CAIxB,QAAO,OAAO;GAAE,MAHH,OAAO,aAAa,KAAK;GAGhB,OAFR,aAAa,iBAAiB,OAAO,aAAa,QAAQ,OAAO,aAAa,MAAM;GAErE,YADV,QAAQ,aAAa,WAAW;GACV,CAAC;AAE5C,SAAO;GAAE,MAAM;GAAI,OAAO;GAAc,YAAY;GAAO;GAC3D;AAIJ,KAAI,OAAO,QAAQ,UAAU,YAAY,QAAQ,MAC/C,QAAO,cAAc,QAAQ,MAAM,CAAC,KAAK,CAAC,KAAK,WAC7C,OAAO;EAAE,MAAM,OAAO,IAAI;EAAE,OAAO,eAAe,MAAM;EAAE,CAAC,CAC5D;AAGH,QAAO,EAAE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { extractFilename } from "./extract-filename.js";
|
|
2
|
-
import
|
|
2
|
+
import MimeTypeParser from "whatwg-mimetype";
|
|
3
3
|
//#region src/v2/blocks/response-block/helpers/process-response-body.ts
|
|
4
4
|
var isBlob = (b) => b instanceof Blob;
|
|
5
5
|
/**
|
|
@@ -8,7 +8,7 @@ var isBlob = (b) => b instanceof Blob;
|
|
|
8
8
|
*/
|
|
9
9
|
function processResponseBody({ data, headers }) {
|
|
10
10
|
const contentType = headers.find((header) => header.name.toLowerCase() === "content-type");
|
|
11
|
-
const mimeType = contentType?.value ? new
|
|
11
|
+
const mimeType = contentType?.value ? new MimeTypeParser(contentType.value) : void 0;
|
|
12
12
|
return {
|
|
13
13
|
mimeType,
|
|
14
14
|
attachmentFilename: extractFilename(headers.find((header) => header.name.toLowerCase() === "content-disposition")?.value ?? ""),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-response-body.js","names":[],"sources":["../../../../../src/v2/blocks/response-block/helpers/process-response-body.ts"],"sourcesContent":["import MimeType from 'whatwg-mimetype'\n\nimport { extractFilename } from './../helpers/extract-filename'\n\nconst isBlob = (b: any): b is Blob => b instanceof Blob\n\n/**\n * Processes the response body of an HTTP request.\n * Extracts MIME type, attachment filename, and generates a data URL.\n */\nexport function processResponseBody({ data, headers }: { data: unknown; headers: { name: string; value: string }[] }) {\n const contentType = headers.find((header) => header.name.toLowerCase() === 'content-type')\n const mimeType = contentType?.value ? new MimeType(contentType.value) : undefined\n const attachmentFilename = extractFilename(\n headers.find((header) => header.name.toLowerCase() === 'content-disposition')?.value ?? '',\n )\n\n const dataUrl = (() => {\n if (isBlob(data)) {\n return URL.createObjectURL(data)\n }\n if (typeof data === 'string') {\n return URL.createObjectURL(new Blob([data], { type: mimeType ? mimeType.toString() : undefined }))\n }\n if (data instanceof Object && Object.keys(data).length) {\n return URL.createObjectURL(\n new Blob([JSON.stringify(data)], {\n type: mimeType ? mimeType.toString() : undefined,\n }),\n )\n }\n return ''\n })()\n\n return { mimeType, attachmentFilename, dataUrl }\n}\n"],"mappings":";;;AAIA,IAAM,UAAU,MAAsB,aAAa;;;;;AAMnD,SAAgB,oBAAoB,EAAE,MAAM,WAA0E;CACpH,MAAM,cAAc,QAAQ,MAAM,WAAW,OAAO,KAAK,aAAa,KAAK,eAAe;CAC1F,MAAM,WAAW,aAAa,QAAQ,IAAI,
|
|
1
|
+
{"version":3,"file":"process-response-body.js","names":[],"sources":["../../../../../src/v2/blocks/response-block/helpers/process-response-body.ts"],"sourcesContent":["import MimeType from 'whatwg-mimetype'\n\nimport { extractFilename } from './../helpers/extract-filename'\n\nconst isBlob = (b: any): b is Blob => b instanceof Blob\n\n/**\n * Processes the response body of an HTTP request.\n * Extracts MIME type, attachment filename, and generates a data URL.\n */\nexport function processResponseBody({ data, headers }: { data: unknown; headers: { name: string; value: string }[] }) {\n const contentType = headers.find((header) => header.name.toLowerCase() === 'content-type')\n const mimeType = contentType?.value ? new MimeType(contentType.value) : undefined\n const attachmentFilename = extractFilename(\n headers.find((header) => header.name.toLowerCase() === 'content-disposition')?.value ?? '',\n )\n\n const dataUrl = (() => {\n if (isBlob(data)) {\n return URL.createObjectURL(data)\n }\n if (typeof data === 'string') {\n return URL.createObjectURL(new Blob([data], { type: mimeType ? mimeType.toString() : undefined }))\n }\n if (data instanceof Object && Object.keys(data).length) {\n return URL.createObjectURL(\n new Blob([JSON.stringify(data)], {\n type: mimeType ? mimeType.toString() : undefined,\n }),\n )\n }\n return ''\n })()\n\n return { mimeType, attachmentFilename, dataUrl }\n}\n"],"mappings":";;;AAIA,IAAM,UAAU,MAAsB,aAAa;;;;;AAMnD,SAAgB,oBAAoB,EAAE,MAAM,WAA0E;CACpH,MAAM,cAAc,QAAQ,MAAM,WAAW,OAAO,KAAK,aAAa,KAAK,eAAe;CAC1F,MAAM,WAAW,aAAa,QAAQ,IAAI,eAAS,YAAY,MAAM,GAAG,KAAA;AAsBxE,QAAO;EAAE;EAAU,oBArBQ,gBACzB,QAAQ,MAAM,WAAW,OAAO,KAAK,aAAa,KAAK,sBAAsB,EAAE,SAAS,GACzF;EAmBsC,gBAjBhB;AACrB,OAAI,OAAO,KAAK,CACd,QAAO,IAAI,gBAAgB,KAAK;AAElC,OAAI,OAAO,SAAS,SAClB,QAAO,IAAI,gBAAgB,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,MAAM,WAAW,SAAS,UAAU,GAAG,KAAA,GAAW,CAAC,CAAC;AAEpG,OAAI,gBAAgB,UAAU,OAAO,KAAK,KAAK,CAAC,OAC9C,QAAO,IAAI,gBACT,IAAI,KAAK,CAAC,KAAK,UAAU,KAAK,CAAC,EAAE,EAC/B,MAAM,WAAW,SAAS,UAAU,GAAG,KAAA,GACxC,CAAC,CACH;AAEH,UAAO;MACL;EAE4C"}
|
package/dist/v2/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/v2/constants.ts
|
|
2
2
|
/** The version number taken from the package.json. Consumers can override at build time via define (e.g. OVERRIDE_PACKAGE_VERSION: JSON.stringify('1.2.3')). */
|
|
3
|
-
var APP_VERSION = typeof OVERRIDE_PACKAGE_VERSION !== "undefined" ? OVERRIDE_PACKAGE_VERSION : "2.39.
|
|
3
|
+
var APP_VERSION = typeof OVERRIDE_PACKAGE_VERSION !== "undefined" ? OVERRIDE_PACKAGE_VERSION : "2.39.3";
|
|
4
4
|
//#endregion
|
|
5
5
|
export { APP_VERSION };
|
|
6
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Editor.vue.d.ts","sourceRoot":"","sources":["../../../../../../src/v2/features/collection/components/Editor/Editor.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Editor.vue.d.ts","sourceRoot":"","sources":["../../../../../../src/v2/features/collection/components/Editor/Editor.vue"],"names":[],"mappings":"AA4xCA,QAAA,MAAM,YAAY;;;;;;;;;;;;kGAEhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -2,7 +2,7 @@ import _plugin_vue_export_helper_default from "../../../../../_virtual/_plugin-v
|
|
|
2
2
|
import Editor_vue_vue_type_script_setup_true_lang_default from "./Editor.vue.script.js";
|
|
3
3
|
/* empty css */
|
|
4
4
|
//#region src/v2/features/collection/components/Editor/Editor.vue
|
|
5
|
-
var Editor_default = /* @__PURE__ */ _plugin_vue_export_helper_default(Editor_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-
|
|
5
|
+
var Editor_default = /* @__PURE__ */ _plugin_vue_export_helper_default(Editor_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-e99ee660"]]);
|
|
6
6
|
//#endregion
|
|
7
7
|
export { Editor_default as default };
|
|
8
8
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Editor.vue.js","names":[],"sources":["../../../../../../src/v2/features/collection/components/Editor/Editor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton, ScalarHotkey, useLoadingState } from '@scalar/components'\nimport { debounce } from '@scalar/helpers/general/debounce'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { ScalarIconArrowsIn, ScalarIconArrowsOut } from '@scalar/icons'\nimport * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n shallowRef,\n watch,\n} from 'vue'\n\nimport type { CollectionProps } from '@/v2/features/app/helpers/routes'\nimport { useEditor, useJsonPointerLinkSupport } from '@/v2/features/editor'\nimport { createJsonModel } from '@/v2/features/editor/helpers/json/create-json-model'\nimport { createYamlModel } from '@/v2/features/editor/helpers/yaml/create-yaml-model'\nimport { useEditorMarkers } from '@/v2/features/editor/hooks/use-editor-markers'\n\nimport EditorDiagnosticsPanel from './components/EditorDiagnosticsPanel.vue'\nimport EditorSavePanel from './components/EditorSavePanel.vue'\nimport { getDiagnosticCounts } from './helpers/get-diagnostic-counts'\nimport { getOperationContext } from './helpers/get-operation-context'\nimport { getVisibleDiagnostics } from './helpers/get-visible-diagnostics'\nimport { parseEditorObject } from './helpers/parse-editor-object'\nimport { stringifyDocument } from './helpers/stringify-document'\nimport { useEditorState } from './hooks/use-editor-state'\n\nconst { collectionType, documentSlug, method, path, workspaceStore } =\n defineProps<CollectionProps>()\n\nconst MAX_VISIBLE_DIAGNOSTICS = 6\nconst EDITOR_PERSIST_DEBOUNCE_KEY = 'editor:replace-document'\n\nconst monacoEditorRef = ref<HTMLElement>()\nconst editorApi = shallowRef<ReturnType<typeof useEditor>>()\n\nconst saveLoader = useLoadingState()\nconst {\n isAutoSaveEnabled,\n isDirty,\n editorLanguage,\n isDiagnosticsPaneExpanded,\n isEditorMaximized,\n isYamlMode,\n editorBottomPadding,\n editorRootClass,\n getLanguageToggleClass,\n toggleEditorMaximized,\n} = useEditorState()\n\nconst jsonModel = createJsonModel()\nconst yamlModel = createYamlModel()\n\nconst selectedLanguage = ref<'json' | 'yaml'>('json')\n\nconst currentModel = computed(() => {\n return selectedLanguage.value === 'json' ? jsonModel : yamlModel\n})\n\nconst monacoEditorInstance = computed(() => editorApi.value?.editor)\nconst { markers: diagnostics } = useEditorMarkers(monacoEditorInstance)\n\nconst syncEditorBottomPadding = () => {\n editorApi.value?.editor.updateOptions({\n padding: {\n top: 0,\n bottom: editorBottomPadding.value,\n },\n })\n}\n\nconst diagnosticCounts = computed(() => getDiagnosticCounts(diagnostics.value))\nconst visibleDiagnostics = computed(() =>\n getVisibleDiagnostics(diagnostics.value, MAX_VISIBLE_DIAGNOSTICS),\n)\n\nconst focusDiagnostic = (marker: monaco.editor.IMarker) => {\n const editor = editorApi.value?.editor\n if (!editor) {\n return\n }\n editor.setSelection({\n startLineNumber: marker.startLineNumber,\n startColumn: marker.startColumn,\n endLineNumber: marker.endLineNumber,\n endColumn: marker.endColumn,\n })\n editor.revealPositionInCenter({\n lineNumber: marker.startLineNumber,\n column: marker.startColumn,\n })\n}\n\nconst getEditorValue = (): string | null => currentModel.value.model.getValue()\n\n/** Value from the editor for a specific language (use when switching to avoid reading the wrong model). */\nconst getEditorValueForLanguage = (language: 'json' | 'yaml'): string | null =>\n (language === 'json' ? jsonModel : yamlModel).model.getValue()\n\nconst getDocumentValue = async (\n language?: 'json' | 'yaml',\n): Promise<string> => {\n const document = await workspaceStore.getEditableDocument(documentSlug)\n return stringifyDocument(document, language ?? selectedLanguage.value)\n}\n\nconst debouncedPersist = debounce({ delay: 1500 })\n\nconst applyProgrammaticEditorValue = (value: string): void => {\n // Cancel pending auto-save work so synthetic model updates do not persist stale data.\n debouncedPersist.cleanup()\n editorApi.value?.setValue(value, true)\n}\n\nconst loadDocumentIntoEditor = async () => {\n applyProgrammaticEditorValue(await getDocumentValue())\n isDirty.value = false\n await focusOperation()\n}\n\nconst formatDocument = async () => {\n await editorApi.value?.formatDocument()\n}\n\nconst focusOperation = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n ])\n}\n\nconst persistEditorToWorkspace = async (value: string) => {\n const parsed = parseEditorObject(value, editorLanguage.value)\n if (!parsed) {\n const firstError = diagnostics.value.find(\n (m) => m.severity === monaco.MarkerSeverity.Error,\n )\n if (firstError) {\n focusDiagnostic(firstError)\n }\n await saveLoader.invalidate()\n return\n }\n\n saveLoader.start()\n await workspaceStore.replaceDocument(documentSlug, parsed)\n isDirty.value = false\n await saveLoader.validate({ duration: 900 })\n}\n\nconst saveNow = async () => {\n const value = getEditorValue()\n if (!value) {\n return\n }\n await persistEditorToWorkspace(value)\n}\n\nconst handleEditorChange = (value: string) => {\n isDirty.value = true\n\n if (!isAutoSaveEnabled.value) {\n return\n }\n\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n}\n\nconst focusOperationServers = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n const value = getEditorValue()\n if (!value) {\n return\n }\n\n const parsed = parseEditorObject(value, editorLanguage.value)\n\n if (!parsed || !isObject(parsed.paths)) {\n return\n }\n\n const pathsObject = parsed.paths as Record<string, unknown>\n const operationPathItem = pathsObject[operationContext.path]\n if (!isObject(operationPathItem)) {\n return\n }\n\n const operation = operationPathItem[operationContext.method]\n if (!isObject(operation)) {\n return\n }\n\n // Add default servers if not present\n operation.servers ??= []\n\n // Update the editor value\n editorApi.value?.setValue(stringifyDocument(parsed, editorLanguage.value))\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n 'servers',\n ])\n}\n\nuseJsonPointerLinkSupport(editorApi, currentModel)\n\nonMounted(() => {\n editorApi.value = useEditor({\n element: monacoEditorRef.value ?? document.createElement('div'),\n onChange: handleEditorChange,\n model: currentModel,\n actions: [\n {\n id: 'scalar.editor.focusOperation',\n label: 'Focus Operation',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyO],\n run: async () => {\n await focusOperation()\n },\n },\n {\n id: 'scalar.editor.focusOperationServers',\n label: 'Focus Operation Servers',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyS],\n run: async () => {\n await focusOperationServers()\n },\n },\n {\n id: 'scalar.editor.formatDocument',\n label: 'Format Document',\n keybindings: [\n monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KeyF,\n ],\n run: async () => {\n await formatDocument()\n },\n },\n ],\n })\n\n syncEditorBottomPadding()\n void loadDocumentIntoEditor()\n})\n\nonBeforeUnmount(() => {\n debouncedPersist.cleanup()\n\n // Persist if there is a pending save\n if (isDirty.value && isAutoSaveEnabled.value) {\n void saveNow()\n }\n editorApi.value?.dispose?.()\n // Dispose models created at setup; useEditor only disposes the editor widget, not external models.\n jsonModel.model.dispose()\n yamlModel.model.dispose()\n})\n\nwatch(() => documentSlug, loadDocumentIntoEditor)\n\nwatch(\n () => [path, method] as const,\n async () => {\n await focusOperation()\n },\n)\n\nwatch(isDiagnosticsPaneExpanded, () => {\n syncEditorBottomPadding()\n})\n\nwatch(editorLanguage, async (nextLanguage, previousLanguage) => {\n const wasDirty = isDirty.value\n // Read from the previous model before switching; getEditorValue() would use currentModel\n // which changes with selectedLanguage, so we would read the (empty) new model otherwise.\n const value = getEditorValueForLanguage(previousLanguage ?? 'json')\n if (!value) {\n selectedLanguage.value = nextLanguage\n await nextTick()\n await focusOperation()\n return\n }\n\n const parsed = parseEditorObject(value, previousLanguage ?? 'json')\n selectedLanguage.value = nextLanguage\n await nextTick()\n if (parsed) {\n applyProgrammaticEditorValue(stringifyDocument(parsed, nextLanguage))\n isDirty.value = wasDirty\n }\n await focusOperation()\n})\n\nwatch(\n isAutoSaveEnabled,\n (isEnabled) => {\n if (!isEnabled) {\n debouncedPersist.cleanup()\n return\n }\n\n if (isDirty.value) {\n const value = getEditorValue()\n if (!value) {\n return\n }\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n }\n },\n { flush: 'post' },\n)\n</script>\n\n<template>\n <div\n v-if=\"\n collectionType === 'operation' &&\n getOperationContext(path, method) !== null\n \"\n class=\"flex w-full min-w-0 flex-1 flex-col gap-2\"\n :class=\"editorRootClass\">\n <div\n class=\"grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3\">\n <div\n class=\"flex min-w-0 items-center gap-1 overflow-x-auto whitespace-nowrap\">\n <span class=\"text-c-2 text-xs font-medium whitespace-nowrap\">\n Shortcuts\n </span>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperation\">\n <span>Operation</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"O\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperationServers\">\n <span>Servers</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"S\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n </div>\n\n <div\n aria-label=\"Editor language\"\n class=\"bg-b-1 shadow-border flex items-center justify-self-center overflow-hidden rounded-lg p-0.5\"\n role=\"tablist\">\n <ScalarButton\n :aria-selected=\"!isYamlMode\"\n :class=\"getLanguageToggleClass(!isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'json'\">\n JSON\n </ScalarButton>\n <ScalarButton\n :aria-selected=\"isYamlMode\"\n :class=\"getLanguageToggleClass(isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'yaml'\">\n YAML\n </ScalarButton>\n </div>\n\n <div class=\"flex min-w-0 shrink-0 items-center gap-2 justify-self-end\">\n <ScalarButton\n size=\"xs\"\n variant=\"ghost\"\n @click=\"formatDocument\">\n <span>Format {{ isYamlMode ? 'YAML' : 'JSON' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"F\"\n :modifier=\"['Alt', 'Shift']\" />\n </span>\n </ScalarButton>\n <ScalarButton\n :aria-label=\"\n isEditorMaximized ? 'Restore editor size' : 'Maximize editor'\n \"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"toggleEditorMaximized\">\n <span>{{ isEditorMaximized ? 'Restore' : 'Maximize' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarIconArrowsIn\n v-if=\"isEditorMaximized\"\n class=\"size-3.5\" />\n <ScalarIconArrowsOut\n v-else\n class=\"size-3.5\" />\n </span>\n </ScalarButton>\n </div>\n </div>\n\n <div class=\"flex min-h-0 w-full min-w-0 flex-1 rounded-lg border\">\n <div\n class=\"relative w-full min-w-0 flex-1\"\n :class=\"isEditorMaximized ? 'h-full min-h-0' : 'h-[500px]'\">\n <div class=\"pointer-events-none absolute top-2 right-2 z-10\">\n <EditorSavePanel\n :isAutoSaveEnabled=\"isAutoSaveEnabled\"\n :isDirty=\"isDirty\"\n :saveLoader=\"saveLoader\"\n @saveNow=\"saveNow\"\n @update:isAutoSaveEnabled=\"isAutoSaveEnabled = $event\" />\n </div>\n\n <EditorDiagnosticsPanel\n :diagnosticCounts=\"diagnosticCounts\"\n :expanded=\"isDiagnosticsPaneExpanded\"\n :visibleDiagnostics=\"visibleDiagnostics\"\n @focusDiagnostic=\"focusDiagnostic\"\n @toggle=\"isDiagnosticsPaneExpanded = !isDiagnosticsPaneExpanded\" />\n\n <div\n ref=\"monacoEditorRef\"\n class=\"h-full w-full min-w-0 flex-1\" />\n </div>\n </div>\n </div>\n <div v-else>No operation context found</div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight) {\n background-color: color-mix(\n in srgb,\n var(--scalar-color-accent, #24b47e) 18%,\n transparent\n );\n border-radius: 4px;\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Editor.vue.js","names":[],"sources":["../../../../../../src/v2/features/collection/components/Editor/Editor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton, ScalarHotkey, useLoadingState } from '@scalar/components'\nimport { debounce } from '@scalar/helpers/general/debounce'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { ScalarIconArrowsIn, ScalarIconArrowsOut } from '@scalar/icons'\nimport * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n shallowRef,\n watch,\n} from 'vue'\n\nimport type { CollectionProps } from '@/v2/features/app/helpers/routes'\nimport { useEditor, useJsonPointerLinkSupport } from '@/v2/features/editor'\nimport { createJsonModel } from '@/v2/features/editor/helpers/json/create-json-model'\nimport { createYamlModel } from '@/v2/features/editor/helpers/yaml/create-yaml-model'\nimport { useEditorMarkers } from '@/v2/features/editor/hooks/use-editor-markers'\n\nimport EditorDiagnosticsPanel from './components/EditorDiagnosticsPanel.vue'\nimport EditorSavePanel from './components/EditorSavePanel.vue'\nimport { getDiagnosticCounts } from './helpers/get-diagnostic-counts'\nimport { getOperationContext } from './helpers/get-operation-context'\nimport { getVisibleDiagnostics } from './helpers/get-visible-diagnostics'\nimport { parseEditorObject } from './helpers/parse-editor-object'\nimport { stringifyDocument } from './helpers/stringify-document'\nimport { useEditorState } from './hooks/use-editor-state'\n\nconst { collectionType, documentSlug, method, path, workspaceStore } =\n defineProps<CollectionProps>()\n\nconst MAX_VISIBLE_DIAGNOSTICS = 6\nconst EDITOR_PERSIST_DEBOUNCE_KEY = 'editor:replace-document'\n\nconst monacoEditorRef = ref<HTMLElement>()\nconst editorApi = shallowRef<ReturnType<typeof useEditor>>()\n\nconst saveLoader = useLoadingState()\nconst {\n isAutoSaveEnabled,\n isDirty,\n editorLanguage,\n isDiagnosticsPaneExpanded,\n isEditorMaximized,\n isYamlMode,\n editorBottomPadding,\n editorRootClass,\n getLanguageToggleClass,\n toggleEditorMaximized,\n} = useEditorState()\n\nconst jsonModel = createJsonModel()\nconst yamlModel = createYamlModel()\n\nconst selectedLanguage = ref<'json' | 'yaml'>('json')\n\nconst currentModel = computed(() => {\n return selectedLanguage.value === 'json' ? jsonModel : yamlModel\n})\n\nconst monacoEditorInstance = computed(() => editorApi.value?.editor)\nconst { markers: diagnostics } = useEditorMarkers(monacoEditorInstance)\n\nconst syncEditorBottomPadding = () => {\n editorApi.value?.editor.updateOptions({\n padding: {\n top: 0,\n bottom: editorBottomPadding.value,\n },\n })\n}\n\nconst diagnosticCounts = computed(() => getDiagnosticCounts(diagnostics.value))\nconst visibleDiagnostics = computed(() =>\n getVisibleDiagnostics(diagnostics.value, MAX_VISIBLE_DIAGNOSTICS),\n)\n\nconst focusDiagnostic = (marker: monaco.editor.IMarker) => {\n const editor = editorApi.value?.editor\n if (!editor) {\n return\n }\n editor.setSelection({\n startLineNumber: marker.startLineNumber,\n startColumn: marker.startColumn,\n endLineNumber: marker.endLineNumber,\n endColumn: marker.endColumn,\n })\n editor.revealPositionInCenter({\n lineNumber: marker.startLineNumber,\n column: marker.startColumn,\n })\n}\n\nconst getEditorValue = (): string | null => currentModel.value.model.getValue()\n\n/** Value from the editor for a specific language (use when switching to avoid reading the wrong model). */\nconst getEditorValueForLanguage = (language: 'json' | 'yaml'): string | null =>\n (language === 'json' ? jsonModel : yamlModel).model.getValue()\n\nconst getDocumentValue = async (\n language?: 'json' | 'yaml',\n): Promise<string> => {\n const document = await workspaceStore.getEditableDocument(documentSlug)\n return stringifyDocument(document, language ?? selectedLanguage.value)\n}\n\nconst debouncedPersist = debounce({ delay: 1500 })\n\nconst applyProgrammaticEditorValue = (value: string): void => {\n // Cancel pending auto-save work so synthetic model updates do not persist stale data.\n debouncedPersist.cleanup()\n editorApi.value?.setValue(value, true)\n}\n\nconst loadDocumentIntoEditor = async () => {\n applyProgrammaticEditorValue(await getDocumentValue())\n isDirty.value = false\n await focusOperation()\n}\n\nconst formatDocument = async () => {\n await editorApi.value?.formatDocument()\n}\n\nconst focusOperation = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n ])\n}\n\nconst persistEditorToWorkspace = async (value: string) => {\n const parsed = parseEditorObject(value, editorLanguage.value)\n if (!parsed) {\n const firstError = diagnostics.value.find(\n (m) => m.severity === monaco.MarkerSeverity.Error,\n )\n if (firstError) {\n focusDiagnostic(firstError)\n }\n await saveLoader.invalidate()\n return\n }\n\n saveLoader.start()\n await workspaceStore.replaceDocument(documentSlug, parsed)\n isDirty.value = false\n await saveLoader.validate({ duration: 900 })\n}\n\nconst saveNow = async () => {\n const value = getEditorValue()\n if (!value) {\n return\n }\n await persistEditorToWorkspace(value)\n}\n\nconst handleEditorChange = (value: string) => {\n isDirty.value = true\n\n if (!isAutoSaveEnabled.value) {\n return\n }\n\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n}\n\nconst focusOperationServers = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n const value = getEditorValue()\n if (!value) {\n return\n }\n\n const parsed = parseEditorObject(value, editorLanguage.value)\n\n if (!parsed || !isObject(parsed.paths)) {\n return\n }\n\n const pathsObject = parsed.paths as Record<string, unknown>\n const operationPathItem = pathsObject[operationContext.path]\n if (!isObject(operationPathItem)) {\n return\n }\n\n const operation = operationPathItem[operationContext.method]\n if (!isObject(operation)) {\n return\n }\n\n // Add default servers if not present\n operation.servers ??= []\n\n // Update the editor value\n editorApi.value?.setValue(stringifyDocument(parsed, editorLanguage.value))\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n 'servers',\n ])\n}\n\nuseJsonPointerLinkSupport(editorApi, currentModel)\n\nonMounted(() => {\n editorApi.value = useEditor({\n element: monacoEditorRef.value ?? document.createElement('div'),\n onChange: handleEditorChange,\n model: currentModel,\n actions: [\n {\n id: 'scalar.editor.focusOperation',\n label: 'Focus Operation',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyO],\n run: async () => {\n await focusOperation()\n },\n },\n {\n id: 'scalar.editor.focusOperationServers',\n label: 'Focus Operation Servers',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyS],\n run: async () => {\n await focusOperationServers()\n },\n },\n {\n id: 'scalar.editor.formatDocument',\n label: 'Format Document',\n keybindings: [\n monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KeyF,\n ],\n run: async () => {\n await formatDocument()\n },\n },\n ],\n })\n\n syncEditorBottomPadding()\n void loadDocumentIntoEditor()\n})\n\nonBeforeUnmount(() => {\n debouncedPersist.cleanup()\n\n // Persist if there is a pending save\n if (isDirty.value && isAutoSaveEnabled.value) {\n void saveNow()\n }\n editorApi.value?.dispose?.()\n // Dispose models created at setup; useEditor only disposes the editor widget, not external models.\n jsonModel.model.dispose()\n yamlModel.model.dispose()\n})\n\nwatch(() => documentSlug, loadDocumentIntoEditor)\n\nwatch(\n () => [path, method] as const,\n async () => {\n await focusOperation()\n },\n)\n\nwatch(isDiagnosticsPaneExpanded, () => {\n syncEditorBottomPadding()\n})\n\nwatch(editorLanguage, async (nextLanguage, previousLanguage) => {\n const wasDirty = isDirty.value\n // Read from the previous model before switching; getEditorValue() would use currentModel\n // which changes with selectedLanguage, so we would read the (empty) new model otherwise.\n const value = getEditorValueForLanguage(previousLanguage ?? 'json')\n if (!value) {\n selectedLanguage.value = nextLanguage\n await nextTick()\n await focusOperation()\n return\n }\n\n const parsed = parseEditorObject(value, previousLanguage ?? 'json')\n selectedLanguage.value = nextLanguage\n await nextTick()\n if (parsed) {\n applyProgrammaticEditorValue(stringifyDocument(parsed, nextLanguage))\n isDirty.value = wasDirty\n }\n await focusOperation()\n})\n\nwatch(\n isAutoSaveEnabled,\n (isEnabled) => {\n if (!isEnabled) {\n debouncedPersist.cleanup()\n return\n }\n\n if (isDirty.value) {\n const value = getEditorValue()\n if (!value) {\n return\n }\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n }\n },\n { flush: 'post' },\n)\n</script>\n\n<template>\n <div\n v-if=\"\n collectionType === 'operation' &&\n getOperationContext(path, method) !== null\n \"\n class=\"flex w-full min-w-0 flex-1 flex-col gap-2\"\n :class=\"editorRootClass\">\n <div\n class=\"grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3\">\n <div\n class=\"flex min-w-0 items-center gap-1 overflow-x-auto whitespace-nowrap\">\n <span class=\"text-c-2 text-xs font-medium whitespace-nowrap\">\n Shortcuts\n </span>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperation\">\n <span>Operation</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"O\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperationServers\">\n <span>Servers</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"S\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n </div>\n\n <div\n aria-label=\"Editor language\"\n class=\"bg-b-1 shadow-border flex items-center justify-self-center overflow-hidden rounded-lg p-0.5\"\n role=\"tablist\">\n <ScalarButton\n :aria-selected=\"!isYamlMode\"\n :class=\"getLanguageToggleClass(!isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'json'\">\n JSON\n </ScalarButton>\n <ScalarButton\n :aria-selected=\"isYamlMode\"\n :class=\"getLanguageToggleClass(isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'yaml'\">\n YAML\n </ScalarButton>\n </div>\n\n <div class=\"flex min-w-0 shrink-0 items-center gap-2 justify-self-end\">\n <ScalarButton\n size=\"xs\"\n variant=\"ghost\"\n @click=\"formatDocument\">\n <span>Format {{ isYamlMode ? 'YAML' : 'JSON' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"F\"\n :modifier=\"['Alt', 'Shift']\" />\n </span>\n </ScalarButton>\n <ScalarButton\n :aria-label=\"\n isEditorMaximized ? 'Restore editor size' : 'Maximize editor'\n \"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"toggleEditorMaximized\">\n <span>{{ isEditorMaximized ? 'Restore' : 'Maximize' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarIconArrowsIn\n v-if=\"isEditorMaximized\"\n class=\"size-3.5\" />\n <ScalarIconArrowsOut\n v-else\n class=\"size-3.5\" />\n </span>\n </ScalarButton>\n </div>\n </div>\n\n <div class=\"flex min-h-0 w-full min-w-0 flex-1 rounded-lg border\">\n <div\n class=\"relative w-full min-w-0 flex-1\"\n :class=\"isEditorMaximized ? 'h-full min-h-0' : 'h-125'\">\n <div class=\"pointer-events-none absolute top-2 right-2 z-10\">\n <EditorSavePanel\n :isAutoSaveEnabled=\"isAutoSaveEnabled\"\n :isDirty=\"isDirty\"\n :saveLoader=\"saveLoader\"\n @saveNow=\"saveNow\"\n @update:isAutoSaveEnabled=\"isAutoSaveEnabled = $event\" />\n </div>\n\n <EditorDiagnosticsPanel\n :diagnosticCounts=\"diagnosticCounts\"\n :expanded=\"isDiagnosticsPaneExpanded\"\n :visibleDiagnostics=\"visibleDiagnostics\"\n @focusDiagnostic=\"focusDiagnostic\"\n @toggle=\"isDiagnosticsPaneExpanded = !isDiagnosticsPaneExpanded\" />\n\n <div\n ref=\"monacoEditorRef\"\n class=\"h-full w-full min-w-0 flex-1 [&_.monaco-editor]:rounded-lg [&_.overflow-guard]:rounded-lg\" />\n </div>\n </div>\n </div>\n <div v-else>No operation context found</div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight) {\n background-color: color-mix(\n in srgb,\n var(--scalar-color-accent, #24b47e) 18%,\n transparent\n );\n border-radius: 4px;\n}\n</style>\n"],"mappings":""}
|
|
@@ -322,7 +322,7 @@ var Editor_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineC
|
|
|
322
322
|
}))])]),
|
|
323
323
|
_: 1
|
|
324
324
|
}, 8, ["aria-label", "onClick"])])
|
|
325
|
-
]), createElementVNode("div", _hoisted_9, [createElementVNode("div", { class: normalizeClass(["relative w-full min-w-0 flex-1", unref(isEditorMaximized) ? "h-full min-h-0" : "h-
|
|
325
|
+
]), createElementVNode("div", _hoisted_9, [createElementVNode("div", { class: normalizeClass(["relative w-full min-w-0 flex-1", unref(isEditorMaximized) ? "h-full min-h-0" : "h-125"]) }, [
|
|
326
326
|
createElementVNode("div", _hoisted_10, [createVNode(EditorSavePanel_default, {
|
|
327
327
|
isAutoSaveEnabled: unref(isAutoSaveEnabled),
|
|
328
328
|
isDirty: unref(isDirty),
|
|
@@ -348,7 +348,7 @@ var Editor_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineC
|
|
|
348
348
|
createElementVNode("div", {
|
|
349
349
|
ref_key: "monacoEditorRef",
|
|
350
350
|
ref: monacoEditorRef,
|
|
351
|
-
class: "h-full w-full min-w-0 flex-1"
|
|
351
|
+
class: "h-full w-full min-w-0 flex-1 [&_.monaco-editor]:rounded-lg [&_.overflow-guard]:rounded-lg"
|
|
352
352
|
}, null, 512)
|
|
353
353
|
], 2)])], 2)) : (openBlock(), createElementBlock("div", _hoisted_11, "No operation context found"));
|
|
354
354
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Editor.vue.script.js","names":[],"sources":["../../../../../../src/v2/features/collection/components/Editor/Editor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton, ScalarHotkey, useLoadingState } from '@scalar/components'\nimport { debounce } from '@scalar/helpers/general/debounce'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { ScalarIconArrowsIn, ScalarIconArrowsOut } from '@scalar/icons'\nimport * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n shallowRef,\n watch,\n} from 'vue'\n\nimport type { CollectionProps } from '@/v2/features/app/helpers/routes'\nimport { useEditor, useJsonPointerLinkSupport } from '@/v2/features/editor'\nimport { createJsonModel } from '@/v2/features/editor/helpers/json/create-json-model'\nimport { createYamlModel } from '@/v2/features/editor/helpers/yaml/create-yaml-model'\nimport { useEditorMarkers } from '@/v2/features/editor/hooks/use-editor-markers'\n\nimport EditorDiagnosticsPanel from './components/EditorDiagnosticsPanel.vue'\nimport EditorSavePanel from './components/EditorSavePanel.vue'\nimport { getDiagnosticCounts } from './helpers/get-diagnostic-counts'\nimport { getOperationContext } from './helpers/get-operation-context'\nimport { getVisibleDiagnostics } from './helpers/get-visible-diagnostics'\nimport { parseEditorObject } from './helpers/parse-editor-object'\nimport { stringifyDocument } from './helpers/stringify-document'\nimport { useEditorState } from './hooks/use-editor-state'\n\nconst { collectionType, documentSlug, method, path, workspaceStore } =\n defineProps<CollectionProps>()\n\nconst MAX_VISIBLE_DIAGNOSTICS = 6\nconst EDITOR_PERSIST_DEBOUNCE_KEY = 'editor:replace-document'\n\nconst monacoEditorRef = ref<HTMLElement>()\nconst editorApi = shallowRef<ReturnType<typeof useEditor>>()\n\nconst saveLoader = useLoadingState()\nconst {\n isAutoSaveEnabled,\n isDirty,\n editorLanguage,\n isDiagnosticsPaneExpanded,\n isEditorMaximized,\n isYamlMode,\n editorBottomPadding,\n editorRootClass,\n getLanguageToggleClass,\n toggleEditorMaximized,\n} = useEditorState()\n\nconst jsonModel = createJsonModel()\nconst yamlModel = createYamlModel()\n\nconst selectedLanguage = ref<'json' | 'yaml'>('json')\n\nconst currentModel = computed(() => {\n return selectedLanguage.value === 'json' ? jsonModel : yamlModel\n})\n\nconst monacoEditorInstance = computed(() => editorApi.value?.editor)\nconst { markers: diagnostics } = useEditorMarkers(monacoEditorInstance)\n\nconst syncEditorBottomPadding = () => {\n editorApi.value?.editor.updateOptions({\n padding: {\n top: 0,\n bottom: editorBottomPadding.value,\n },\n })\n}\n\nconst diagnosticCounts = computed(() => getDiagnosticCounts(diagnostics.value))\nconst visibleDiagnostics = computed(() =>\n getVisibleDiagnostics(diagnostics.value, MAX_VISIBLE_DIAGNOSTICS),\n)\n\nconst focusDiagnostic = (marker: monaco.editor.IMarker) => {\n const editor = editorApi.value?.editor\n if (!editor) {\n return\n }\n editor.setSelection({\n startLineNumber: marker.startLineNumber,\n startColumn: marker.startColumn,\n endLineNumber: marker.endLineNumber,\n endColumn: marker.endColumn,\n })\n editor.revealPositionInCenter({\n lineNumber: marker.startLineNumber,\n column: marker.startColumn,\n })\n}\n\nconst getEditorValue = (): string | null => currentModel.value.model.getValue()\n\n/** Value from the editor for a specific language (use when switching to avoid reading the wrong model). */\nconst getEditorValueForLanguage = (language: 'json' | 'yaml'): string | null =>\n (language === 'json' ? jsonModel : yamlModel).model.getValue()\n\nconst getDocumentValue = async (\n language?: 'json' | 'yaml',\n): Promise<string> => {\n const document = await workspaceStore.getEditableDocument(documentSlug)\n return stringifyDocument(document, language ?? selectedLanguage.value)\n}\n\nconst debouncedPersist = debounce({ delay: 1500 })\n\nconst applyProgrammaticEditorValue = (value: string): void => {\n // Cancel pending auto-save work so synthetic model updates do not persist stale data.\n debouncedPersist.cleanup()\n editorApi.value?.setValue(value, true)\n}\n\nconst loadDocumentIntoEditor = async () => {\n applyProgrammaticEditorValue(await getDocumentValue())\n isDirty.value = false\n await focusOperation()\n}\n\nconst formatDocument = async () => {\n await editorApi.value?.formatDocument()\n}\n\nconst focusOperation = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n ])\n}\n\nconst persistEditorToWorkspace = async (value: string) => {\n const parsed = parseEditorObject(value, editorLanguage.value)\n if (!parsed) {\n const firstError = diagnostics.value.find(\n (m) => m.severity === monaco.MarkerSeverity.Error,\n )\n if (firstError) {\n focusDiagnostic(firstError)\n }\n await saveLoader.invalidate()\n return\n }\n\n saveLoader.start()\n await workspaceStore.replaceDocument(documentSlug, parsed)\n isDirty.value = false\n await saveLoader.validate({ duration: 900 })\n}\n\nconst saveNow = async () => {\n const value = getEditorValue()\n if (!value) {\n return\n }\n await persistEditorToWorkspace(value)\n}\n\nconst handleEditorChange = (value: string) => {\n isDirty.value = true\n\n if (!isAutoSaveEnabled.value) {\n return\n }\n\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n}\n\nconst focusOperationServers = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n const value = getEditorValue()\n if (!value) {\n return\n }\n\n const parsed = parseEditorObject(value, editorLanguage.value)\n\n if (!parsed || !isObject(parsed.paths)) {\n return\n }\n\n const pathsObject = parsed.paths as Record<string, unknown>\n const operationPathItem = pathsObject[operationContext.path]\n if (!isObject(operationPathItem)) {\n return\n }\n\n const operation = operationPathItem[operationContext.method]\n if (!isObject(operation)) {\n return\n }\n\n // Add default servers if not present\n operation.servers ??= []\n\n // Update the editor value\n editorApi.value?.setValue(stringifyDocument(parsed, editorLanguage.value))\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n 'servers',\n ])\n}\n\nuseJsonPointerLinkSupport(editorApi, currentModel)\n\nonMounted(() => {\n editorApi.value = useEditor({\n element: monacoEditorRef.value ?? document.createElement('div'),\n onChange: handleEditorChange,\n model: currentModel,\n actions: [\n {\n id: 'scalar.editor.focusOperation',\n label: 'Focus Operation',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyO],\n run: async () => {\n await focusOperation()\n },\n },\n {\n id: 'scalar.editor.focusOperationServers',\n label: 'Focus Operation Servers',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyS],\n run: async () => {\n await focusOperationServers()\n },\n },\n {\n id: 'scalar.editor.formatDocument',\n label: 'Format Document',\n keybindings: [\n monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KeyF,\n ],\n run: async () => {\n await formatDocument()\n },\n },\n ],\n })\n\n syncEditorBottomPadding()\n void loadDocumentIntoEditor()\n})\n\nonBeforeUnmount(() => {\n debouncedPersist.cleanup()\n\n // Persist if there is a pending save\n if (isDirty.value && isAutoSaveEnabled.value) {\n void saveNow()\n }\n editorApi.value?.dispose?.()\n // Dispose models created at setup; useEditor only disposes the editor widget, not external models.\n jsonModel.model.dispose()\n yamlModel.model.dispose()\n})\n\nwatch(() => documentSlug, loadDocumentIntoEditor)\n\nwatch(\n () => [path, method] as const,\n async () => {\n await focusOperation()\n },\n)\n\nwatch(isDiagnosticsPaneExpanded, () => {\n syncEditorBottomPadding()\n})\n\nwatch(editorLanguage, async (nextLanguage, previousLanguage) => {\n const wasDirty = isDirty.value\n // Read from the previous model before switching; getEditorValue() would use currentModel\n // which changes with selectedLanguage, so we would read the (empty) new model otherwise.\n const value = getEditorValueForLanguage(previousLanguage ?? 'json')\n if (!value) {\n selectedLanguage.value = nextLanguage\n await nextTick()\n await focusOperation()\n return\n }\n\n const parsed = parseEditorObject(value, previousLanguage ?? 'json')\n selectedLanguage.value = nextLanguage\n await nextTick()\n if (parsed) {\n applyProgrammaticEditorValue(stringifyDocument(parsed, nextLanguage))\n isDirty.value = wasDirty\n }\n await focusOperation()\n})\n\nwatch(\n isAutoSaveEnabled,\n (isEnabled) => {\n if (!isEnabled) {\n debouncedPersist.cleanup()\n return\n }\n\n if (isDirty.value) {\n const value = getEditorValue()\n if (!value) {\n return\n }\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n }\n },\n { flush: 'post' },\n)\n</script>\n\n<template>\n <div\n v-if=\"\n collectionType === 'operation' &&\n getOperationContext(path, method) !== null\n \"\n class=\"flex w-full min-w-0 flex-1 flex-col gap-2\"\n :class=\"editorRootClass\">\n <div\n class=\"grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3\">\n <div\n class=\"flex min-w-0 items-center gap-1 overflow-x-auto whitespace-nowrap\">\n <span class=\"text-c-2 text-xs font-medium whitespace-nowrap\">\n Shortcuts\n </span>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperation\">\n <span>Operation</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"O\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperationServers\">\n <span>Servers</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"S\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n </div>\n\n <div\n aria-label=\"Editor language\"\n class=\"bg-b-1 shadow-border flex items-center justify-self-center overflow-hidden rounded-lg p-0.5\"\n role=\"tablist\">\n <ScalarButton\n :aria-selected=\"!isYamlMode\"\n :class=\"getLanguageToggleClass(!isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'json'\">\n JSON\n </ScalarButton>\n <ScalarButton\n :aria-selected=\"isYamlMode\"\n :class=\"getLanguageToggleClass(isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'yaml'\">\n YAML\n </ScalarButton>\n </div>\n\n <div class=\"flex min-w-0 shrink-0 items-center gap-2 justify-self-end\">\n <ScalarButton\n size=\"xs\"\n variant=\"ghost\"\n @click=\"formatDocument\">\n <span>Format {{ isYamlMode ? 'YAML' : 'JSON' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"F\"\n :modifier=\"['Alt', 'Shift']\" />\n </span>\n </ScalarButton>\n <ScalarButton\n :aria-label=\"\n isEditorMaximized ? 'Restore editor size' : 'Maximize editor'\n \"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"toggleEditorMaximized\">\n <span>{{ isEditorMaximized ? 'Restore' : 'Maximize' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarIconArrowsIn\n v-if=\"isEditorMaximized\"\n class=\"size-3.5\" />\n <ScalarIconArrowsOut\n v-else\n class=\"size-3.5\" />\n </span>\n </ScalarButton>\n </div>\n </div>\n\n <div class=\"flex min-h-0 w-full min-w-0 flex-1 rounded-lg border\">\n <div\n class=\"relative w-full min-w-0 flex-1\"\n :class=\"isEditorMaximized ? 'h-full min-h-0' : 'h-[500px]'\">\n <div class=\"pointer-events-none absolute top-2 right-2 z-10\">\n <EditorSavePanel\n :isAutoSaveEnabled=\"isAutoSaveEnabled\"\n :isDirty=\"isDirty\"\n :saveLoader=\"saveLoader\"\n @saveNow=\"saveNow\"\n @update:isAutoSaveEnabled=\"isAutoSaveEnabled = $event\" />\n </div>\n\n <EditorDiagnosticsPanel\n :diagnosticCounts=\"diagnosticCounts\"\n :expanded=\"isDiagnosticsPaneExpanded\"\n :visibleDiagnostics=\"visibleDiagnostics\"\n @focusDiagnostic=\"focusDiagnostic\"\n @toggle=\"isDiagnosticsPaneExpanded = !isDiagnosticsPaneExpanded\" />\n\n <div\n ref=\"monacoEditorRef\"\n class=\"h-full w-full min-w-0 flex-1\" />\n </div>\n </div>\n </div>\n <div v-else>No operation context found</div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight) {\n background-color: color-mix(\n in srgb,\n var(--scalar-color-accent, #24b47e) 18%,\n transparent\n );\n border-radius: 4px;\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Editor.vue.script.js","names":[],"sources":["../../../../../../src/v2/features/collection/components/Editor/Editor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton, ScalarHotkey, useLoadingState } from '@scalar/components'\nimport { debounce } from '@scalar/helpers/general/debounce'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { ScalarIconArrowsIn, ScalarIconArrowsOut } from '@scalar/icons'\nimport * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n shallowRef,\n watch,\n} from 'vue'\n\nimport type { CollectionProps } from '@/v2/features/app/helpers/routes'\nimport { useEditor, useJsonPointerLinkSupport } from '@/v2/features/editor'\nimport { createJsonModel } from '@/v2/features/editor/helpers/json/create-json-model'\nimport { createYamlModel } from '@/v2/features/editor/helpers/yaml/create-yaml-model'\nimport { useEditorMarkers } from '@/v2/features/editor/hooks/use-editor-markers'\n\nimport EditorDiagnosticsPanel from './components/EditorDiagnosticsPanel.vue'\nimport EditorSavePanel from './components/EditorSavePanel.vue'\nimport { getDiagnosticCounts } from './helpers/get-diagnostic-counts'\nimport { getOperationContext } from './helpers/get-operation-context'\nimport { getVisibleDiagnostics } from './helpers/get-visible-diagnostics'\nimport { parseEditorObject } from './helpers/parse-editor-object'\nimport { stringifyDocument } from './helpers/stringify-document'\nimport { useEditorState } from './hooks/use-editor-state'\n\nconst { collectionType, documentSlug, method, path, workspaceStore } =\n defineProps<CollectionProps>()\n\nconst MAX_VISIBLE_DIAGNOSTICS = 6\nconst EDITOR_PERSIST_DEBOUNCE_KEY = 'editor:replace-document'\n\nconst monacoEditorRef = ref<HTMLElement>()\nconst editorApi = shallowRef<ReturnType<typeof useEditor>>()\n\nconst saveLoader = useLoadingState()\nconst {\n isAutoSaveEnabled,\n isDirty,\n editorLanguage,\n isDiagnosticsPaneExpanded,\n isEditorMaximized,\n isYamlMode,\n editorBottomPadding,\n editorRootClass,\n getLanguageToggleClass,\n toggleEditorMaximized,\n} = useEditorState()\n\nconst jsonModel = createJsonModel()\nconst yamlModel = createYamlModel()\n\nconst selectedLanguage = ref<'json' | 'yaml'>('json')\n\nconst currentModel = computed(() => {\n return selectedLanguage.value === 'json' ? jsonModel : yamlModel\n})\n\nconst monacoEditorInstance = computed(() => editorApi.value?.editor)\nconst { markers: diagnostics } = useEditorMarkers(monacoEditorInstance)\n\nconst syncEditorBottomPadding = () => {\n editorApi.value?.editor.updateOptions({\n padding: {\n top: 0,\n bottom: editorBottomPadding.value,\n },\n })\n}\n\nconst diagnosticCounts = computed(() => getDiagnosticCounts(diagnostics.value))\nconst visibleDiagnostics = computed(() =>\n getVisibleDiagnostics(diagnostics.value, MAX_VISIBLE_DIAGNOSTICS),\n)\n\nconst focusDiagnostic = (marker: monaco.editor.IMarker) => {\n const editor = editorApi.value?.editor\n if (!editor) {\n return\n }\n editor.setSelection({\n startLineNumber: marker.startLineNumber,\n startColumn: marker.startColumn,\n endLineNumber: marker.endLineNumber,\n endColumn: marker.endColumn,\n })\n editor.revealPositionInCenter({\n lineNumber: marker.startLineNumber,\n column: marker.startColumn,\n })\n}\n\nconst getEditorValue = (): string | null => currentModel.value.model.getValue()\n\n/** Value from the editor for a specific language (use when switching to avoid reading the wrong model). */\nconst getEditorValueForLanguage = (language: 'json' | 'yaml'): string | null =>\n (language === 'json' ? jsonModel : yamlModel).model.getValue()\n\nconst getDocumentValue = async (\n language?: 'json' | 'yaml',\n): Promise<string> => {\n const document = await workspaceStore.getEditableDocument(documentSlug)\n return stringifyDocument(document, language ?? selectedLanguage.value)\n}\n\nconst debouncedPersist = debounce({ delay: 1500 })\n\nconst applyProgrammaticEditorValue = (value: string): void => {\n // Cancel pending auto-save work so synthetic model updates do not persist stale data.\n debouncedPersist.cleanup()\n editorApi.value?.setValue(value, true)\n}\n\nconst loadDocumentIntoEditor = async () => {\n applyProgrammaticEditorValue(await getDocumentValue())\n isDirty.value = false\n await focusOperation()\n}\n\nconst formatDocument = async () => {\n await editorApi.value?.formatDocument()\n}\n\nconst focusOperation = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n ])\n}\n\nconst persistEditorToWorkspace = async (value: string) => {\n const parsed = parseEditorObject(value, editorLanguage.value)\n if (!parsed) {\n const firstError = diagnostics.value.find(\n (m) => m.severity === monaco.MarkerSeverity.Error,\n )\n if (firstError) {\n focusDiagnostic(firstError)\n }\n await saveLoader.invalidate()\n return\n }\n\n saveLoader.start()\n await workspaceStore.replaceDocument(documentSlug, parsed)\n isDirty.value = false\n await saveLoader.validate({ duration: 900 })\n}\n\nconst saveNow = async () => {\n const value = getEditorValue()\n if (!value) {\n return\n }\n await persistEditorToWorkspace(value)\n}\n\nconst handleEditorChange = (value: string) => {\n isDirty.value = true\n\n if (!isAutoSaveEnabled.value) {\n return\n }\n\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n}\n\nconst focusOperationServers = async () => {\n const operationContext = getOperationContext(path, method)\n if (!operationContext) {\n return\n }\n\n const value = getEditorValue()\n if (!value) {\n return\n }\n\n const parsed = parseEditorObject(value, editorLanguage.value)\n\n if (!parsed || !isObject(parsed.paths)) {\n return\n }\n\n const pathsObject = parsed.paths as Record<string, unknown>\n const operationPathItem = pathsObject[operationContext.path]\n if (!isObject(operationPathItem)) {\n return\n }\n\n const operation = operationPathItem[operationContext.method]\n if (!isObject(operation)) {\n return\n }\n\n // Add default servers if not present\n operation.servers ??= []\n\n // Update the editor value\n editorApi.value?.setValue(stringifyDocument(parsed, editorLanguage.value))\n await editorApi.value?.focusPath([\n 'paths',\n operationContext.path,\n operationContext.method,\n 'servers',\n ])\n}\n\nuseJsonPointerLinkSupport(editorApi, currentModel)\n\nonMounted(() => {\n editorApi.value = useEditor({\n element: monacoEditorRef.value ?? document.createElement('div'),\n onChange: handleEditorChange,\n model: currentModel,\n actions: [\n {\n id: 'scalar.editor.focusOperation',\n label: 'Focus Operation',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyO],\n run: async () => {\n await focusOperation()\n },\n },\n {\n id: 'scalar.editor.focusOperationServers',\n label: 'Focus Operation Servers',\n keybindings: [monaco.KeyMod.Alt | monaco.KeyCode.KeyS],\n run: async () => {\n await focusOperationServers()\n },\n },\n {\n id: 'scalar.editor.formatDocument',\n label: 'Format Document',\n keybindings: [\n monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KeyF,\n ],\n run: async () => {\n await formatDocument()\n },\n },\n ],\n })\n\n syncEditorBottomPadding()\n void loadDocumentIntoEditor()\n})\n\nonBeforeUnmount(() => {\n debouncedPersist.cleanup()\n\n // Persist if there is a pending save\n if (isDirty.value && isAutoSaveEnabled.value) {\n void saveNow()\n }\n editorApi.value?.dispose?.()\n // Dispose models created at setup; useEditor only disposes the editor widget, not external models.\n jsonModel.model.dispose()\n yamlModel.model.dispose()\n})\n\nwatch(() => documentSlug, loadDocumentIntoEditor)\n\nwatch(\n () => [path, method] as const,\n async () => {\n await focusOperation()\n },\n)\n\nwatch(isDiagnosticsPaneExpanded, () => {\n syncEditorBottomPadding()\n})\n\nwatch(editorLanguage, async (nextLanguage, previousLanguage) => {\n const wasDirty = isDirty.value\n // Read from the previous model before switching; getEditorValue() would use currentModel\n // which changes with selectedLanguage, so we would read the (empty) new model otherwise.\n const value = getEditorValueForLanguage(previousLanguage ?? 'json')\n if (!value) {\n selectedLanguage.value = nextLanguage\n await nextTick()\n await focusOperation()\n return\n }\n\n const parsed = parseEditorObject(value, previousLanguage ?? 'json')\n selectedLanguage.value = nextLanguage\n await nextTick()\n if (parsed) {\n applyProgrammaticEditorValue(stringifyDocument(parsed, nextLanguage))\n isDirty.value = wasDirty\n }\n await focusOperation()\n})\n\nwatch(\n isAutoSaveEnabled,\n (isEnabled) => {\n if (!isEnabled) {\n debouncedPersist.cleanup()\n return\n }\n\n if (isDirty.value) {\n const value = getEditorValue()\n if (!value) {\n return\n }\n debouncedPersist.execute(EDITOR_PERSIST_DEBOUNCE_KEY, () =>\n persistEditorToWorkspace(value),\n )\n }\n },\n { flush: 'post' },\n)\n</script>\n\n<template>\n <div\n v-if=\"\n collectionType === 'operation' &&\n getOperationContext(path, method) !== null\n \"\n class=\"flex w-full min-w-0 flex-1 flex-col gap-2\"\n :class=\"editorRootClass\">\n <div\n class=\"grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3\">\n <div\n class=\"flex min-w-0 items-center gap-1 overflow-x-auto whitespace-nowrap\">\n <span class=\"text-c-2 text-xs font-medium whitespace-nowrap\">\n Shortcuts\n </span>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperation\">\n <span>Operation</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"O\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n\n <ScalarButton\n class=\"whitespace-nowrap\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"focusOperationServers\">\n <span>Servers</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"S\"\n :modifier=\"['Alt']\" />\n </span>\n </ScalarButton>\n </div>\n\n <div\n aria-label=\"Editor language\"\n class=\"bg-b-1 shadow-border flex items-center justify-self-center overflow-hidden rounded-lg p-0.5\"\n role=\"tablist\">\n <ScalarButton\n :aria-selected=\"!isYamlMode\"\n :class=\"getLanguageToggleClass(!isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'json'\">\n JSON\n </ScalarButton>\n <ScalarButton\n :aria-selected=\"isYamlMode\"\n :class=\"getLanguageToggleClass(isYamlMode)\"\n role=\"tab\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"editorLanguage = 'yaml'\">\n YAML\n </ScalarButton>\n </div>\n\n <div class=\"flex min-w-0 shrink-0 items-center gap-2 justify-self-end\">\n <ScalarButton\n size=\"xs\"\n variant=\"ghost\"\n @click=\"formatDocument\">\n <span>Format {{ isYamlMode ? 'YAML' : 'JSON' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarHotkey\n hotkey=\"F\"\n :modifier=\"['Alt', 'Shift']\" />\n </span>\n </ScalarButton>\n <ScalarButton\n :aria-label=\"\n isEditorMaximized ? 'Restore editor size' : 'Maximize editor'\n \"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"toggleEditorMaximized\">\n <span>{{ isEditorMaximized ? 'Restore' : 'Maximize' }}</span>\n <span class=\"text-c-3 ml-2 text-[11px]\">\n <ScalarIconArrowsIn\n v-if=\"isEditorMaximized\"\n class=\"size-3.5\" />\n <ScalarIconArrowsOut\n v-else\n class=\"size-3.5\" />\n </span>\n </ScalarButton>\n </div>\n </div>\n\n <div class=\"flex min-h-0 w-full min-w-0 flex-1 rounded-lg border\">\n <div\n class=\"relative w-full min-w-0 flex-1\"\n :class=\"isEditorMaximized ? 'h-full min-h-0' : 'h-125'\">\n <div class=\"pointer-events-none absolute top-2 right-2 z-10\">\n <EditorSavePanel\n :isAutoSaveEnabled=\"isAutoSaveEnabled\"\n :isDirty=\"isDirty\"\n :saveLoader=\"saveLoader\"\n @saveNow=\"saveNow\"\n @update:isAutoSaveEnabled=\"isAutoSaveEnabled = $event\" />\n </div>\n\n <EditorDiagnosticsPanel\n :diagnosticCounts=\"diagnosticCounts\"\n :expanded=\"isDiagnosticsPaneExpanded\"\n :visibleDiagnostics=\"visibleDiagnostics\"\n @focusDiagnostic=\"focusDiagnostic\"\n @toggle=\"isDiagnosticsPaneExpanded = !isDiagnosticsPaneExpanded\" />\n\n <div\n ref=\"monacoEditorRef\"\n class=\"h-full w-full min-w-0 flex-1 [&_.monaco-editor]:rounded-lg [&_.overflow-guard]:rounded-lg\" />\n </div>\n </div>\n </div>\n <div v-else>No operation context found</div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight) {\n background-color: color-mix(\n in srgb,\n var(--scalar-color-accent, #24b47e) 18%,\n transparent\n );\n border-radius: 4px;\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-scalar-theme.js","names":[],"sources":["../../../../../../src/v2/features/editor/helpers/theme/apply-scalar-theme.ts"],"sourcesContent":["import * as monaco from 'monaco-editor'\n\nimport { loadCssVariables } from './load-css-variables'\n\nconst THEME_NAME = 'scalar-theme'\n\n/**\n * Applies a custom Scalar theme to the Monaco editor.\n *\n * This function loads CSS variables for either dark or light mode, maps the colors to Monaco's editor theme keys,\n * and then applies the new theme. Theme color tokens are resolved from CSS custom properties.\n *\n * Example usage:\n *\n * await applyScalarTheme('scalar', true); // Apply dark mode\n * await applyScalarTheme('scalar', false); // Apply light mode\n *\n * @param theme - The theme string key to load variables for (e.g. 'scalar').\n * @param isDarkMode - Whether to use dark or light variables.\n */\nexport const applyScalarTheme = async (theme: string, isDarkMode: boolean) => {\n // Load all CSS variables for the given theme\n const allVars = await loadCssVariables(theme)\n\n // Pick variables for dark or light mode\n const vars = isDarkMode ? allVars.dark : allVars.light\n\n /**\n * Map Monaco editor theme keys to CSS variable names.\n */\n const varsMap = {\n 'editor.background': '--scalar-background-1',\n 'editor.foreground': '--scalar-color-1',\n 'editorLineNumber.foreground': '--scalar-color-3',\n 'editorLineNumber.activeForeground': '--scalar-color-1',\n 'editorLineHighlight.background': '--scalar-background-2',\n 'editorCursor.foreground': '--scalar-color-1',\n 'editorCursor.background': '--scalar-background-1',\n 'editor.selectionBackground': '--scalar-background-3',\n 'editor.inactiveSelectionBackground': '--scalar-background-3',\n 'editorIndentGuide.background': '--scalar-background-3',\n 'editorIndentGuide.activeBackground': '--scalar-background-2',\n 'editorWhitespace.foreground': '--scalar-border-color',\n 'editorBracketMatch.background': '--scalar-background-3',\n 'editorBracketMatch.border': '--scalar-color-accent',\n 'editor.selectionHighlightBackground': '--scalar-background-3',\n 'editor.hoverHighlightBackground': '--scalar-background-3',\n 'editorLink.activeForeground': '--scalar-color-3',\n 'editorOverviewRuler.border': '--scalar-border-color',\n }\n\n /**\n * Build a colors object for Monaco from the variable map.\n * Only assign if the variable exists in the loaded vars.\n */\n const colors = Object.fromEntries(\n Object.entries(varsMap)\n .filter(([_, cssVar]) => !!vars[cssVar])\n .map(([prop, cssVar]) => [prop, vars[cssVar]]),\n )\n\n // Define the theme in Monaco using the chosen variables.\n monaco.editor.defineTheme(THEME_NAME, {\n base: isDarkMode ? 'vs-dark' : 'vs',\n inherit: true,\n rules: [\n // Default text\n { token: '', foreground: vars['--scalar-color-3'] },\n // Comments\n {\n token: 'comment',\n foreground: vars['--scalar-color-2'],\n fontStyle: 'italic',\n },\n // Keywords\n {\n token: 'keyword',\n foreground: vars['--scalar-color-accent'],\n fontStyle: 'bold',\n },\n // Numbers\n { token: 'number', foreground: vars['--scalar-color-purple'] },\n // Strings\n { token: 'string', foreground: vars['--scalar-color-2'] },\n // Delimiters (punctuation)\n { token: 'delimiter', foreground: vars['--scalar-color-3'] },\n ],\n colors,\n })\n\n // Finally, set the theme\n monaco.editor.setTheme(THEME_NAME)\n}\n"],"mappings":";;;AAIA,IAAM,aAAa;;;;;;;;;;;;;;;AAgBnB,IAAa,mBAAmB,OAAO,OAAe,eAAwB;CAE5E,MAAM,UAAU,MAAM,iBAAiB,MAAM;CAG7C,MAAM,OAAO,aAAa,QAAQ,OAAO,QAAQ;;;;;CA8BjD,MAAM,SAAS,OAAO,YACpB,OAAO,QA1BO;EACd,qBAAqB;EACrB,qBAAqB;EACrB,+BAA+B;EAC/B,qCAAqC;EACrC,kCAAkC;EAClC,2BAA2B;EAC3B,2BAA2B;EAC3B,8BAA8B;EAC9B,sCAAsC;EACtC,gCAAgC;EAChC,sCAAsC;EACtC,+BAA+B;EAC/B,iCAAiC;EACjC,6BAA6B;EAC7B,uCAAuC;EACvC,mCAAmC;EACnC,+BAA+B;EAC/B,8BAA8B;EAC/B,CAOwB,CACpB,QAAQ,CAAC,GAAG,YAAY,CAAC,CAAC,KAAK,QAAQ,CACvC,KAAK,CAAC,MAAM,YAAY,CAAC,MAAM,KAAK,
|
|
1
|
+
{"version":3,"file":"apply-scalar-theme.js","names":[],"sources":["../../../../../../src/v2/features/editor/helpers/theme/apply-scalar-theme.ts"],"sourcesContent":["import * as monaco from 'monaco-editor'\n\nimport { loadCssVariables } from './load-css-variables'\n\nconst THEME_NAME = 'scalar-theme'\n\n/**\n * Applies a custom Scalar theme to the Monaco editor.\n *\n * This function loads CSS variables for either dark or light mode, maps the colors to Monaco's editor theme keys,\n * and then applies the new theme. Theme color tokens are resolved from CSS custom properties.\n *\n * Example usage:\n *\n * await applyScalarTheme('scalar', true); // Apply dark mode\n * await applyScalarTheme('scalar', false); // Apply light mode\n *\n * @param theme - The theme string key to load variables for (e.g. 'scalar').\n * @param isDarkMode - Whether to use dark or light variables.\n */\nexport const applyScalarTheme = async (theme: string, isDarkMode: boolean) => {\n // Load all CSS variables for the given theme\n const allVars = await loadCssVariables(theme)\n\n // Pick variables for dark or light mode\n const vars = isDarkMode ? allVars.dark : allVars.light\n\n /**\n * Map Monaco editor theme keys to CSS variable names.\n */\n const varsMap = {\n 'editor.background': '--scalar-background-1',\n 'editor.foreground': '--scalar-color-1',\n 'editorLineNumber.foreground': '--scalar-color-3',\n 'editorLineNumber.activeForeground': '--scalar-color-1',\n 'editorLineHighlight.background': '--scalar-background-2',\n 'editorCursor.foreground': '--scalar-color-1',\n 'editorCursor.background': '--scalar-background-1',\n 'editor.selectionBackground': '--scalar-background-3',\n 'editor.inactiveSelectionBackground': '--scalar-background-3',\n 'editorIndentGuide.background': '--scalar-background-3',\n 'editorIndentGuide.activeBackground': '--scalar-background-2',\n 'editorWhitespace.foreground': '--scalar-border-color',\n 'editorBracketMatch.background': '--scalar-background-3',\n 'editorBracketMatch.border': '--scalar-color-accent',\n 'editor.selectionHighlightBackground': '--scalar-background-3',\n 'editor.hoverHighlightBackground': '--scalar-background-3',\n 'editorLink.activeForeground': '--scalar-color-3',\n 'editorOverviewRuler.border': '--scalar-border-color',\n }\n\n /**\n * Build a colors object for Monaco from the variable map.\n * Only assign if the variable exists in the loaded vars.\n */\n const colors = Object.fromEntries(\n Object.entries(varsMap)\n .filter(([_, cssVar]) => !!vars[cssVar])\n .map(([prop, cssVar]) => [prop, vars[cssVar] as string]),\n )\n\n // Define the theme in Monaco using the chosen variables.\n monaco.editor.defineTheme(THEME_NAME, {\n base: isDarkMode ? 'vs-dark' : 'vs',\n inherit: true,\n rules: [\n // Default text\n { token: '', foreground: vars['--scalar-color-3'] },\n // Comments\n {\n token: 'comment',\n foreground: vars['--scalar-color-2'],\n fontStyle: 'italic',\n },\n // Keywords\n {\n token: 'keyword',\n foreground: vars['--scalar-color-accent'],\n fontStyle: 'bold',\n },\n // Numbers\n { token: 'number', foreground: vars['--scalar-color-purple'] },\n // Strings\n { token: 'string', foreground: vars['--scalar-color-2'] },\n // Delimiters (punctuation)\n { token: 'delimiter', foreground: vars['--scalar-color-3'] },\n ],\n colors,\n })\n\n // Finally, set the theme\n monaco.editor.setTheme(THEME_NAME)\n}\n"],"mappings":";;;AAIA,IAAM,aAAa;;;;;;;;;;;;;;;AAgBnB,IAAa,mBAAmB,OAAO,OAAe,eAAwB;CAE5E,MAAM,UAAU,MAAM,iBAAiB,MAAM;CAG7C,MAAM,OAAO,aAAa,QAAQ,OAAO,QAAQ;;;;;CA8BjD,MAAM,SAAS,OAAO,YACpB,OAAO,QA1BO;EACd,qBAAqB;EACrB,qBAAqB;EACrB,+BAA+B;EAC/B,qCAAqC;EACrC,kCAAkC;EAClC,2BAA2B;EAC3B,2BAA2B;EAC3B,8BAA8B;EAC9B,sCAAsC;EACtC,gCAAgC;EAChC,sCAAsC;EACtC,+BAA+B;EAC/B,iCAAiC;EACjC,6BAA6B;EAC7B,uCAAuC;EACvC,mCAAmC;EACnC,+BAA+B;EAC/B,8BAA8B;EAC/B,CAOwB,CACpB,QAAQ,CAAC,GAAG,YAAY,CAAC,CAAC,KAAK,QAAQ,CACvC,KAAK,CAAC,MAAM,YAAY,CAAC,MAAM,KAAK,QAAkB,CAAC,CAC3D;AAGD,QAAO,OAAO,YAAY,YAAY;EACpC,MAAM,aAAa,YAAY;EAC/B,SAAS;EACT,OAAO;GAEL;IAAE,OAAO;IAAI,YAAY,KAAK;IAAqB;GAEnD;IACE,OAAO;IACP,YAAY,KAAK;IACjB,WAAW;IACZ;GAED;IACE,OAAO;IACP,YAAY,KAAK;IACjB,WAAW;IACZ;GAED;IAAE,OAAO;IAAU,YAAY,KAAK;IAA0B;GAE9D;IAAE,OAAO;IAAU,YAAY,KAAK;IAAqB;GAEzD;IAAE,OAAO;IAAa,YAAY,KAAK;IAAqB;GAC7D;EACD;EACD,CAAC;AAGF,QAAO,OAAO,SAAS,WAAW"}
|
|
@@ -1,14 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps a comma-separated selector list to the theme modes it applies to.
|
|
3
|
+
* Only **exact** `.light-mode` or `.dark-mode` selectors match (no compound selectors like `.light-mode .foo`).
|
|
4
|
+
*/
|
|
5
|
+
export declare const getColorModesFromSelectors: (text: string) => ("dark" | "light")[];
|
|
6
|
+
/**
|
|
7
|
+
* Parses a single custom property value from the stylesheet into a normalized form:
|
|
8
|
+
* - `#RRGGBB` / `#RRGGBBAA` (uppercase)
|
|
9
|
+
* - `#RGB` short hex expanded to six digits
|
|
10
|
+
* - `rgb()` / `rgba()` with comma-separated channels (lower input only)
|
|
11
|
+
* - `var(--x)` / `var(--x, fallback)` returned as-is for a later resolve pass
|
|
12
|
+
*/
|
|
13
|
+
export declare const parseVariableValue: (value: string) => string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Recursively resolves a value if it is (or becomes) `var(--name)` against `variables`.
|
|
16
|
+
* Missing names or non-var values are returned unchanged.
|
|
17
|
+
*/
|
|
18
|
+
export declare const resolveVariableValue: (value: string, variables: Record<string, string>) => string;
|
|
19
|
+
/**
|
|
20
|
+
* Resolves `var(--*)` values in a flat map of custom properties in one pass.
|
|
21
|
+
* Values that are not var references are copied through.
|
|
22
|
+
*/
|
|
23
|
+
export declare const resolveVariables: (variables: Record<string, string>) => Record<string, string>;
|
|
1
24
|
/**
|
|
2
25
|
* Extracts CSS custom properties (variables) from a given CSS string
|
|
3
26
|
* for .light-mode and .dark-mode selectors and returns an object
|
|
4
27
|
* with 'light' and 'dark' keys containing the filtered variables.
|
|
5
|
-
* Only variables matching hex color values (#RRGGBB or #RRGGBBAA) are accepted.
|
|
6
28
|
*
|
|
7
29
|
* @param css - The CSS string to parse.
|
|
8
30
|
* @returns An object with `light` and `dark` properties containing the extracted CSS variables.
|
|
9
31
|
*/
|
|
10
32
|
export declare const loadCssVariables: (css: string) => Promise<{
|
|
11
|
-
light:
|
|
12
|
-
dark:
|
|
33
|
+
light: Record<string, string>;
|
|
34
|
+
dark: Record<string, string>;
|
|
13
35
|
}>;
|
|
14
36
|
//# sourceMappingURL=load-css-variables.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-css-variables.d.ts","sourceRoot":"","sources":["../../../../../../src/v2/features/editor/helpers/theme/load-css-variables.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"load-css-variables.d.ts","sourceRoot":"","sources":["../../../../../../src/v2/features/editor/helpers/theme/load-css-variables.ts"],"names":[],"mappings":"AAoBA;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GAAI,MAAM,MAAM,yBActD,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,uBAmC/C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,EAAE,WAAW,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,MAcvF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAW,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAWzF,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAAU,KAAK,MAAM;;;EAsCjD,CAAA"}
|
|
@@ -1,10 +1,84 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
//#region src/v2/features/editor/helpers/theme/load-css-variables.ts
|
|
3
3
|
/**
|
|
4
|
+
* Patterns for CSS color values we can normalize to hex (or pass through as var()).
|
|
5
|
+
* Space-separated rgb() and modern slash syntax are not supported here.
|
|
6
|
+
*/
|
|
7
|
+
var hexShortRegex = /^#([0-9a-fA-F]){3}$/;
|
|
8
|
+
var hexRegex = /^#([0-9a-fA-F]{6})$/;
|
|
9
|
+
var hexAlphaRegex = /^#([0-9a-fA-F]{8})$/;
|
|
10
|
+
var rgbRegex = /^rgb\(\s*(\d{1,3})\s*,?\s*(\d{1,3})\s*,?\s*(\d{1,3})\s*\)$/;
|
|
11
|
+
var rgbaRegex = /^rgba\(\s*(\d{1,3})\s*,?\s*(\d{1,3})\s*,?\s*(\d{1,3})\s*,?\s*(\d*\.?\d+)\s*\)$/;
|
|
12
|
+
/** Optional fallback: var(--name, fallback) */
|
|
13
|
+
var varRegex = /^var\(\s*(--[^)]+)\s*(?:,\s*([^)]*))?\s*\)$/i;
|
|
14
|
+
/**
|
|
15
|
+
* Maps a comma-separated selector list to the theme modes it applies to.
|
|
16
|
+
* Only **exact** `.light-mode` or `.dark-mode` selectors match (no compound selectors like `.light-mode .foo`).
|
|
17
|
+
*/
|
|
18
|
+
var getColorModesFromSelectors = (text) => {
|
|
19
|
+
return text.split(",").map((selector) => selector.trim()).map((selector) => {
|
|
20
|
+
if (selector === ".light-mode") return "light";
|
|
21
|
+
if (selector === ".dark-mode") return "dark";
|
|
22
|
+
return null;
|
|
23
|
+
}).filter((mode) => mode !== null);
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Parses a single custom property value from the stylesheet into a normalized form:
|
|
27
|
+
* - `#RRGGBB` / `#RRGGBBAA` (uppercase)
|
|
28
|
+
* - `#RGB` short hex expanded to six digits
|
|
29
|
+
* - `rgb()` / `rgba()` with comma-separated channels (lower input only)
|
|
30
|
+
* - `var(--x)` / `var(--x, fallback)` returned as-is for a later resolve pass
|
|
31
|
+
*/
|
|
32
|
+
var parseVariableValue = (value) => {
|
|
33
|
+
const normalized = value.trim().toLowerCase();
|
|
34
|
+
const hexValue = z.union([z.string().regex(hexRegex), z.string().regex(hexAlphaRegex)]).safeParse(normalized);
|
|
35
|
+
if (!hexValue.error) return hexValue.data.toUpperCase();
|
|
36
|
+
const shortHexValue = z.string().regex(hexShortRegex).safeParse(normalized);
|
|
37
|
+
if (!shortHexValue.error) {
|
|
38
|
+
const [_, r, g, b] = shortHexValue.data.toUpperCase();
|
|
39
|
+
return `#${r}${r}${g}${g}${b}${b}`;
|
|
40
|
+
}
|
|
41
|
+
const rgbValue = z.union([z.string().regex(rgbRegex), z.string().regex(rgbaRegex)]).safeParse(normalized);
|
|
42
|
+
if (!rgbValue.error) {
|
|
43
|
+
const [_, r = "0", g = "0", b = "0", a = "1"] = rgbValue.data.startsWith("rgba") ? rgbaRegex.exec(rgbValue.data) ?? [] : rgbRegex.exec(rgbValue.data) ?? [];
|
|
44
|
+
const toHex = (v) => Number.parseInt(v, 10).toString(16).padStart(2, "0").toUpperCase();
|
|
45
|
+
const alpha = Math.round(Number.parseFloat(a) * 255);
|
|
46
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}${alpha === 255 ? "" : toHex(String(alpha))}`;
|
|
47
|
+
}
|
|
48
|
+
const varValue = z.string().regex(varRegex).safeParse(normalized);
|
|
49
|
+
if (!varValue.error) return varValue.data;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Recursively resolves a value if it is (or becomes) `var(--name)` against `variables`.
|
|
53
|
+
* Missing names or non-var values are returned unchanged.
|
|
54
|
+
*/
|
|
55
|
+
var resolveVariableValue = (value, variables) => {
|
|
56
|
+
const varValue = z.string().regex(varRegex).safeParse(value);
|
|
57
|
+
if (!varValue.error) {
|
|
58
|
+
const [_, varName] = varRegex.exec(varValue.data) ?? [];
|
|
59
|
+
if (!varName) return value;
|
|
60
|
+
const resolved = variables[varName];
|
|
61
|
+
if (!resolved) return value;
|
|
62
|
+
return resolveVariableValue(resolved, variables);
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Resolves `var(--*)` values in a flat map of custom properties in one pass.
|
|
68
|
+
* Values that are not var references are copied through.
|
|
69
|
+
*/
|
|
70
|
+
var resolveVariables = (variables) => {
|
|
71
|
+
const resolved = Object.entries(variables).map(([name, value]) => {
|
|
72
|
+
const varValue = z.string().regex(varRegex).safeParse(value);
|
|
73
|
+
if (!varValue.error) return [name, resolveVariableValue(varValue.data, variables)];
|
|
74
|
+
return [name, value];
|
|
75
|
+
});
|
|
76
|
+
return Object.fromEntries(resolved);
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
4
79
|
* Extracts CSS custom properties (variables) from a given CSS string
|
|
5
80
|
* for .light-mode and .dark-mode selectors and returns an object
|
|
6
81
|
* with 'light' and 'dark' keys containing the filtered variables.
|
|
7
|
-
* Only variables matching hex color values (#RRGGBB or #RRGGBBAA) are accepted.
|
|
8
82
|
*
|
|
9
83
|
* @param css - The CSS string to parse.
|
|
10
84
|
* @returns An object with `light` and `dark` properties containing the extracted CSS variables.
|
|
@@ -12,28 +86,30 @@ import { z } from "zod";
|
|
|
12
86
|
var loadCssVariables = async (css) => {
|
|
13
87
|
const sheet = new CSSStyleSheet();
|
|
14
88
|
await sheet.replace(css);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
89
|
+
const parsed = Array.from(sheet.cssRules).filter((cssRule) => cssRule instanceof CSSStyleRule).reduce((variables, cssRule) => {
|
|
90
|
+
const colorModes = getColorModesFromSelectors(cssRule.selectorText);
|
|
91
|
+
if (!colorModes.length) return variables;
|
|
92
|
+
const styles = Array.from(cssRule.style).reduce((style, name) => {
|
|
18
93
|
if (!name.startsWith("--")) return style;
|
|
19
|
-
const
|
|
20
|
-
if (
|
|
21
|
-
style[name] = value.data;
|
|
94
|
+
const parsedValue = parseVariableValue(cssRule.style.getPropertyValue(name));
|
|
95
|
+
if (parsedValue) style[name] = parsedValue;
|
|
22
96
|
return style;
|
|
23
97
|
}, {});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
};
|
|
32
|
-
return acc;
|
|
98
|
+
colorModes.forEach((colorMode) => {
|
|
99
|
+
variables[colorMode] = {
|
|
100
|
+
...variables[colorMode],
|
|
101
|
+
...styles
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
return variables;
|
|
33
105
|
}, {
|
|
34
106
|
light: {},
|
|
35
107
|
dark: {}
|
|
36
108
|
});
|
|
109
|
+
return {
|
|
110
|
+
light: resolveVariables(parsed.light),
|
|
111
|
+
dark: resolveVariables(parsed.dark)
|
|
112
|
+
};
|
|
37
113
|
};
|
|
38
114
|
//#endregion
|
|
39
115
|
export { loadCssVariables };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-css-variables.js","names":[],"sources":["../../../../../../src/v2/features/editor/helpers/theme/load-css-variables.ts"],"sourcesContent":["import { z } from 'zod'\n\n/**\n * Extracts CSS custom properties (variables) from a given CSS string\n * for .light-mode and .dark-mode selectors and returns an object\n * with 'light' and 'dark' keys containing the filtered variables.\n
|
|
1
|
+
{"version":3,"file":"load-css-variables.js","names":[],"sources":["../../../../../../src/v2/features/editor/helpers/theme/load-css-variables.ts"],"sourcesContent":["import { z } from 'zod'\n\n/** Which theme bucket a CSS rule applies to after parsing. */\ntype ColorMode = 'light' | 'dark'\n\n/** Accumulated custom properties per mode while walking stylesheet rules. */\ntype Variables = Record<ColorMode, Record<string, string>>\n\n/**\n * Patterns for CSS color values we can normalize to hex (or pass through as var()).\n * Space-separated rgb() and modern slash syntax are not supported here.\n */\nconst hexShortRegex = /^#([0-9a-fA-F]){3}$/\nconst hexRegex = /^#([0-9a-fA-F]{6})$/\nconst hexAlphaRegex = /^#([0-9a-fA-F]{8})$/\nconst rgbRegex = /^rgb\\(\\s*(\\d{1,3})\\s*,?\\s*(\\d{1,3})\\s*,?\\s*(\\d{1,3})\\s*\\)$/\nconst rgbaRegex = /^rgba\\(\\s*(\\d{1,3})\\s*,?\\s*(\\d{1,3})\\s*,?\\s*(\\d{1,3})\\s*,?\\s*(\\d*\\.?\\d+)\\s*\\)$/\n/** Optional fallback: var(--name, fallback) */\nconst varRegex = /^var\\(\\s*(--[^)]+)\\s*(?:,\\s*([^)]*))?\\s*\\)$/i\n\n/**\n * Maps a comma-separated selector list to the theme modes it applies to.\n * Only **exact** `.light-mode` or `.dark-mode` selectors match (no compound selectors like `.light-mode .foo`).\n */\nexport const getColorModesFromSelectors = (text: string) => {\n const selectors = text.split(',').map((selector) => selector.trim())\n\n return selectors\n .map((selector) => {\n if (selector === '.light-mode') {\n return 'light'\n }\n if (selector === '.dark-mode') {\n return 'dark'\n }\n return null\n })\n .filter((mode) => mode !== null)\n}\n\n/**\n * Parses a single custom property value from the stylesheet into a normalized form:\n * - `#RRGGBB` / `#RRGGBBAA` (uppercase)\n * - `#RGB` short hex expanded to six digits\n * - `rgb()` / `rgba()` with comma-separated channels (lower input only)\n * - `var(--x)` / `var(--x, fallback)` returned as-is for a later resolve pass\n */\nexport const parseVariableValue = (value: string) => {\n const normalized = value.trim().toLowerCase()\n\n const hexValue = z.union([z.string().regex(hexRegex), z.string().regex(hexAlphaRegex)]).safeParse(normalized)\n\n if (!hexValue.error) {\n return hexValue.data.toUpperCase()\n }\n\n const shortHexValue = z.string().regex(hexShortRegex).safeParse(normalized)\n\n if (!shortHexValue.error) {\n const [_, r, g, b] = shortHexValue.data.toUpperCase()\n return `#${r}${r}${g}${g}${b}${b}`\n }\n\n const rgbValue = z.union([z.string().regex(rgbRegex), z.string().regex(rgbaRegex)]).safeParse(normalized)\n\n if (!rgbValue.error) {\n const [_, r = '0', g = '0', b = '0', a = '1'] = rgbValue.data.startsWith('rgba')\n ? (rgbaRegex.exec(rgbValue.data) ?? [])\n : (rgbRegex.exec(rgbValue.data) ?? [])\n\n const toHex = (v: string) => Number.parseInt(v, 10).toString(16).padStart(2, '0').toUpperCase()\n const alpha = Math.round(Number.parseFloat(a) * 255)\n\n return `#${toHex(r)}${toHex(g)}${toHex(b)}${alpha === 255 ? '' : toHex(String(alpha))}`\n }\n\n const varValue = z.string().regex(varRegex).safeParse(normalized)\n if (!varValue.error) {\n return varValue.data\n }\n\n return undefined\n}\n\n/**\n * Recursively resolves a value if it is (or becomes) `var(--name)` against `variables`.\n * Missing names or non-var values are returned unchanged.\n */\nexport const resolveVariableValue = (value: string, variables: Record<string, string>): string => {\n const varValue = z.string().regex(varRegex).safeParse(value)\n if (!varValue.error) {\n const [_, varName] = varRegex.exec(varValue.data) ?? []\n if (!varName) {\n return value\n }\n const resolved = variables[varName]\n if (!resolved) {\n return value\n }\n return resolveVariableValue(resolved, variables)\n }\n return value\n}\n\n/**\n * Resolves `var(--*)` values in a flat map of custom properties in one pass.\n * Values that are not var references are copied through.\n */\nexport const resolveVariables = (variables: Record<string, string>): Record<string, string> => {\n const entries = Object.entries(variables)\n\n const resolved = entries.map(([name, value]) => {\n const varValue = z.string().regex(varRegex).safeParse(value)\n if (!varValue.error) {\n return [name, resolveVariableValue(varValue.data, variables)]\n }\n return [name, value]\n })\n return Object.fromEntries(resolved)\n}\n\n/**\n * Extracts CSS custom properties (variables) from a given CSS string\n * for .light-mode and .dark-mode selectors and returns an object\n * with 'light' and 'dark' keys containing the filtered variables.\n *\n * @param css - The CSS string to parse.\n * @returns An object with `light` and `dark` properties containing the extracted CSS variables.\n */\nexport const loadCssVariables = async (css: string) => {\n const sheet = new CSSStyleSheet()\n await sheet.replace(css)\n\n const cssRules = Array.from(sheet.cssRules).filter((cssRule) => cssRule instanceof CSSStyleRule)\n const parsed = cssRules.reduce<Variables>(\n (variables, cssRule) => {\n const colorModes = getColorModesFromSelectors(cssRule.selectorText)\n if (!colorModes.length) {\n return variables\n }\n\n // Collect valid CSS variable declarations from the rule's style\n const styles = Array.from(cssRule.style).reduce<Record<string, string>>((style, name) => {\n if (!name.startsWith('--')) {\n return style\n }\n const value = cssRule.style.getPropertyValue(name)\n const parsedValue = parseVariableValue(value)\n if (parsedValue) {\n style[name] = parsedValue\n }\n return style\n }, {})\n\n colorModes.forEach((colorMode) => {\n variables[colorMode] = { ...variables[colorMode], ...styles }\n })\n\n return variables\n },\n { light: {}, dark: {} },\n )\n\n return {\n light: resolveVariables(parsed.light),\n dark: resolveVariables(parsed.dark),\n }\n}\n"],"mappings":"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"}
|
package/dist/v2/posthog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"posthog.d.ts","sourceRoot":"","sources":["../../src/v2/posthog.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,GAAG,EAAS,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"posthog.d.ts","sourceRoot":"","sources":["../../src/v2/posthog.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,GAAG,EAAS,MAAM,KAAK,CAAA;AAarC,eAAO,MAAM,UAAU,GAAI,SAAS,GAAG,CAAC,OAAO,CAAC,SAY/C,CAAA"}
|
package/dist/v2/posthog.js
CHANGED
|
@@ -7,6 +7,7 @@ var posthog = ph.init("phc_3elIjSOvGOo5aEwg6krzIY9IcQiRubsBtglOXsQ4Uu4", {
|
|
|
7
7
|
defaults: "2025-11-30",
|
|
8
8
|
opt_out_capturing_by_default: true
|
|
9
9
|
});
|
|
10
|
+
posthog.register({ product: "api-client" });
|
|
10
11
|
var usePosthog = (enabled) => {
|
|
11
12
|
watch(enabled, (value) => {
|
|
12
13
|
if (value) posthog.opt_in_capturing();
|
package/dist/v2/posthog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"posthog.js","names":[],"sources":["../../src/v2/posthog.ts"],"sourcesContent":["import ph from 'posthog-js'\nimport { type Ref, watch } from 'vue'\n\nconst posthog = ph.init('phc_3elIjSOvGOo5aEwg6krzIY9IcQiRubsBtglOXsQ4Uu4', {\n api_host: 'https://magic.scalar.com',\n ui_host: 'https://us.posthog.com',\n defaults: '2025-11-30',\n opt_out_capturing_by_default: true,\n})\n\nexport const usePosthog = (enabled: Ref<boolean>) => {\n watch(\n enabled,\n (value) => {\n if (value) {\n posthog.opt_in_capturing()\n } else {\n posthog.opt_out_capturing()\n }\n },\n { immediate: true },\n )\n}\n"],"mappings":";;;AAGA,IAAM,UAAU,GAAG,KAAK,mDAAmD;CACzE,UAAU;CACV,SAAS;CACT,UAAU;CACV,8BAA8B;CAC/B,CAAC;AAEF,IAAa,cAAc,YAA0B;AACnD,OACE,UACC,UAAU;AACT,MAAI,MACF,SAAQ,kBAAkB;MAE1B,SAAQ,mBAAmB;IAG/B,EAAE,WAAW,MAAM,CACpB"}
|
|
1
|
+
{"version":3,"file":"posthog.js","names":[],"sources":["../../src/v2/posthog.ts"],"sourcesContent":["import ph from 'posthog-js'\nimport { type Ref, watch } from 'vue'\n\nconst posthog = ph.init('phc_3elIjSOvGOo5aEwg6krzIY9IcQiRubsBtglOXsQ4Uu4', {\n api_host: 'https://magic.scalar.com',\n ui_host: 'https://us.posthog.com',\n defaults: '2025-11-30',\n opt_out_capturing_by_default: true,\n})\n\nposthog.register({\n product: 'api-client',\n})\n\nexport const usePosthog = (enabled: Ref<boolean>) => {\n watch(\n enabled,\n (value) => {\n if (value) {\n posthog.opt_in_capturing()\n } else {\n posthog.opt_out_capturing()\n }\n },\n { immediate: true },\n )\n}\n"],"mappings":";;;AAGA,IAAM,UAAU,GAAG,KAAK,mDAAmD;CACzE,UAAU;CACV,SAAS;CACT,UAAU;CACV,8BAA8B;CAC/B,CAAC;AAEF,QAAQ,SAAS,EACf,SAAS,cACV,CAAC;AAEF,IAAa,cAAc,YAA0B;AACnD,OACE,UACC,UAAU;AACT,MAAI,MACF,SAAQ,kBAAkB;MAE1B,SAAQ,mBAAmB;IAG/B,EAAE,WAAW,MAAM,CACpB"}
|
|
@@ -45,7 +45,7 @@ var ResponseEmpty_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */
|
|
|
45
45
|
const handleHotKey = (event) => {
|
|
46
46
|
if (event?.createNew && route.name === "request") addRequest();
|
|
47
47
|
};
|
|
48
|
-
const packageVersion = "2.39.
|
|
48
|
+
const packageVersion = "2.39.3";
|
|
49
49
|
onMounted(() => events.hotKeys.on(handleHotKey));
|
|
50
50
|
onBeforeUnmount(() => events.hotKeys.off(handleHotKey));
|
|
51
51
|
return (_ctx, _cache) => {
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"rest",
|
|
19
19
|
"testing"
|
|
20
20
|
],
|
|
21
|
-
"version": "2.39.
|
|
21
|
+
"version": "2.39.3",
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": ">=22"
|
|
24
24
|
},
|
|
@@ -349,22 +349,22 @@
|
|
|
349
349
|
"zod": "^4.3.5",
|
|
350
350
|
"@scalar/components": "0.21.1",
|
|
351
351
|
"@scalar/draggable": "0.4.1",
|
|
352
|
-
"@scalar/icons": "0.7.0",
|
|
353
|
-
"@scalar/helpers": "0.4.2",
|
|
354
352
|
"@scalar/import": "0.5.3",
|
|
355
|
-
"@scalar/
|
|
353
|
+
"@scalar/helpers": "0.4.2",
|
|
356
354
|
"@scalar/json-magic": "0.12.4",
|
|
357
|
-
"@scalar/
|
|
355
|
+
"@scalar/icons": "0.7.0",
|
|
356
|
+
"@scalar/oas-utils": "0.10.13",
|
|
358
357
|
"@scalar/openapi-types": "0.6.1",
|
|
358
|
+
"@scalar/object-utils": "1.3.3",
|
|
359
359
|
"@scalar/postman-to-openapi": "0.6.0",
|
|
360
|
-
"@scalar/sidebar": "0.8.
|
|
360
|
+
"@scalar/sidebar": "0.8.15",
|
|
361
361
|
"@scalar/snippetz": "0.7.7",
|
|
362
362
|
"@scalar/themes": "0.15.1",
|
|
363
363
|
"@scalar/types": "0.7.5",
|
|
364
|
+
"@scalar/use-codemirror": "0.14.10",
|
|
364
365
|
"@scalar/use-hooks": "0.4.1",
|
|
365
|
-
"@scalar/
|
|
366
|
-
"@scalar/
|
|
367
|
-
"@scalar/use-codemirror": "0.14.10"
|
|
366
|
+
"@scalar/workspace-store": "0.41.2",
|
|
367
|
+
"@scalar/use-toasts": "0.10.1"
|
|
368
368
|
},
|
|
369
369
|
"devDependencies": {
|
|
370
370
|
"@tailwindcss/vite": "^4.2.0",
|
|
@@ -379,8 +379,8 @@
|
|
|
379
379
|
"vite": "8.0.0",
|
|
380
380
|
"vite-svg-loader": "5.1.1",
|
|
381
381
|
"vitest": "4.1.0",
|
|
382
|
-
"@scalar/
|
|
383
|
-
"@scalar/
|
|
382
|
+
"@scalar/galaxy": "0.6.1",
|
|
383
|
+
"@scalar/pre-post-request-scripts": "0.3.15"
|
|
384
384
|
},
|
|
385
385
|
"scripts": {
|
|
386
386
|
"build": "vite build && vue-tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|