vuepress-plugin-md-power 1.0.0-rc.100

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +239 -0
  3. package/lib/client/components/Bilibili.vue +45 -0
  4. package/lib/client/components/CanIUse.vue +94 -0
  5. package/lib/client/components/CodeEditor.vue +146 -0
  6. package/lib/client/components/CodeRepl.vue +242 -0
  7. package/lib/client/components/CodeSandbox.vue +75 -0
  8. package/lib/client/components/FileTreeItem.vue +169 -0
  9. package/lib/client/components/IconClose.vue +8 -0
  10. package/lib/client/components/IconConsole.vue +8 -0
  11. package/lib/client/components/IconRun.vue +8 -0
  12. package/lib/client/components/Loading.vue +44 -0
  13. package/lib/client/components/PDFViewer.vue +39 -0
  14. package/lib/client/components/Plot.vue +97 -0
  15. package/lib/client/components/Replit.vue +58 -0
  16. package/lib/client/components/Youtube.vue +41 -0
  17. package/lib/client/composables/codeRepl.d.ts +22 -0
  18. package/lib/client/composables/codeRepl.js +266 -0
  19. package/lib/client/composables/pdf.d.ts +19 -0
  20. package/lib/client/composables/pdf.js +59 -0
  21. package/lib/client/composables/rustRepl.d.ts +10 -0
  22. package/lib/client/composables/rustRepl.js +104 -0
  23. package/lib/client/composables/size.d.ts +26 -0
  24. package/lib/client/composables/size.js +37 -0
  25. package/lib/client/index.d.ts +1 -0
  26. package/lib/client/index.js +2 -0
  27. package/lib/client/options.d.ts +5 -0
  28. package/lib/client/options.js +5 -0
  29. package/lib/client/shim.d.ts +7 -0
  30. package/lib/client/utils/http.d.ts +6 -0
  31. package/lib/client/utils/http.js +25 -0
  32. package/lib/client/utils/is.d.ts +5 -0
  33. package/lib/client/utils/is.js +19 -0
  34. package/lib/client/utils/link.d.ts +3 -0
  35. package/lib/client/utils/link.js +9 -0
  36. package/lib/client/utils/sleep.d.ts +3 -0
  37. package/lib/client/utils/sleep.js +9 -0
  38. package/lib/node/index.d.ts +287 -0
  39. package/lib/node/index.js +1688 -0
  40. package/lib/node/markdown-it-container.d.ts +6 -0
  41. package/lib/shared/index.d.ts +277 -0
  42. package/lib/shared/index.js +0 -0
  43. package/package.json +65 -0
@@ -0,0 +1,97 @@
1
+ <script setup lang="ts">
2
+ import { onClickOutside, useMediaQuery } from '@vueuse/core'
3
+ import { computed, ref, shallowRef } from 'vue'
4
+ import { usePageFrontmatter } from 'vuepress/client'
5
+ import { pluginOptions } from '../options.js'
6
+ import type { PlotOptions } from '../../shared/index.js'
7
+
8
+ const props = defineProps<Omit<PlotOptions, 'tag'>>()
9
+
10
+ const matter = usePageFrontmatter()
11
+
12
+ const options = computed(() => {
13
+ const plot = typeof pluginOptions.plot === 'object' ? pluginOptions.plot : {}
14
+
15
+ return {
16
+ trigger: props.trigger || matter.value.plotTrigger || plot.trigger || 'hover',
17
+ color: props.color || plot.color,
18
+ mask: props.mask || plot.mask,
19
+ }
20
+ })
21
+
22
+ const styles = computed(() => {
23
+ const plot = options.value
24
+ if (!plot.color && !plot.mask)
25
+ return {}
26
+ const style: Record<string, string> = {}
27
+ if (plot.color) {
28
+ if (typeof plot.color === 'string') {
29
+ style['--vp-c-plot-light'] = plot.color
30
+ }
31
+ else {
32
+ style['--vp-c-plot-light'] = plot.color.light
33
+ style['--vp-c-plot-dark'] = plot.color.dark
34
+ }
35
+ }
36
+ if (plot.mask) {
37
+ if (typeof plot.mask === 'string') {
38
+ style['--vp-c-bg-plot-light'] = plot.mask
39
+ }
40
+ else {
41
+ style['--vp-c-bg-plot-light'] = plot.mask.light
42
+ style['--vp-c-bg-plot-dark'] = plot.mask.dark
43
+ }
44
+ }
45
+ return style
46
+ })
47
+
48
+ const isMobile = useMediaQuery('(max-width: 768px)')
49
+ const active = ref(false)
50
+ const el = shallowRef<HTMLElement>()
51
+
52
+ onClickOutside(el, () => {
53
+ if (options.value.trigger === 'click' || isMobile.value)
54
+ active.value = false
55
+ })
56
+
57
+ function onClick() {
58
+ if (props.trigger === 'click' || isMobile.value)
59
+ active.value = !active.value
60
+ }
61
+ </script>
62
+
63
+ <template>
64
+ <span
65
+ ref="el"
66
+ class="vp-plot"
67
+ :class="{ hover: options.trigger !== 'click', active }"
68
+ :style="styles"
69
+ @click="onClick"
70
+ >
71
+ <slot />
72
+ </span>
73
+ </template>
74
+
75
+ <style>
76
+ .vp-plot {
77
+ padding-right: 2px;
78
+ padding-left: 2px;
79
+ color: transparent;
80
+ background-color: var(--vp-c-bg-plot-light, #000);
81
+ transition: color ease 0.25s, background-color ease 0.25s;
82
+ }
83
+
84
+ .dark .vp-plot {
85
+ background-color: var(--vp-c-bg-plot-dark, #fff);
86
+ }
87
+
88
+ .vp-plot.hover:hover,
89
+ .vp-plot.active {
90
+ color: var(--vp-c-plot-light, #fff);
91
+ }
92
+
93
+ .dark .vp-plot.hover:hover,
94
+ .dark .vp-plot.active {
95
+ color: var(--vp-c-plot-dark, #000);
96
+ }
97
+ </style>
@@ -0,0 +1,58 @@
1
+ <script setup lang="ts">
2
+ import { computed, getCurrentInstance, ref } from 'vue'
3
+ import Loading from './Loading.vue'
4
+ import type { ReplitTokenMeta } from '../../shared/index.js'
5
+
6
+ const props = defineProps<ReplitTokenMeta>()
7
+
8
+ const current = getCurrentInstance()
9
+ // magic height
10
+ const height = ref('47px')
11
+ const loaded = ref(false)
12
+
13
+ const REPLIT_LINK = 'https://replit.com/'
14
+
15
+ const isDark = computed(() => current?.appContext.config.globalProperties.$isDark.value)
16
+
17
+ const link = computed(() => {
18
+ const url = new URL(`/${props.source}`, REPLIT_LINK)
19
+ url.searchParams.set('embed', 'true')
20
+
21
+ const theme = props.theme || (isDark.value ? 'dark' : 'light')
22
+ url.searchParams.set('theme', theme)
23
+
24
+ return url.toString()
25
+ })
26
+
27
+ function onload() {
28
+ loaded.value = true
29
+ height.value = props.height || '450px'
30
+ }
31
+ </script>
32
+
33
+ <template>
34
+ <ClientOnly>
35
+ <iframe
36
+ class="replit-iframe-wrapper"
37
+ :src="link"
38
+ :title="title || 'Replit'"
39
+ :style="{ width, height }"
40
+ allowtransparency="true"
41
+ allowfullscree="true"
42
+ @load="onload"
43
+ />
44
+ <Loading v-if="!loaded" />
45
+ </ClientOnly>
46
+ </template>
47
+
48
+ <style>
49
+ .replit-iframe-wrapper {
50
+ width: 100%;
51
+ margin: 16px auto;
52
+ border: none;
53
+ border-top: 1px solid var(--vp-c-divider, #e2e2e3);
54
+ border-bottom-right-radius: 8px;
55
+ border-bottom-left-radius: 8px;
56
+ transition: border 0.25s;
57
+ }
58
+ </style>
@@ -0,0 +1,41 @@
1
+ <script setup lang="ts">
2
+ import { toRefs } from 'vue'
3
+ import { useSize } from '../composables/size.js'
4
+
5
+ const props = defineProps<{
6
+ src: string
7
+ title: string
8
+ width?: string
9
+ height?: string
10
+ ratio?: string
11
+ }>()
12
+
13
+ const IFRAME_ALLOW = 'accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture'
14
+
15
+ const options = toRefs(props)
16
+
17
+ const { el, width, height, resize } = useSize(options)
18
+ </script>
19
+
20
+ <template>
21
+ <ClientOnly>
22
+ <iframe
23
+ ref="el"
24
+ class="video-youtube-iframe"
25
+ :src="src"
26
+ :title="title || 'Youtube'"
27
+ :style="{ width, height }"
28
+ :allow="IFRAME_ALLOW"
29
+ @load="resize"
30
+ />
31
+ </ClientOnly>
32
+ </template>
33
+
34
+ <style>
35
+ .video-youtube-iframe {
36
+ width: 100%;
37
+ margin: 16px auto;
38
+ border: none;
39
+ border-radius: 5px;
40
+ }
41
+ </style>
@@ -0,0 +1,22 @@
1
+ import { Ref } from 'vue';
2
+
3
+ type Lang = 'kotlin' | 'go' | 'rust';
4
+ declare function resolveCode(el: HTMLElement): string;
5
+ declare function resolveCodeInfo(el: HTMLDivElement): {
6
+ lang: Lang;
7
+ code: string;
8
+ };
9
+ declare function useCodeRepl(el: Ref<HTMLDivElement | null>): {
10
+ onRunCode: () => Promise<void>;
11
+ onCleanRun: () => void;
12
+ lang: Ref<Lang | undefined, Lang | undefined>;
13
+ backendVersion: Ref<string, string>;
14
+ firstRun: Ref<boolean, boolean>;
15
+ stderr: Ref<string[], string[]>;
16
+ stdout: Ref<string[], string[]>;
17
+ loaded: Ref<boolean, boolean>;
18
+ finished: Ref<boolean, boolean>;
19
+ error: Ref<string, string>;
20
+ };
21
+
22
+ export { resolveCode, resolveCodeInfo, useCodeRepl };
@@ -0,0 +1,266 @@
1
+ // src/client/composables/codeRepl.ts
2
+ import { onMounted, ref } from "vue";
3
+ import { http } from "../utils/http.js";
4
+ import { sleep } from "../utils/sleep.js";
5
+
6
+ // src/client/composables/rustRepl.ts
7
+ import { tryOnScopeDispose } from "@vueuse/core";
8
+ var wsUrl = "wss://play.rust-lang.org/websocket";
9
+ var payloadType = {
10
+ connected: "websocket/connected",
11
+ request: "output/execute/wsExecuteRequest",
12
+ execute: {
13
+ begin: "output/execute/wsExecuteBegin",
14
+ // status: 'output/execute/wsExecuteStatus',
15
+ stderr: "output/execute/wsExecuteStderr",
16
+ stdout: "output/execute/wsExecuteStdout",
17
+ end: "output/execute/wsExecuteEnd"
18
+ }
19
+ };
20
+ var ws = null;
21
+ var isOpen = false;
22
+ var uuid = 0;
23
+ function connect() {
24
+ if (isOpen)
25
+ return Promise.resolve();
26
+ ws = new WebSocket(wsUrl);
27
+ uuid = 0;
28
+ ws.addEventListener("open", () => {
29
+ isOpen = true;
30
+ send(
31
+ payloadType.connected,
32
+ { iAcceptThisIsAnUnsupportedApi: true },
33
+ { websocket: true, sequenceNumber: uuid }
34
+ );
35
+ });
36
+ ws.addEventListener("close", () => {
37
+ isOpen = false;
38
+ ws = null;
39
+ });
40
+ tryOnScopeDispose(() => ws?.close());
41
+ return new Promise((resolve) => {
42
+ function connected(e) {
43
+ const data = JSON.parse(e.data);
44
+ if (data.type === payloadType.connected) {
45
+ ws?.removeEventListener("message", connected);
46
+ resolve();
47
+ }
48
+ }
49
+ ws?.addEventListener("message", connected);
50
+ });
51
+ }
52
+ function send(type, payload, meta) {
53
+ const msg = { type, meta, payload };
54
+ ws?.send(JSON.stringify(msg));
55
+ }
56
+ async function rustExecute(code, { onEnd, onError, onStderr, onStdout, onBegin }) {
57
+ await connect();
58
+ const meta = { sequenceNumber: uuid++ };
59
+ const payload = {
60
+ backtrace: false,
61
+ channel: "stable",
62
+ crateType: "bin",
63
+ edition: "2021",
64
+ mode: "release",
65
+ tests: false,
66
+ code
67
+ };
68
+ send(payloadType.request, payload, meta);
69
+ let stdout = "";
70
+ let stderr = "";
71
+ function onMessage(e) {
72
+ const data = JSON.parse(e.data);
73
+ const { type, payload: payload2, meta: _meta = {} } = data;
74
+ if (_meta.sequenceNumber !== meta.sequenceNumber)
75
+ return;
76
+ if (type === payloadType.execute.begin)
77
+ onBegin?.();
78
+ if (type === payloadType.execute.stdout) {
79
+ stdout += payload2;
80
+ if (stdout.endsWith("\n")) {
81
+ onStdout?.(stdout);
82
+ stdout = "";
83
+ }
84
+ }
85
+ if (type === payloadType.execute.stderr) {
86
+ stderr += payload2;
87
+ if (stderr.endsWith("\n")) {
88
+ if (stderr.startsWith("error:")) {
89
+ const index = stderr.indexOf("\n");
90
+ onStderr?.(stderr.slice(0, index));
91
+ onStderr?.(stderr.slice(index + 1));
92
+ } else {
93
+ onStderr?.(stderr);
94
+ }
95
+ stderr = "";
96
+ }
97
+ }
98
+ if (type === payloadType.execute.end) {
99
+ if (payload2.success === false)
100
+ onError?.(payload2.exitDetail);
101
+ ws?.removeEventListener("message", onMessage);
102
+ onEnd?.();
103
+ }
104
+ }
105
+ ws?.addEventListener("message", onMessage);
106
+ }
107
+
108
+ // src/client/composables/codeRepl.ts
109
+ var ignoredNodes = [".diff.remove", ".vp-copy-ignore"];
110
+ var RE_LANGUAGE = /language-(\w+)/;
111
+ var api = {
112
+ go: "https://api.pengzhanbo.cn/repl/golang/run",
113
+ kotlin: "https://api.pengzhanbo.cn/repl/kotlin/run"
114
+ };
115
+ var langAlias = {
116
+ kt: "kotlin",
117
+ kotlin: "kotlin",
118
+ go: "go",
119
+ rust: "rust",
120
+ rs: "rust"
121
+ };
122
+ var supportLang = ["kotlin", "go", "rust"];
123
+ function resolveLang(lang) {
124
+ return lang ? langAlias[lang] || lang : "";
125
+ }
126
+ function resolveCode(el) {
127
+ const clone = el.cloneNode(true);
128
+ clone.querySelectorAll(ignoredNodes.join(",")).forEach((node) => node.remove());
129
+ return clone.textContent || "";
130
+ }
131
+ function resolveCodeInfo(el) {
132
+ const wrapper = el.querySelector("div[class*=language-]");
133
+ const lang = wrapper?.className.match(RE_LANGUAGE)?.[1];
134
+ const codeEl = wrapper?.querySelector("pre");
135
+ let code = "";
136
+ if (codeEl)
137
+ code = resolveCode(codeEl);
138
+ return { lang: resolveLang(lang), code };
139
+ }
140
+ function useCodeRepl(el) {
141
+ const lang = ref();
142
+ const loaded = ref(true);
143
+ const firstRun = ref(true);
144
+ const finished = ref(true);
145
+ const stdout = ref([]);
146
+ const stderr = ref([]);
147
+ const error = ref("");
148
+ const backendVersion = ref("");
149
+ onMounted(() => {
150
+ if (el.value) {
151
+ const info = resolveCodeInfo(el.value);
152
+ lang.value = info.lang;
153
+ }
154
+ });
155
+ const executeMap = {
156
+ kotlin: executeKotlin,
157
+ go: executeGolang,
158
+ rust: executeRust
159
+ };
160
+ function onCleanRun() {
161
+ loaded.value = false;
162
+ finished.value = false;
163
+ stdout.value = [];
164
+ stderr.value = [];
165
+ error.value = "";
166
+ firstRun.value = true;
167
+ backendVersion.value = "";
168
+ }
169
+ async function onRunCode() {
170
+ if (!el.value || !loaded.value)
171
+ return;
172
+ const info = resolveCodeInfo(el.value);
173
+ lang.value = info.lang;
174
+ if (!lang.value || !info.code || !supportLang.includes(lang.value))
175
+ return;
176
+ if (firstRun.value)
177
+ firstRun.value = false;
178
+ loaded.value = false;
179
+ finished.value = false;
180
+ stdout.value = [];
181
+ stderr.value = [];
182
+ error.value = "";
183
+ await executeMap[lang.value]?.(info.code);
184
+ }
185
+ async function executeGolang(code) {
186
+ const res = await http.post(api.go, { code });
187
+ backendVersion.value = `v${res.version}`;
188
+ loaded.value = true;
189
+ if (res.error) {
190
+ error.value = res.error;
191
+ finished.value = true;
192
+ return;
193
+ }
194
+ const events = res.events || [];
195
+ for (const event of events) {
196
+ if (event.kind === "stdout") {
197
+ if (event.delay)
198
+ await sleep(event.delay / 1e6);
199
+ stdout.value.push(event.message);
200
+ } else if (event.kind === "stderr") {
201
+ stderr.value.push(event.message);
202
+ }
203
+ }
204
+ finished.value = true;
205
+ }
206
+ async function executeKotlin(code) {
207
+ const filename = "File.kt";
208
+ const res = await http.post(api.kotlin, {
209
+ args: "",
210
+ files: [{ name: filename, publicId: "", text: code }]
211
+ });
212
+ backendVersion.value = `v${res.version}`;
213
+ loaded.value = true;
214
+ if (res.errors) {
215
+ const errors = Array.isArray(res.errors[filename]) ? res.errors[filename] : [res.errors[filename]];
216
+ if (errors.length) {
217
+ errors.forEach(
218
+ ({ message, severity }) => severity === "ERROR" && stderr.value.push(message)
219
+ );
220
+ }
221
+ }
222
+ stdout.value.push(res.text);
223
+ finished.value = true;
224
+ }
225
+ async function executeRust(code) {
226
+ await rustExecute(code, {
227
+ onBegin: () => {
228
+ loaded.value = true;
229
+ finished.value = false;
230
+ stdout.value = [];
231
+ stderr.value = [];
232
+ error.value = "";
233
+ backendVersion.value = "release";
234
+ },
235
+ onError(message) {
236
+ error.value = message;
237
+ },
238
+ onStdout(message) {
239
+ stdout.value.push(message);
240
+ },
241
+ onStderr(message) {
242
+ stderr.value.push(message);
243
+ },
244
+ onEnd: () => {
245
+ finished.value = true;
246
+ }
247
+ });
248
+ }
249
+ return {
250
+ onRunCode,
251
+ onCleanRun,
252
+ lang,
253
+ backendVersion,
254
+ firstRun,
255
+ stderr,
256
+ stdout,
257
+ loaded,
258
+ finished,
259
+ error
260
+ };
261
+ }
262
+ export {
263
+ resolveCode,
264
+ resolveCodeInfo,
265
+ useCodeRepl
266
+ };
@@ -0,0 +1,19 @@
1
+ import { PDFEmbedType, PDFTokenMeta } from '../../shared/index.js';
2
+
3
+ /**
4
+ * Fork for https://github.com/vuepress-theme-hope/vuepress-theme-hope/blob/main/packages/components/src/client/utils/viewPDF.ts
5
+ *
6
+ * The MIT License (MIT)
7
+ * Copyright (C) 2021 - PRESENT by Mr.Hope<mister-hope@outlook.com>
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ */
15
+
16
+ declare function renderPDF(el: HTMLElement, url: string, embedType: PDFEmbedType, options: PDFTokenMeta): void;
17
+ declare function usePDF(el: HTMLElement, url: string, options: PDFTokenMeta): void;
18
+
19
+ export { renderPDF, usePDF };
@@ -0,0 +1,59 @@
1
+ // src/client/composables/pdf.ts
2
+ import { withBase } from "vuepress/client";
3
+ import { ensureEndingSlash, isLinkHttp } from "vuepress/shared";
4
+ import { pluginOptions } from "../options.js";
5
+ import { checkIsiPad, checkIsMobile, checkIsSafari } from "../utils/is.js";
6
+ function queryStringify(options) {
7
+ const { page, noToolbar, zoom } = options;
8
+ const params = [
9
+ `page=${page}`,
10
+ `toolbar=${noToolbar ? 0 : 1}`,
11
+ `zoom=${zoom}`
12
+ ];
13
+ let queryString = params.join("&");
14
+ if (queryString)
15
+ queryString = `#${queryString}`;
16
+ return queryString;
17
+ }
18
+ function renderPDF(el, url, embedType, options) {
19
+ if (!pluginOptions.pdf)
20
+ return;
21
+ url = isLinkHttp(url) ? url : new URL(withBase(url), typeof location !== "undefined" ? location.href : "").toString();
22
+ const pdfOptions = pluginOptions.pdf === true ? {} : pluginOptions.pdf;
23
+ pdfOptions.pdfjsUrl ??= "https://static.pengzhanbo.cn/pdfjs/";
24
+ const pdfjsUrl = `${ensureEndingSlash(withBase(pdfOptions.pdfjsUrl))}web/viewer.html`;
25
+ const queryString = queryStringify(options);
26
+ const source = embedType === "pdfjs" ? `${pdfjsUrl}?file=${url}${queryString}` : `${url}${queryString}`;
27
+ const tagName = embedType === "pdfjs" || embedType === "iframe" ? "iframe" : "embed";
28
+ el.innerHTML = "";
29
+ const pdf = document.createElement(tagName);
30
+ pdf.className = "pdf-viewer";
31
+ pdf.type = "application/pdf";
32
+ pdf.title = options.title || "PDF Viewer";
33
+ pdf.src = source;
34
+ if (pdf instanceof HTMLIFrameElement)
35
+ pdf.allow = "fullscreen";
36
+ el.appendChild(pdf);
37
+ }
38
+ function usePDF(el, url, options) {
39
+ if (typeof window === "undefined" || !window?.navigator?.userAgent)
40
+ return;
41
+ const { navigator } = window;
42
+ const { userAgent } = navigator;
43
+ const isModernBrowser = typeof window.Promise === "function";
44
+ const isMobileDevice = checkIsiPad(userAgent) || checkIsMobile(userAgent);
45
+ const isSafariDesktop = !isMobileDevice && checkIsSafari(userAgent);
46
+ const isFirefoxWithPDFJS = !isMobileDevice && /firefox/iu.test(userAgent) && userAgent.split("rv:").length > 1 ? Number.parseInt(userAgent.split("rv:")[1].split(".")[0], 10) > 18 : false;
47
+ const supportsPDFs = !isMobileDevice && (isModernBrowser || isFirefoxWithPDFJS);
48
+ if (!url)
49
+ return;
50
+ if (supportsPDFs || !isMobileDevice) {
51
+ const embedType = isSafariDesktop ? "iframe" : "embed";
52
+ return renderPDF(el, url, embedType, options);
53
+ }
54
+ return renderPDF(el, url, "pdfjs", options);
55
+ }
56
+ export {
57
+ renderPDF,
58
+ usePDF
59
+ };
@@ -0,0 +1,10 @@
1
+ declare function rustExecute(code: string, { onEnd, onError, onStderr, onStdout, onBegin }: RustExecuteOptions): Promise<void>;
2
+ interface RustExecuteOptions {
3
+ onBegin?: () => void;
4
+ onStdout?: (message: string) => void;
5
+ onStderr?: (message: string) => void;
6
+ onEnd?: () => void;
7
+ onError?: (message: string) => void;
8
+ }
9
+
10
+ export { rustExecute };
@@ -0,0 +1,104 @@
1
+ // src/client/composables/rustRepl.ts
2
+ import { tryOnScopeDispose } from "@vueuse/core";
3
+ var wsUrl = "wss://play.rust-lang.org/websocket";
4
+ var payloadType = {
5
+ connected: "websocket/connected",
6
+ request: "output/execute/wsExecuteRequest",
7
+ execute: {
8
+ begin: "output/execute/wsExecuteBegin",
9
+ // status: 'output/execute/wsExecuteStatus',
10
+ stderr: "output/execute/wsExecuteStderr",
11
+ stdout: "output/execute/wsExecuteStdout",
12
+ end: "output/execute/wsExecuteEnd"
13
+ }
14
+ };
15
+ var ws = null;
16
+ var isOpen = false;
17
+ var uuid = 0;
18
+ function connect() {
19
+ if (isOpen)
20
+ return Promise.resolve();
21
+ ws = new WebSocket(wsUrl);
22
+ uuid = 0;
23
+ ws.addEventListener("open", () => {
24
+ isOpen = true;
25
+ send(
26
+ payloadType.connected,
27
+ { iAcceptThisIsAnUnsupportedApi: true },
28
+ { websocket: true, sequenceNumber: uuid }
29
+ );
30
+ });
31
+ ws.addEventListener("close", () => {
32
+ isOpen = false;
33
+ ws = null;
34
+ });
35
+ tryOnScopeDispose(() => ws?.close());
36
+ return new Promise((resolve) => {
37
+ function connected(e) {
38
+ const data = JSON.parse(e.data);
39
+ if (data.type === payloadType.connected) {
40
+ ws?.removeEventListener("message", connected);
41
+ resolve();
42
+ }
43
+ }
44
+ ws?.addEventListener("message", connected);
45
+ });
46
+ }
47
+ function send(type, payload, meta) {
48
+ const msg = { type, meta, payload };
49
+ ws?.send(JSON.stringify(msg));
50
+ }
51
+ async function rustExecute(code, { onEnd, onError, onStderr, onStdout, onBegin }) {
52
+ await connect();
53
+ const meta = { sequenceNumber: uuid++ };
54
+ const payload = {
55
+ backtrace: false,
56
+ channel: "stable",
57
+ crateType: "bin",
58
+ edition: "2021",
59
+ mode: "release",
60
+ tests: false,
61
+ code
62
+ };
63
+ send(payloadType.request, payload, meta);
64
+ let stdout = "";
65
+ let stderr = "";
66
+ function onMessage(e) {
67
+ const data = JSON.parse(e.data);
68
+ const { type, payload: payload2, meta: _meta = {} } = data;
69
+ if (_meta.sequenceNumber !== meta.sequenceNumber)
70
+ return;
71
+ if (type === payloadType.execute.begin)
72
+ onBegin?.();
73
+ if (type === payloadType.execute.stdout) {
74
+ stdout += payload2;
75
+ if (stdout.endsWith("\n")) {
76
+ onStdout?.(stdout);
77
+ stdout = "";
78
+ }
79
+ }
80
+ if (type === payloadType.execute.stderr) {
81
+ stderr += payload2;
82
+ if (stderr.endsWith("\n")) {
83
+ if (stderr.startsWith("error:")) {
84
+ const index = stderr.indexOf("\n");
85
+ onStderr?.(stderr.slice(0, index));
86
+ onStderr?.(stderr.slice(index + 1));
87
+ } else {
88
+ onStderr?.(stderr);
89
+ }
90
+ stderr = "";
91
+ }
92
+ }
93
+ if (type === payloadType.execute.end) {
94
+ if (payload2.success === false)
95
+ onError?.(payload2.exitDetail);
96
+ ws?.removeEventListener("message", onMessage);
97
+ onEnd?.();
98
+ }
99
+ }
100
+ ws?.addEventListener("message", onMessage);
101
+ }
102
+ export {
103
+ rustExecute
104
+ };