@on-panda/serve 0.6.1

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 (84) hide show
  1. package/README.md +21 -0
  2. package/bin/serve.js +65 -0
  3. package/package.json +23 -0
  4. package/server/bypassCorsProxyPlugin.js +120 -0
  5. package/server/runtimeImportPlugin.js +46 -0
  6. package/server/safeProxyMiddleware.js +18 -0
  7. package/server/urlVerification.js +8 -0
  8. package/server/usingServerProxyPlugin.js +123 -0
  9. package/server/webConfigPlugin.js +41 -0
  10. package/web/assets/AudioPlugin-B9D2vg7i.js +73 -0
  11. package/web/assets/AudioPlugin-Ctg1swNk.css +1 -0
  12. package/web/assets/ImagePlugin-B55vjXoR.css +1 -0
  13. package/web/assets/ImagePlugin-D-lN3l8G.js +1 -0
  14. package/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  15. package/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  16. package/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  17. package/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  18. package/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  19. package/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  20. package/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  21. package/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  22. package/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  23. package/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  24. package/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  25. package/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  26. package/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  27. package/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  28. package/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  29. package/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  30. package/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  31. package/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  32. package/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  33. package/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  34. package/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  35. package/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  36. package/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  37. package/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  38. package/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  39. package/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  40. package/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  41. package/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  42. package/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  43. package/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  44. package/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  45. package/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  46. package/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  47. package/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  48. package/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  49. package/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  50. package/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  51. package/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  52. package/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  53. package/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  54. package/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  55. package/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  56. package/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  57. package/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  58. package/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  59. package/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  60. package/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  61. package/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  62. package/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  63. package/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  64. package/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  65. package/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  66. package/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  67. package/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  68. package/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  69. package/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  70. package/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  71. package/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  72. package/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  73. package/web/assets/VideoPlugin-Bzko1Oof.css +1 -0
  74. package/web/assets/VideoPlugin-CwcxLwKM.js +1 -0
  75. package/web/assets/browserAgentMcp-Dpy7F3rj.js +35 -0
  76. package/web/assets/defaultCustom-FmFgRqLi.js +1 -0
  77. package/web/assets/index-6pA5bJ5o.css +10 -0
  78. package/web/assets/index-BWq4H1Vs.js +8 -0
  79. package/web/assets/index-VDofnkb1.js +701 -0
  80. package/web/assets/streamableHttp-P4toeGJI.js +5 -0
  81. package/web/assets/types-JPNfEfAJ.js +26 -0
  82. package/web/img/on-panda-demo-candidate-continue-generating-cn.mp4 +0 -0
  83. package/web/img/on-panda-logo-256x256.png +0 -0
  84. package/web/index.html +22 -0
package/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # @on-panda/serve
2
+
3
+ A production server for the onPanda web application. It serves the same Vite production build as `pnpm preview`, including the server proxy middleware.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npx @on-panda/serve --port 8080 --web_config config.json5
9
+ ```
10
+
11
+ Open `http://localhost:8080/`. The server listens on `0.0.0.0`, so it is also accessible through the host's network address.
12
+
13
+ | Option | Default | Meaning |
14
+ | --- | --- | --- |
15
+ | `--web_config` | Omitted | JSON5 configuration file loaded by the web UI. When omitted, the config endpoint returns `{}`. |
16
+ | `--port` | `4173` | HTTP port. |
17
+ | `--help`, `-h` | | Print usage and exit. |
18
+
19
+ Relative paths for `--web_config` are resolved against the working directory where the command is run.
20
+
21
+ The server has no authentication. The web configuration is returned to browser clients, so its values should be treated as public.
package/bin/serve.js ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from 'node:path'
4
+ import { fileURLToPath } from 'node:url'
5
+ import { preview } from 'vite'
6
+
7
+ import { createBypassCorsProxyPlugin } from '../server/bypassCorsProxyPlugin.js'
8
+ import { createRuntimeImportPlugin } from '../server/runtimeImportPlugin.js'
9
+ import { createUsingServerProxyPlugin } from '../server/usingServerProxyPlugin.js'
10
+ import { createWebConfigPlugin } from '../server/webConfigPlugin.js'
11
+
12
+ const packageDir = fileURLToPath(new URL('..', import.meta.url))
13
+
14
+ function parseArgs(args) {
15
+ const options = {
16
+ port: 4173,
17
+ webConfig: '',
18
+ }
19
+ for (let index = 0; index < args.length; index += 1) {
20
+ const arg = args[index]
21
+ if (arg === '--web_config') {
22
+ options.webConfig = args[++index]
23
+ } else if (arg === '--port') {
24
+ options.port = Number(args[++index])
25
+ } else if (arg === '--help' || arg === '-h') {
26
+ console.log('Usage: serve [--web_config web_config.json5] [--port 4173]')
27
+ process.exit(0)
28
+ }
29
+ }
30
+ return {
31
+ ...options,
32
+ webConfig: options.webConfig ? path.resolve(options.webConfig) : '',
33
+ }
34
+ }
35
+
36
+ async function startServer(options) {
37
+ const runtimeImportPath = process.env.VITE_ON_PANDA_WEB_RUNTIME_IMPORT
38
+ ? path.resolve(process.cwd(), process.env.VITE_ON_PANDA_WEB_RUNTIME_IMPORT)
39
+ : ''
40
+ const server = await preview({
41
+ root: packageDir,
42
+ appType: 'spa',
43
+ plugins: [
44
+ createBypassCorsProxyPlugin(process.env.VITE_ON_PANDA_BROWSER_AGENT_PROXY_PATH),
45
+ createRuntimeImportPlugin(runtimeImportPath),
46
+ createUsingServerProxyPlugin(),
47
+ createWebConfigPlugin(options.webConfig),
48
+ ],
49
+ build: {
50
+ outDir: 'web',
51
+ },
52
+ preview: {
53
+ host: '0.0.0.0',
54
+ port: options.port,
55
+ },
56
+ })
57
+ const [url] = server.resolvedUrls.local
58
+ console.log(`onPanda web server: ${url}`)
59
+ }
60
+
61
+ const options = parseArgs(process.argv.slice(2))
62
+ startServer(options).catch(error => {
63
+ console.error(error)
64
+ process.exitCode = 1
65
+ })
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@on-panda/serve",
3
+ "version": "0.6.1",
4
+ "description": "A production server for the onPanda web application",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "serve": "./bin/serve.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "server",
13
+ "web"
14
+ ],
15
+ "dependencies": {
16
+ "json5": "^2.x",
17
+ "undici": "^7.x",
18
+ "vite": "^5.3.1"
19
+ },
20
+ "scripts": {
21
+ "preview": "node bin/serve.js"
22
+ }
23
+ }
@@ -0,0 +1,120 @@
1
+ import { Readable } from 'node:stream'
2
+
3
+ import { withSafeProxyMiddleware } from './safeProxyMiddleware.js'
4
+ import { verifyUrlIsLlmApiCall, verifyUrlIsMcp } from './urlVerification.js'
5
+
6
+ export function createBypassCorsProxyPlugin(browserAgentProxyPath = '') {
7
+ const corsMiddleware = withSafeProxyMiddleware('Bypass CORS proxy', createBypassCorsProxyMiddleware({
8
+ verifyUrl: targetUrl => verifyUrlIsLlmApiCall(targetUrl) || verifyUrlIsMcp(targetUrl),
9
+ }))
10
+ // Browser-agent proxy: forwards arbitrary URLs as a fallback for cors-internet.
11
+ const browserAgentMiddleware = browserAgentProxyPath
12
+ ? withSafeProxyMiddleware('Browser agent proxy', createBypassCorsProxyMiddleware())
13
+ : null
14
+ const register = (server) => {
15
+ server.middlewares.use('/bypass-CORS', corsMiddleware)
16
+ if (browserAgentMiddleware) server.middlewares.use(browserAgentProxyPath, browserAgentMiddleware)
17
+ }
18
+
19
+ return {
20
+ name: 'bypass-cors-proxy-middleware',
21
+ configureServer: register,
22
+ configurePreviewServer: register,
23
+ }
24
+ }
25
+
26
+ function createBypassCorsProxyMiddleware({ verifyUrl } = {}) {
27
+ return async (req, res) => {
28
+ const abortController = new AbortController()
29
+ try {
30
+ const rawPath = req.url || ''
31
+ const withoutPrefix = rawPath.startsWith('/bypass-CORS/')
32
+ ? rawPath.replace(/^\/bypass-CORS\//, '')
33
+ : rawPath.replace(/^\/+/, '')
34
+ const targetUrl = new URL(decodeURIComponent(withoutPrefix))
35
+ if (verifyUrl && !verifyUrl(targetUrl)) {
36
+ res.statusCode = 400
37
+ res.end(`Not an allowed target URL: ${targetUrl}`)
38
+ return
39
+ }
40
+
41
+ const method = req.method || 'GET'
42
+ const hopByHopHeaders = new Set(['connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailers', 'transfer-encoding', 'upgrade', 'host'])
43
+ const headers = new Headers()
44
+ const abortUpstream = () => abortController.abort()
45
+ req.on('aborted', abortUpstream)
46
+ res.on('close', () => {
47
+ if (!res.writableEnded) {
48
+ abortUpstream()
49
+ }
50
+ })
51
+ Object.entries(req.headers).forEach(([key, value]) => {
52
+ if (!value || hopByHopHeaders.has(key.toLowerCase()) || key.toLowerCase() === 'accept-encoding') {
53
+ return
54
+ }
55
+ const values = Array.isArray(value) ? value : [value]
56
+ values.forEach(val => headers.append(key, val))
57
+ })
58
+ headers.set('accept-encoding', 'identity')
59
+
60
+ const fetchInit = {
61
+ method,
62
+ headers,
63
+ compress: false,
64
+ signal: abortController.signal,
65
+ }
66
+ const hasBody = !['GET', 'HEAD'].includes(method.toUpperCase())
67
+ if (hasBody) {
68
+ fetchInit.body = req
69
+ fetchInit.duplex = 'half'
70
+ }
71
+
72
+ const upstreamResponse = await fetch(targetUrl.toString(), fetchInit)
73
+ res.statusCode = upstreamResponse.status
74
+ const upstreamContentEncoding = upstreamResponse.headers.get('content-encoding')
75
+ const shouldNormalizeContentEncoding = upstreamContentEncoding && upstreamContentEncoding.toLowerCase() !== 'identity'
76
+ if (shouldNormalizeContentEncoding) {
77
+ res.setHeader('content-encoding', 'identity')
78
+ }
79
+ upstreamResponse.headers.forEach((value, key) => {
80
+ const lowerKey = key.toLowerCase()
81
+ if (hopByHopHeaders.has(lowerKey)) {
82
+ return
83
+ }
84
+ if (shouldNormalizeContentEncoding && ['content-encoding', 'content-length'].includes(lowerKey)) {
85
+ return
86
+ }
87
+ res.setHeader(key, value)
88
+ })
89
+ if (!upstreamResponse.body) {
90
+ res.end()
91
+ return
92
+ }
93
+ const proxyStream = Readable.fromWeb(upstreamResponse.body)
94
+ proxyStream.on('error', (err) => {
95
+ if (abortController.signal.aborted) {
96
+ return
97
+ }
98
+ console.error('Bypass CORS proxy stream failed', err)
99
+ if (!res.headersSent) {
100
+ res.statusCode = 502
101
+ res.setHeader('content-type', 'text/plain')
102
+ res.end('Proxy stream error')
103
+ } else {
104
+ res.destroy(err)
105
+ }
106
+ })
107
+ proxyStream.pipe(res)
108
+ } catch (error) {
109
+ if (abortController.signal.aborted) {
110
+ return
111
+ }
112
+ console.error('Bypass CORS proxy failed', error)
113
+ if (!res.headersSent) {
114
+ res.statusCode = 500
115
+ res.setHeader('content-type', 'text/plain')
116
+ }
117
+ res.end(`Proxy error: ${error instanceof Error ? error.message : 'Unknown error'}`)
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,46 @@
1
+ import fs from 'node:fs'
2
+ import fsPromises from 'node:fs/promises'
3
+
4
+ export function createRuntimeImportPlugin(runtimeImportPath) {
5
+ // For configurations that are included in `pnpm dev`/`preview` but not in git or the build.
6
+ if (!runtimeImportPath) {
7
+ return null
8
+ }
9
+
10
+ if (!fs.existsSync(runtimeImportPath)) {
11
+ throw new Error(`VITE_ON_PANDA_WEB_RUNTIME_IMPORT file not found: ${runtimeImportPath}`)
12
+ }
13
+
14
+ return {
15
+ name: 'on-panda-web-runtime-import',
16
+ configureServer(server) {
17
+ server.middlewares.use('/on-panda-web-runtime.js', createRuntimeFileMiddleware(runtimeImportPath))
18
+ },
19
+ configurePreviewServer(server) {
20
+ server.middlewares.use('/on-panda-web-runtime.js', createRuntimeFileMiddleware(runtimeImportPath))
21
+ }
22
+ }
23
+ }
24
+
25
+ function createRuntimeFileMiddleware(filePath) {
26
+ return async (req, res) => {
27
+ if (!['GET', 'HEAD'].includes(req.method || 'GET')) {
28
+ res.statusCode = 405
29
+ res.end('Method Not Allowed')
30
+ return
31
+ }
32
+ try {
33
+ const data = await fsPromises.readFile(filePath)
34
+ res.statusCode = 200
35
+ res.setHeader('content-type', 'text/javascript; charset=utf-8')
36
+ if (req.method === 'HEAD') {
37
+ res.end()
38
+ return
39
+ }
40
+ res.end(data)
41
+ } catch {
42
+ res.statusCode = 404
43
+ res.end('Not Found')
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,18 @@
1
+ export function withSafeProxyMiddleware(proxyName, middleware) {
2
+ return async (req, res) => {
3
+ try {
4
+ await middleware(req, res)
5
+ } catch (error) {
6
+ console.error(`${proxyName} crashed`, error)
7
+ if (res.headersSent) {
8
+ if (!res.writableEnded) {
9
+ res.end()
10
+ }
11
+ return
12
+ }
13
+ res.statusCode = 500
14
+ res.setHeader('content-type', 'text/plain')
15
+ res.end(`Proxy error: ${error instanceof Error ? error.message : 'Unknown error'}`)
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,8 @@
1
+ export function verifyUrlIsLlmApiCall(url) {
2
+ // check is LLM api call by /models or /completions
3
+ return /(^|\/)(models|completions|messages)(\/|$)/.test(url.pathname)
4
+ }
5
+
6
+ export function verifyUrlIsMcp(url) {
7
+ return /(^|\/)mcp(\/|$)/.test(url.pathname)
8
+ }
@@ -0,0 +1,123 @@
1
+ import { Readable } from 'node:stream'
2
+
3
+ import { ProxyAgent } from 'undici'
4
+
5
+ import { withSafeProxyMiddleware } from './safeProxyMiddleware.js'
6
+ import { verifyUrlIsLlmApiCall } from './urlVerification.js'
7
+
8
+ export function createUsingServerProxyPlugin() {
9
+ const middleware = withSafeProxyMiddleware('Server proxy', createServerProxyMiddleware())
10
+
11
+ return {
12
+ name: 'using-server-proxy-middleware',
13
+ configureServer(server) {
14
+ server.middlewares.use('/using-server-proxy', middleware)
15
+ },
16
+ configurePreviewServer(server) {
17
+ server.middlewares.use('/using-server-proxy', middleware)
18
+ }
19
+ }
20
+ }
21
+
22
+ function createServerProxyMiddleware() {
23
+ return async (req, res) => {
24
+ const abortController = new AbortController()
25
+ try {
26
+ const rawPath = req.url || ''
27
+ const withoutPrefix = rawPath.startsWith('/using-server-proxy/')
28
+ ? rawPath.replace(/^\/using-server-proxy\//, '')
29
+ : rawPath.replace(/^\/+/, '')
30
+ const incomingPath = decodeURIComponent(withoutPrefix)
31
+ const match = incomingPath.match(/^(?<proxy>[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^/]+)\/(?<target>https?:\/\/.+)$/)
32
+ if (!match || !match.groups) {
33
+ res.statusCode = 400
34
+ res.end('Invalid proxy request: expected /using-server-proxy/<proxy>/<target>')
35
+ return
36
+ }
37
+ const { proxy: proxyUrl, target } = match.groups
38
+ const targetUrl = new URL(target)
39
+ if (!verifyUrlIsLlmApiCall(targetUrl)) {
40
+ throw new Error(`Not a valid LLM API call: ${targetUrl}`)
41
+ }
42
+
43
+ const method = req.method || 'GET'
44
+ const hopByHopHeaders = new Set(['connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailers', 'transfer-encoding', 'upgrade', 'host'])
45
+ const headers = new Headers()
46
+ const abortUpstream = () => abortController.abort()
47
+ req.on('aborted', abortUpstream)
48
+ res.on('close', () => {
49
+ if (!res.writableEnded) {
50
+ abortUpstream()
51
+ }
52
+ })
53
+ Object.entries(req.headers).forEach(([key, value]) => {
54
+ if (!value || hopByHopHeaders.has(key.toLowerCase()) || key.toLowerCase() === 'accept-encoding') {
55
+ return
56
+ }
57
+ const values = Array.isArray(value) ? value : [value]
58
+ values.forEach(val => headers.append(key, val))
59
+ })
60
+ headers.set('accept-encoding', 'identity')
61
+
62
+ const fetchInit = {
63
+ method,
64
+ headers,
65
+ dispatcher: new ProxyAgent(proxyUrl),
66
+ compress: false,
67
+ signal: abortController.signal,
68
+ }
69
+ const hasBody = !['GET', 'HEAD'].includes(method.toUpperCase())
70
+ if (hasBody) {
71
+ fetchInit.body = req
72
+ fetchInit.duplex = 'half'
73
+ }
74
+
75
+ const upstreamResponse = await fetch(targetUrl.toString(), fetchInit)
76
+ res.statusCode = upstreamResponse.status
77
+ const upstreamContentEncoding = upstreamResponse.headers.get('content-encoding')
78
+ const shouldNormalizeContentEncoding = upstreamContentEncoding && upstreamContentEncoding.toLowerCase() !== 'identity'
79
+ if (shouldNormalizeContentEncoding) {
80
+ res.setHeader('content-encoding', 'identity')
81
+ }
82
+ upstreamResponse.headers.forEach((value, key) => {
83
+ const lowerKey = key.toLowerCase()
84
+ if (hopByHopHeaders.has(lowerKey)) {
85
+ return
86
+ }
87
+ if (shouldNormalizeContentEncoding && ['content-encoding', 'content-length'].includes(lowerKey)) {
88
+ return
89
+ }
90
+ res.setHeader(key, value)
91
+ })
92
+ if (!upstreamResponse.body) {
93
+ res.end()
94
+ return
95
+ }
96
+ const proxyStream = Readable.fromWeb(upstreamResponse.body)
97
+ proxyStream.on('error', (err) => {
98
+ if (abortController.signal.aborted) {
99
+ return
100
+ }
101
+ console.error('Server proxy stream failed', err)
102
+ if (!res.headersSent) {
103
+ res.statusCode = 502
104
+ res.setHeader('content-type', 'text/plain')
105
+ res.end('Proxy stream error')
106
+ } else {
107
+ res.destroy(err)
108
+ }
109
+ })
110
+ proxyStream.pipe(res)
111
+ } catch (error) {
112
+ if (abortController.signal.aborted) {
113
+ return
114
+ }
115
+ console.error('Server proxy failed', error)
116
+ if (!res.headersSent) {
117
+ res.statusCode = 500
118
+ res.setHeader('content-type', 'text/plain')
119
+ }
120
+ res.end(`Proxy error: ${error instanceof Error ? error.message : 'Unknown error'}`)
121
+ }
122
+ }
123
+ }
@@ -0,0 +1,41 @@
1
+ import fs from 'node:fs/promises'
2
+
3
+ import JSON5 from 'json5'
4
+
5
+ const webConfigUrl = '/on-panda-web/web_config.json5'
6
+
7
+ export function createWebConfigPlugin(webConfigPath = '') {
8
+ const middleware = createWebConfigMiddleware(webConfigPath)
9
+ return {
10
+ name: 'on-panda-web-config',
11
+ configureServer(server) {
12
+ server.middlewares.use(webConfigUrl, middleware)
13
+ },
14
+ configurePreviewServer(server) {
15
+ server.middlewares.use(webConfigUrl, middleware)
16
+ },
17
+ }
18
+ }
19
+
20
+ function createWebConfigMiddleware(webConfigPath) {
21
+ return async (req, res) => {
22
+ if (!['GET', 'HEAD'].includes(req.method || 'GET')) {
23
+ res.statusCode = 405
24
+ res.end('Method Not Allowed')
25
+ return
26
+ }
27
+ try {
28
+ const config = webConfigPath ? JSON5.parse(await fs.readFile(webConfigPath, 'utf8')) : {}
29
+ const body = JSON.stringify(config)
30
+ res.statusCode = 200
31
+ res.setHeader('content-type', 'application/json; charset=utf-8')
32
+ res.setHeader('cache-control', 'no-store')
33
+ res.setHeader('content-length', Buffer.byteLength(body))
34
+ res.end(req.method === 'HEAD' ? undefined : body)
35
+ } catch (error) {
36
+ res.statusCode = error.code === 'ENOENT' ? 404 : 400
37
+ res.setHeader('content-type', 'text/plain; charset=utf-8')
38
+ res.end(error.message)
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,73 @@
1
+ import{_ as U,r as W,c as F,m as V,w as q,b as G,d as Y,o as $,a as B,e as j,f as J}from"./index-VDofnkb1.js";function y(c,t,e,i){return new(e||(e=Promise))(function(s,n){function r(o){try{l(i.next(o))}catch(h){n(h)}}function a(o){try{l(i.throw(o))}catch(h){n(h)}}function l(o){var h;o.done?s(o.value):(h=o.value,h instanceof e?h:new e(function(u){u(h)})).then(r,a)}l((i=i.apply(c,t||[])).next())})}let R=class{constructor(){this.listeners={}}on(t,e,i){if(this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),i==null?void 0:i.once){const s=()=>{this.un(t,s),this.un(t,e)};return this.on(t,s),s}return()=>this.un(t,e)}un(t,e){var i;(i=this.listeners[t])===null||i===void 0||i.delete(e)}once(t,e){return this.on(t,e,{once:!0})}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach(i=>i(...e))}};const L={decode:function(c,t){return y(this,void 0,void 0,function*(){const e=new AudioContext({sampleRate:t});return e.decodeAudioData(c).finally(()=>e.close())})},createBuffer:function(c,t){return typeof c[0]=="number"&&(c=[c]),function(e){const i=e[0];if(i.some(s=>s>1||s<-1)){const s=i.length;let n=0;for(let r=0;r<s;r++){const a=Math.abs(i[r]);a>n&&(n=a)}for(const r of e)for(let a=0;a<s;a++)r[a]/=n}}(c),{duration:t,length:c[0].length,sampleRate:c[0].length/t,numberOfChannels:c.length,getChannelData:e=>c==null?void 0:c[e],copyFromChannel:AudioBuffer.prototype.copyFromChannel,copyToChannel:AudioBuffer.prototype.copyToChannel}}};function H(c,t){const e=t.xmlns?document.createElementNS(t.xmlns,c):document.createElement(c);for(const[i,s]of Object.entries(t))if(i==="children")for(const[n,r]of Object.entries(t))typeof r=="string"?e.appendChild(document.createTextNode(r)):e.appendChild(H(n,r));else i==="style"?Object.assign(e.style,s):i==="textContent"?e.textContent=s:e.setAttribute(i,s.toString());return e}function z(c,t,e){const i=H(c,t||{});return e==null||e.appendChild(i),i}var K=Object.freeze({__proto__:null,createElement:z,default:z});const Q={fetchBlob:function(c,t,e){return y(this,void 0,void 0,function*(){const i=yield fetch(c,e);if(i.status>=400)throw new Error(`Failed to fetch ${c}: ${i.status} (${i.statusText})`);return function(s,n){y(this,void 0,void 0,function*(){if(!s.body||!s.headers)return;const r=s.body.getReader(),a=Number(s.headers.get("Content-Length"))||0;let l=0;const o=u=>y(this,void 0,void 0,function*(){l+=(u==null?void 0:u.length)||0;const d=Math.round(l/a*100);n(d)}),h=()=>y(this,void 0,void 0,function*(){let u;try{u=yield r.read()}catch{return}u.done||(o(u.value),yield h())});h()})}(i.clone(),t),i.blob()})}};class Z extends R{constructor(t){super(),this.isExternalMedia=!1,t.media?(this.media=t.media,this.isExternalMedia=!0):this.media=document.createElement("audio"),t.mediaControls&&(this.media.controls=!0),t.autoplay&&(this.media.autoplay=!0),t.playbackRate!=null&&this.onMediaEvent("canplay",()=>{t.playbackRate!=null&&(this.media.playbackRate=t.playbackRate)},{once:!0})}onMediaEvent(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e,i)}getSrc(){return this.media.currentSrc||this.media.src||""}revokeSrc(){const t=this.getSrc();t.startsWith("blob:")&&URL.revokeObjectURL(t)}canPlayType(t){return this.media.canPlayType(t)!==""}setSrc(t,e){const i=this.getSrc();if(t&&i===t)return;this.revokeSrc();const s=e instanceof Blob&&(this.canPlayType(e.type)||!t)?URL.createObjectURL(e):t;i&&(this.media.src="");try{this.media.src=s}catch{this.media.src=t}}destroy(){this.isExternalMedia||(this.media.pause(),this.media.remove(),this.revokeSrc(),this.media.src="",this.media.load())}setMediaElement(t){this.media=t}play(){return y(this,void 0,void 0,function*(){return this.media.play()})}pause(){this.media.pause()}isPlaying(){return!this.media.paused&&!this.media.ended}setTime(t){this.media.currentTime=Math.max(0,Math.min(t,this.getDuration()))}getDuration(){return this.media.duration}getCurrentTime(){return this.media.currentTime}getVolume(){return this.media.volume}setVolume(t){this.media.volume=t}getMuted(){return this.media.muted}setMuted(t){this.media.muted=t}getPlaybackRate(){return this.media.playbackRate}isSeeking(){return this.media.seeking}setPlaybackRate(t,e){e!=null&&(this.media.preservesPitch=e),this.media.playbackRate=t}getMediaElement(){return this.media}setSinkId(t){return this.media.setSinkId(t)}}class T extends R{constructor(t,e){super(),this.timeouts=[],this.isScrollable=!1,this.audioData=null,this.resizeObserver=null,this.lastContainerWidth=0,this.isDragging=!1,this.subscriptions=[],this.unsubscribeOnScroll=[],this.subscriptions=[],this.options=t;const i=this.parentFromOptionsContainer(t.container);this.parent=i;const[s,n]=this.initHtml();i.appendChild(s),this.container=s,this.scrollContainer=n.querySelector(".scroll"),this.wrapper=n.querySelector(".wrapper"),this.canvasWrapper=n.querySelector(".canvases"),this.progressWrapper=n.querySelector(".progress"),this.cursor=n.querySelector(".cursor"),e&&n.appendChild(e),this.initEvents()}parentFromOptionsContainer(t){let e;if(typeof t=="string"?e=document.querySelector(t):t instanceof HTMLElement&&(e=t),!e)throw new Error("Container not found");return e}initEvents(){const t=e=>{const i=this.wrapper.getBoundingClientRect(),s=e.clientX-i.left,n=e.clientY-i.top;return[s/i.width,n/i.height]};if(this.wrapper.addEventListener("click",e=>{const[i,s]=t(e);this.emit("click",i,s)}),this.wrapper.addEventListener("dblclick",e=>{const[i,s]=t(e);this.emit("dblclick",i,s)}),this.options.dragToSeek!==!0&&typeof this.options.dragToSeek!="object"||this.initDrag(),this.scrollContainer.addEventListener("scroll",()=>{const{scrollLeft:e,scrollWidth:i,clientWidth:s}=this.scrollContainer,n=e/i,r=(e+s)/i;this.emit("scroll",n,r,e,e+s)}),typeof ResizeObserver=="function"){const e=this.createDelay(100);this.resizeObserver=new ResizeObserver(()=>{e().then(()=>this.onContainerResize()).catch(()=>{})}),this.resizeObserver.observe(this.scrollContainer)}}onContainerResize(){const t=this.parent.clientWidth;t===this.lastContainerWidth&&this.options.height!=="auto"||(this.lastContainerWidth=t,this.reRender())}initDrag(){this.subscriptions.push(function(t,e,i,s,n=3,r=0,a=100){if(!t)return()=>{};const l=matchMedia("(pointer: coarse)").matches;let o=()=>{};const h=u=>{if(u.button!==r)return;u.preventDefault(),u.stopPropagation();let d=u.clientX,p=u.clientY,m=!1;const w=Date.now(),f=v=>{if(v.preventDefault(),v.stopPropagation(),l&&Date.now()-w<a)return;const S=v.clientX,x=v.clientY,P=S-d,M=x-p;if(m||Math.abs(P)>n||Math.abs(M)>n){const k=t.getBoundingClientRect(),{left:A,top:N}=k;m||(i==null||i(d-A,p-N),m=!0),e(P,M,S-A,x-N),d=S,p=x}},g=v=>{if(m){const S=v.clientX,x=v.clientY,P=t.getBoundingClientRect(),{left:M,top:k}=P;s==null||s(S-M,x-k)}o()},b=v=>{v.relatedTarget&&v.relatedTarget!==document.documentElement||g(v)},E=v=>{m&&(v.stopPropagation(),v.preventDefault())},C=v=>{m&&v.preventDefault()};document.addEventListener("pointermove",f),document.addEventListener("pointerup",g),document.addEventListener("pointerout",b),document.addEventListener("pointercancel",b),document.addEventListener("touchmove",C,{passive:!1}),document.addEventListener("click",E,{capture:!0}),o=()=>{document.removeEventListener("pointermove",f),document.removeEventListener("pointerup",g),document.removeEventListener("pointerout",b),document.removeEventListener("pointercancel",b),document.removeEventListener("touchmove",C),setTimeout(()=>{document.removeEventListener("click",E,{capture:!0})},10)}};return t.addEventListener("pointerdown",h),()=>{o(),t.removeEventListener("pointerdown",h)}}(this.wrapper,(t,e,i)=>{this.emit("drag",Math.max(0,Math.min(1,i/this.wrapper.getBoundingClientRect().width)))},t=>{this.isDragging=!0,this.emit("dragstart",Math.max(0,Math.min(1,t/this.wrapper.getBoundingClientRect().width)))},t=>{this.isDragging=!1,this.emit("dragend",Math.max(0,Math.min(1,t/this.wrapper.getBoundingClientRect().width)))}))}getHeight(t,e){var i;const s=((i=this.audioData)===null||i===void 0?void 0:i.numberOfChannels)||1;if(t==null)return 128;if(!isNaN(Number(t)))return Number(t);if(t==="auto"){const n=this.parent.clientHeight||128;return e!=null&&e.every(r=>!r.overlay)?n/s:n}return 128}initHtml(){const t=document.createElement("div"),e=t.attachShadow({mode:"open"}),i=this.options.cspNonce&&typeof this.options.cspNonce=="string"?this.options.cspNonce.replace(/"/g,""):"";return e.innerHTML=`
2
+ <style${i?` nonce="${i}"`:""}>
3
+ :host {
4
+ user-select: none;
5
+ min-width: 1px;
6
+ }
7
+ :host audio {
8
+ display: block;
9
+ width: 100%;
10
+ }
11
+ :host .scroll {
12
+ overflow-x: auto;
13
+ overflow-y: hidden;
14
+ width: 100%;
15
+ position: relative;
16
+ }
17
+ :host .noScrollbar {
18
+ scrollbar-color: transparent;
19
+ scrollbar-width: none;
20
+ }
21
+ :host .noScrollbar::-webkit-scrollbar {
22
+ display: none;
23
+ -webkit-appearance: none;
24
+ }
25
+ :host .wrapper {
26
+ position: relative;
27
+ overflow: visible;
28
+ z-index: 2;
29
+ }
30
+ :host .canvases {
31
+ min-height: ${this.getHeight(this.options.height,this.options.splitChannels)}px;
32
+ }
33
+ :host .canvases > div {
34
+ position: relative;
35
+ }
36
+ :host canvas {
37
+ display: block;
38
+ position: absolute;
39
+ top: 0;
40
+ image-rendering: pixelated;
41
+ }
42
+ :host .progress {
43
+ pointer-events: none;
44
+ position: absolute;
45
+ z-index: 2;
46
+ top: 0;
47
+ left: 0;
48
+ width: 0;
49
+ height: 100%;
50
+ overflow: hidden;
51
+ }
52
+ :host .progress > div {
53
+ position: relative;
54
+ }
55
+ :host .cursor {
56
+ pointer-events: none;
57
+ position: absolute;
58
+ z-index: 5;
59
+ top: 0;
60
+ left: 0;
61
+ height: 100%;
62
+ border-radius: 2px;
63
+ }
64
+ </style>
65
+
66
+ <div class="scroll" part="scroll">
67
+ <div class="wrapper" part="wrapper">
68
+ <div class="canvases" part="canvases"></div>
69
+ <div class="progress" part="progress"></div>
70
+ <div class="cursor" part="cursor"></div>
71
+ </div>
72
+ </div>
73
+ `,[t,e]}setOptions(t){if(this.options.container!==t.container){const e=this.parentFromOptionsContainer(t.container);e.appendChild(this.container),this.parent=e}t.dragToSeek!==!0&&typeof this.options.dragToSeek!="object"||this.initDrag(),this.options=t,this.reRender()}getWrapper(){return this.wrapper}getWidth(){return this.scrollContainer.clientWidth}getScroll(){return this.scrollContainer.scrollLeft}setScroll(t){this.scrollContainer.scrollLeft=t}setScrollPercentage(t){const{scrollWidth:e}=this.scrollContainer,i=e*t;this.setScroll(i)}destroy(){var t,e;this.subscriptions.forEach(i=>i()),this.container.remove(),(t=this.resizeObserver)===null||t===void 0||t.disconnect(),(e=this.unsubscribeOnScroll)===null||e===void 0||e.forEach(i=>i()),this.unsubscribeOnScroll=[]}createDelay(t=10){let e,i;const s=()=>{e&&clearTimeout(e),i&&i()};return this.timeouts.push(s),()=>new Promise((n,r)=>{s(),i=r,e=setTimeout(()=>{e=void 0,i=void 0,n()},t)})}convertColorValues(t){if(!Array.isArray(t))return t||"";if(t.length<2)return t[0]||"";const e=document.createElement("canvas"),i=e.getContext("2d"),s=e.height*(window.devicePixelRatio||1),n=i.createLinearGradient(0,0,0,s),r=1/(t.length-1);return t.forEach((a,l)=>{const o=l*r;n.addColorStop(o,a)}),n}getPixelRatio(){return Math.max(1,window.devicePixelRatio||1)}renderBarWaveform(t,e,i,s){const n=t[0],r=t[1]||t[0],a=n.length,{width:l,height:o}=i.canvas,h=o/2,u=this.getPixelRatio(),d=e.barWidth?e.barWidth*u:1,p=e.barGap?e.barGap*u:e.barWidth?d/2:0,m=e.barRadius||0,w=l/(d+p)/a,f=m&&"roundRect"in i?"roundRect":"rect";i.beginPath();let g=0,b=0,E=0;for(let C=0;C<=a;C++){const v=Math.round(C*w);if(v>g){const P=Math.round(b*h*s),M=P+Math.round(E*h*s)||1;let k=h-P;e.barAlign==="top"?k=0:e.barAlign==="bottom"&&(k=o-M),i[f](g*(d+p),k,d,M,m),g=v,b=0,E=0}const S=Math.abs(n[C]||0),x=Math.abs(r[C]||0);S>b&&(b=S),x>E&&(E=x)}i.fill(),i.closePath()}renderLineWaveform(t,e,i,s){const n=r=>{const a=t[r]||t[0],l=a.length,{height:o}=i.canvas,h=o/2,u=i.canvas.width/l;i.moveTo(0,h);let d=0,p=0;for(let m=0;m<=l;m++){const w=Math.round(m*u);if(w>d){const g=h+(Math.round(p*h*s)||1)*(r===0?-1:1);i.lineTo(d,g),d=w,p=0}const f=Math.abs(a[m]||0);f>p&&(p=f)}i.lineTo(d,h)};i.beginPath(),n(0),n(1),i.fill(),i.closePath()}renderWaveform(t,e,i){if(i.fillStyle=this.convertColorValues(e.waveColor),e.renderFunction)return void e.renderFunction(t,i);let s=e.barHeight||1;if(e.normalize){const n=Array.from(t[0]).reduce((r,a)=>Math.max(r,Math.abs(a)),0);s=n?1/n:1}e.barWidth||e.barGap||e.barAlign?this.renderBarWaveform(t,e,i,s):this.renderLineWaveform(t,e,i,s)}renderSingleCanvas(t,e,i,s,n,r,a){const l=this.getPixelRatio(),o=document.createElement("canvas");o.width=Math.round(i*l),o.height=Math.round(s*l),o.style.width=`${i}px`,o.style.height=`${s}px`,o.style.left=`${Math.round(n)}px`,r.appendChild(o);const h=o.getContext("2d");if(this.renderWaveform(t,e,h),o.width>0&&o.height>0){const u=o.cloneNode(),d=u.getContext("2d");d.drawImage(o,0,0),d.globalCompositeOperation="source-in",d.fillStyle=this.convertColorValues(e.progressColor),d.fillRect(0,0,o.width,o.height),a.appendChild(u)}}renderMultiCanvas(t,e,i,s,n,r){const a=this.getPixelRatio(),{clientWidth:l}=this.scrollContainer,o=i/a;let h=Math.min(T.MAX_CANVAS_WIDTH,l,o),u={};if(h===0)return;if(e.barWidth||e.barGap){const f=e.barWidth||.5,g=f+(e.barGap||f/2);h%g!=0&&(h=Math.floor(h/g)*g)}const d=f=>{if(f<0||f>=p||u[f])return;u[f]=!0;const g=f*h,b=Math.min(o-g,h);if(b<=0)return;const E=t.map(C=>{const v=Math.floor(g/o*C.length),S=Math.floor((g+b)/o*C.length);return C.slice(v,S)});this.renderSingleCanvas(E,e,b,s,g,n,r)},p=Math.ceil(o/h);if(!this.isScrollable){for(let f=0;f<p;f++)d(f);return}const m=this.scrollContainer.scrollLeft/o,w=Math.floor(m*p);if(d(w-1),d(w),d(w+1),p>1){const f=this.on("scroll",()=>{const{scrollLeft:g}=this.scrollContainer,b=Math.floor(g/o*p);Object.keys(u).length>T.MAX_NODES&&(n.innerHTML="",r.innerHTML="",u={}),d(b-1),d(b),d(b+1)});this.unsubscribeOnScroll.push(f)}}renderChannel(t,e,i,s){var{overlay:n}=e,r=function(h,u){var d={};for(var p in h)Object.prototype.hasOwnProperty.call(h,p)&&u.indexOf(p)<0&&(d[p]=h[p]);if(h!=null&&typeof Object.getOwnPropertySymbols=="function"){var m=0;for(p=Object.getOwnPropertySymbols(h);m<p.length;m++)u.indexOf(p[m])<0&&Object.prototype.propertyIsEnumerable.call(h,p[m])&&(d[p[m]]=h[p[m]])}return d}(e,["overlay"]);const a=document.createElement("div"),l=this.getHeight(r.height,r.splitChannels);a.style.height=`${l}px`,n&&s>0&&(a.style.marginTop=`-${l}px`),this.canvasWrapper.style.minHeight=`${l}px`,this.canvasWrapper.appendChild(a);const o=a.cloneNode();this.progressWrapper.appendChild(o),this.renderMultiCanvas(t,r,i,l,a,o)}render(t){return y(this,void 0,void 0,function*(){var e;this.timeouts.forEach(l=>l()),this.timeouts=[],this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="",this.options.width!=null&&(this.scrollContainer.style.width=typeof this.options.width=="number"?`${this.options.width}px`:this.options.width);const i=this.getPixelRatio(),s=this.scrollContainer.clientWidth,n=Math.ceil(t.duration*(this.options.minPxPerSec||0));this.isScrollable=n>s;const r=this.options.fillParent&&!this.isScrollable,a=(r?s:n)*i;if(this.wrapper.style.width=r?"100%":`${n}px`,this.scrollContainer.style.overflowX=this.isScrollable?"auto":"hidden",this.scrollContainer.classList.toggle("noScrollbar",!!this.options.hideScrollbar),this.cursor.style.backgroundColor=`${this.options.cursorColor||this.options.progressColor}`,this.cursor.style.width=`${this.options.cursorWidth}px`,this.audioData=t,this.emit("render"),this.options.splitChannels)for(let l=0;l<t.numberOfChannels;l++){const o=Object.assign(Object.assign({},this.options),(e=this.options.splitChannels)===null||e===void 0?void 0:e[l]);this.renderChannel([t.getChannelData(l)],o,a,l)}else{const l=[t.getChannelData(0)];t.numberOfChannels>1&&l.push(t.getChannelData(1)),this.renderChannel(l,this.options,a,0)}Promise.resolve().then(()=>this.emit("rendered"))})}reRender(){if(this.unsubscribeOnScroll.forEach(i=>i()),this.unsubscribeOnScroll=[],!this.audioData)return;const{scrollWidth:t}=this.scrollContainer,{right:e}=this.progressWrapper.getBoundingClientRect();if(this.render(this.audioData),this.isScrollable&&t!==this.scrollContainer.scrollWidth){const{right:i}=this.progressWrapper.getBoundingClientRect();let s=i-e;s*=2,s=s<0?Math.floor(s):Math.ceil(s),s/=2,this.scrollContainer.scrollLeft+=s}}zoom(t){this.options.minPxPerSec=t,this.reRender()}scrollIntoView(t,e=!1){const{scrollLeft:i,scrollWidth:s,clientWidth:n}=this.scrollContainer,r=t*s,a=i,l=i+n,o=n/2;if(this.isDragging)r+30>l?this.scrollContainer.scrollLeft+=30:r-30<a&&(this.scrollContainer.scrollLeft-=30);else{(r<a||r>l)&&(this.scrollContainer.scrollLeft=r-(this.options.autoCenter?o:0));const h=r-i-o;e&&this.options.autoCenter&&h>0&&(this.scrollContainer.scrollLeft+=Math.min(h,10))}{const h=this.scrollContainer.scrollLeft,u=h/s,d=(h+n)/s;this.emit("scroll",u,d,h,h+n)}}renderProgress(t,e){if(isNaN(t))return;const i=100*t;this.canvasWrapper.style.clipPath=`polygon(${i}% 0, 100% 0, 100% 100%, ${i}% 100%)`,this.progressWrapper.style.width=`${i}%`,this.cursor.style.left=`${i}%`,this.cursor.style.transform=`translateX(-${Math.round(i)===100?this.options.cursorWidth:0}px)`,this.isScrollable&&this.options.autoScroll&&this.scrollIntoView(t,e)}exportImage(t,e,i){return y(this,void 0,void 0,function*(){const s=this.canvasWrapper.querySelectorAll("canvas");if(!s.length)throw new Error("No waveform data");if(i==="dataURL"){const n=Array.from(s).map(r=>r.toDataURL(t,e));return Promise.resolve(n)}return Promise.all(Array.from(s).map(n=>new Promise((r,a)=>{n.toBlob(l=>{l?r(l):a(new Error("Could not export image"))},t,e)})))})}}T.MAX_CANVAS_WIDTH=8e3,T.MAX_NODES=10;class tt extends R{constructor(){super(...arguments),this.unsubscribe=()=>{}}start(){this.unsubscribe=this.on("tick",()=>{requestAnimationFrame(()=>{this.emit("tick")})}),this.emit("tick")}stop(){this.unsubscribe()}destroy(){this.unsubscribe()}}class O extends R{constructor(t=new AudioContext){super(),this.bufferNode=null,this.playStartTime=0,this.playedDuration=0,this._muted=!1,this._playbackRate=1,this._duration=void 0,this.buffer=null,this.currentSrc="",this.paused=!0,this.crossOrigin=null,this.seeking=!1,this.autoplay=!1,this.addEventListener=this.on,this.removeEventListener=this.un,this.audioContext=t,this.gainNode=this.audioContext.createGain(),this.gainNode.connect(this.audioContext.destination)}load(){return y(this,void 0,void 0,function*(){})}get src(){return this.currentSrc}set src(t){if(this.currentSrc=t,this._duration=void 0,!t)return this.buffer=null,void this.emit("emptied");fetch(t).then(e=>{if(e.status>=400)throw new Error(`Failed to fetch ${t}: ${e.status} (${e.statusText})`);return e.arrayBuffer()}).then(e=>this.currentSrc!==t?null:this.audioContext.decodeAudioData(e)).then(e=>{this.currentSrc===t&&(this.buffer=e,this.emit("loadedmetadata"),this.emit("canplay"),this.autoplay&&this.play())})}_play(){var t;if(!this.paused)return;this.paused=!1,(t=this.bufferNode)===null||t===void 0||t.disconnect(),this.bufferNode=this.audioContext.createBufferSource(),this.buffer&&(this.bufferNode.buffer=this.buffer),this.bufferNode.playbackRate.value=this._playbackRate,this.bufferNode.connect(this.gainNode);let e=this.playedDuration*this._playbackRate;(e>=this.duration||e<0)&&(e=0,this.playedDuration=0),this.bufferNode.start(this.audioContext.currentTime,e),this.playStartTime=this.audioContext.currentTime,this.bufferNode.onended=()=>{this.currentTime>=this.duration&&(this.pause(),this.emit("ended"))}}_pause(){var t;this.paused=!0,(t=this.bufferNode)===null||t===void 0||t.stop(),this.playedDuration+=this.audioContext.currentTime-this.playStartTime}play(){return y(this,void 0,void 0,function*(){this.paused&&(this._play(),this.emit("play"))})}pause(){this.paused||(this._pause(),this.emit("pause"))}stopAt(t){const e=t-this.currentTime,i=this.bufferNode;i==null||i.stop(this.audioContext.currentTime+e),i==null||i.addEventListener("ended",()=>{i===this.bufferNode&&(this.bufferNode=null,this.pause())},{once:!0})}setSinkId(t){return y(this,void 0,void 0,function*(){return this.audioContext.setSinkId(t)})}get playbackRate(){return this._playbackRate}set playbackRate(t){this._playbackRate=t,this.bufferNode&&(this.bufferNode.playbackRate.value=t)}get currentTime(){return(this.paused?this.playedDuration:this.playedDuration+(this.audioContext.currentTime-this.playStartTime))*this._playbackRate}set currentTime(t){const e=!this.paused;e&&this._pause(),this.playedDuration=t/this._playbackRate,e&&this._play(),this.emit("seeking"),this.emit("timeupdate")}get duration(){var t,e;return(t=this._duration)!==null&&t!==void 0?t:((e=this.buffer)===null||e===void 0?void 0:e.duration)||0}set duration(t){this._duration=t}get volume(){return this.gainNode.gain.value}set volume(t){this.gainNode.gain.value=t,this.emit("volumechange")}get muted(){return this._muted}set muted(t){this._muted!==t&&(this._muted=t,this._muted?this.gainNode.disconnect():this.gainNode.connect(this.audioContext.destination))}canPlayType(t){return/^(audio|video)\//.test(t)}getGainNode(){return this.gainNode}getChannelData(){const t=[];if(!this.buffer)return t;const e=this.buffer.numberOfChannels;for(let i=0;i<e;i++)t.push(this.buffer.getChannelData(i));return t}}const et={waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,dragToSeek:!1,autoScroll:!0,autoCenter:!0,sampleRate:8e3};class D extends Z{static create(t){return new D(t)}constructor(t){const e=t.media||(t.backend==="WebAudio"?new O:void 0);super({media:e,mediaControls:t.mediaControls,autoplay:t.autoplay,playbackRate:t.audioRate}),this.plugins=[],this.decodedData=null,this.stopAtPosition=null,this.subscriptions=[],this.mediaSubscriptions=[],this.abortController=null,this.options=Object.assign({},et,t),this.timer=new tt;const i=e?void 0:this.getMediaElement();this.renderer=new T(this.options,i),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initPlugins();const s=this.options.url||this.getSrc()||"";Promise.resolve().then(()=>{this.emit("init");const{peaks:n,duration:r}=this.options;(s||n&&r)&&this.load(s,n,r).catch(()=>null)})}updateProgress(t=this.getCurrentTime()){return this.renderer.renderProgress(t/this.getDuration(),this.isPlaying()),t}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",()=>{if(!this.isSeeking()){const t=this.updateProgress();this.emit("timeupdate",t),this.emit("audioprocess",t),this.stopAtPosition!=null&&this.isPlaying()&&t>=this.stopAtPosition&&this.pause()}}))}initPlayerEvents(){this.isPlaying()&&(this.emit("play"),this.timer.start()),this.mediaSubscriptions.push(this.onMediaEvent("timeupdate",()=>{const t=this.updateProgress();this.emit("timeupdate",t)}),this.onMediaEvent("play",()=>{this.emit("play"),this.timer.start()}),this.onMediaEvent("pause",()=>{this.emit("pause"),this.timer.stop(),this.stopAtPosition=null}),this.onMediaEvent("emptied",()=>{this.timer.stop(),this.stopAtPosition=null}),this.onMediaEvent("ended",()=>{this.emit("timeupdate",this.getDuration()),this.emit("finish"),this.stopAtPosition=null}),this.onMediaEvent("seeking",()=>{this.emit("seeking",this.getCurrentTime())}),this.onMediaEvent("error",()=>{var t;this.emit("error",(t=this.getMediaElement().error)!==null&&t!==void 0?t:new Error("Media error")),this.stopAtPosition=null}))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",(t,e)=>{this.options.interact&&(this.seekTo(t),this.emit("interaction",t*this.getDuration()),this.emit("click",t,e))}),this.renderer.on("dblclick",(t,e)=>{this.emit("dblclick",t,e)}),this.renderer.on("scroll",(t,e,i,s)=>{const n=this.getDuration();this.emit("scroll",t*n,e*n,i,s)}),this.renderer.on("render",()=>{this.emit("redraw")}),this.renderer.on("rendered",()=>{this.emit("redrawcomplete")}),this.renderer.on("dragstart",t=>{this.emit("dragstart",t)}),this.renderer.on("dragend",t=>{this.emit("dragend",t)}));{let t;this.subscriptions.push(this.renderer.on("drag",e=>{if(!this.options.interact)return;let i;this.renderer.renderProgress(e),clearTimeout(t),this.isPlaying()?i=0:this.options.dragToSeek===!0?i=200:typeof this.options.dragToSeek=="object"&&this.options.dragToSeek!==void 0&&(i=this.options.dragToSeek.debounceTime),t=setTimeout(()=>{this.seekTo(e)},i),this.emit("interaction",e*this.getDuration()),this.emit("drag",e)}))}}initPlugins(){var t;!((t=this.options.plugins)===null||t===void 0)&&t.length&&this.options.plugins.forEach(e=>{this.registerPlugin(e)})}unsubscribePlayerEvents(){this.mediaSubscriptions.forEach(t=>t()),this.mediaSubscriptions=[]}setOptions(t){this.options=Object.assign({},this.options,t),t.duration&&!t.peaks&&(this.decodedData=L.createBuffer(this.exportPeaks(),t.duration)),t.peaks&&t.duration&&(this.decodedData=L.createBuffer(t.peaks,t.duration)),this.renderer.setOptions(this.options),t.audioRate&&this.setPlaybackRate(t.audioRate),t.mediaControls!=null&&(this.getMediaElement().controls=t.mediaControls)}registerPlugin(t){return t._init(this),this.plugins.push(t),this.subscriptions.push(t.once("destroy",()=>{this.plugins=this.plugins.filter(e=>e!==t)})),t}getWrapper(){return this.renderer.getWrapper()}getWidth(){return this.renderer.getWidth()}getScroll(){return this.renderer.getScroll()}setScroll(t){return this.renderer.setScroll(t)}setScrollTime(t){const e=t/this.getDuration();this.renderer.setScrollPercentage(e)}getActivePlugins(){return this.plugins}loadAudio(t,e,i,s){return y(this,void 0,void 0,function*(){var n;if(this.emit("load",t),!this.options.media&&this.isPlaying()&&this.pause(),this.decodedData=null,this.stopAtPosition=null,!e&&!i){const a=this.options.fetchParams||{};window.AbortController&&!a.signal&&(this.abortController=new AbortController,a.signal=(n=this.abortController)===null||n===void 0?void 0:n.signal);const l=h=>this.emit("loading",h);e=yield Q.fetchBlob(t,l,a);const o=this.options.blobMimeType;o&&(e=new Blob([e],{type:o}))}this.setSrc(t,e);const r=yield new Promise(a=>{const l=s||this.getDuration();l?a(l):this.mediaSubscriptions.push(this.onMediaEvent("loadedmetadata",()=>a(this.getDuration()),{once:!0}))});if(!t&&!e){const a=this.getMediaElement();a instanceof O&&(a.duration=r)}if(i)this.decodedData=L.createBuffer(i,r||0);else if(e){const a=yield e.arrayBuffer();this.decodedData=yield L.decode(a,this.options.sampleRate)}this.decodedData&&(this.emit("decode",this.getDuration()),this.renderer.render(this.decodedData)),this.emit("ready",this.getDuration())})}load(t,e,i){return y(this,void 0,void 0,function*(){try{return yield this.loadAudio(t,void 0,e,i)}catch(s){throw this.emit("error",s),s}})}loadBlob(t,e,i){return y(this,void 0,void 0,function*(){try{return yield this.loadAudio("",t,e,i)}catch(s){throw this.emit("error",s),s}})}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(t),this.emit("zoom",t)}getDecodedData(){return this.decodedData}exportPeaks({channels:t=2,maxLength:e=8e3,precision:i=1e4}={}){if(!this.decodedData)throw new Error("The audio has not been decoded yet");const s=Math.min(t,this.decodedData.numberOfChannels),n=[];for(let r=0;r<s;r++){const a=this.decodedData.getChannelData(r),l=[],o=a.length/e;for(let h=0;h<e;h++){const u=a.slice(Math.floor(h*o),Math.ceil((h+1)*o));let d=0;for(let p=0;p<u.length;p++){const m=u[p];Math.abs(m)>Math.abs(d)&&(d=m)}l.push(Math.round(d*i)/i)}n.push(l)}return n}getDuration(){let t=super.getDuration()||0;return t!==0&&t!==1/0||!this.decodedData||(t=this.decodedData.duration),t}toggleInteraction(t){this.options.interact=t}setTime(t){this.stopAtPosition=null,super.setTime(t),this.updateProgress(t),this.emit("timeupdate",t)}seekTo(t){const e=this.getDuration()*t;this.setTime(e)}play(t,e){const i=Object.create(null,{play:{get:()=>super.play}});return y(this,void 0,void 0,function*(){t!=null&&this.setTime(t);const s=yield i.play.call(this);return e!=null&&(this.media instanceof O?this.media.stopAt(e):this.stopAtPosition=e),s})}playPause(){return y(this,void 0,void 0,function*(){return this.isPlaying()?this.pause():this.play()})}stop(){this.pause(),this.setTime(0)}skip(t){this.setTime(this.getCurrentTime()+t)}empty(){this.load("",[[0]],.001)}setMediaElement(t){this.unsubscribePlayerEvents(),super.setMediaElement(t),this.initPlayerEvents()}exportImage(){return y(this,arguments,void 0,function*(t="image/png",e=1,i="dataURL"){return this.renderer.exportImage(t,e,i)})}destroy(){var t;this.emit("destroy"),(t=this.abortController)===null||t===void 0||t.abort(),this.plugins.forEach(e=>e.destroy()),this.subscriptions.forEach(e=>e()),this.unsubscribePlayerEvents(),this.timer.destroy(),this.renderer.destroy(),super.destroy()}}D.BasePlugin=class extends R{constructor(c){super(),this.subscriptions=[],this.options=c}onInit(){}_init(c){this.wavesurfer=c,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach(c=>c())}},D.dom=K;class it{constructor(){this.listeners={}}on(t,e,i){if(this.listeners[t]||(this.listeners[t]=new Set),this.listeners[t].add(e),i==null?void 0:i.once){const s=()=>{this.un(t,s),this.un(t,e)};return this.on(t,s),s}return()=>this.un(t,e)}un(t,e){var i;(i=this.listeners[t])===null||i===void 0||i.delete(e)}once(t,e){return this.on(t,e,{once:!0})}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach(i=>i(...e))}}class st extends it{constructor(t){super(),this.subscriptions=[],this.options=t}onInit(){}_init(t){this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach(t=>t())}}function X(c,t){const e=t.xmlns?document.createElementNS(t.xmlns,c):document.createElement(c);for(const[i,s]of Object.entries(t))if(i==="children")for(const[n,r]of Object.entries(t))typeof r=="string"?e.appendChild(document.createTextNode(r)):e.appendChild(X(n,r));else i==="style"?Object.assign(e.style,s):i==="textContent"?e.textContent=s:e.setAttribute(i,s.toString());return e}function I(c,t,e){const i=X(c,t||{});return e==null||e.appendChild(i),i}const nt={lineWidth:1,labelSize:11,formatTimeCallback:c=>`${Math.floor(c/60)}:${`0${Math.floor(c)%60}`.slice(-2)}`};class _ extends st{constructor(t){super(t||{}),this.unsubscribe=()=>{},this.onPointerMove=e=>{if(!this.wavesurfer)return;const i=this.wavesurfer.getWrapper().getBoundingClientRect(),{width:s}=i,n=e.clientX-i.left,r=Math.min(1,Math.max(0,n/s)),a=Math.min(s-this.options.lineWidth-1,n);this.wrapper.style.transform=`translateX(${a}px)`,this.wrapper.style.opacity="1";const l=this.wavesurfer.getDuration()||0;this.label.textContent=this.options.formatTimeCallback(l*r);const o=this.label.offsetWidth;this.label.style.transform=a+o>s?`translateX(-${o+this.options.lineWidth}px)`:"",this.emit("hover",r)},this.onPointerLeave=()=>{this.wrapper.style.opacity="0"},this.options=Object.assign({},nt,t),this.wrapper=I("div",{part:"hover"}),this.label=I("span",{part:"hover-label"},this.wrapper)}static create(t){return new _(t)}addUnits(t){return`${t}${typeof t=="number"?"px":""}`}onInit(){if(!this.wavesurfer)throw Error("WaveSurfer is not initialized");const t=this.wavesurfer.options,e=this.options.lineColor||t.cursorColor||t.progressColor;Object.assign(this.wrapper.style,{position:"absolute",zIndex:10,left:0,top:0,height:"100%",pointerEvents:"none",borderLeft:`${this.addUnits(this.options.lineWidth)} solid ${e}`,opacity:"0",transition:"opacity .1s ease-in"}),Object.assign(this.label.style,{display:"block",backgroundColor:this.options.labelBackground,color:this.options.labelColor,fontSize:`${this.addUnits(this.options.labelSize)}`,transition:"transform .1s ease-in",padding:"2px 3px"});const i=this.wavesurfer.getWrapper();i.appendChild(this.wrapper),i.addEventListener("pointermove",this.onPointerMove),i.addEventListener("pointerleave",this.onPointerLeave),i.addEventListener("wheel",this.onPointerMove),this.unsubscribe=()=>{i.removeEventListener("pointermove",this.onPointerMove),i.removeEventListener("pointerleave",this.onPointerLeave),i.removeEventListener("wheel",this.onPointerLeave)}}destroy(){super.destroy(),this.unsubscribe(),this.wrapper.remove()}}const rt={class:"on-panda-audio-plugin"},ot=["src"],at={key:0,class:"fallback-message"},lt={__name:"AudioPlugin",props:{content:{type:Object,default:()=>({})}},setup(c){const t=c,e=W(null),i=W(null),s=W(!1);let n=null;const r=F(()=>V(t.content).base64_url),a=()=>{if(!(!e.value||!i.value||!r.value))try{n=D.create({container:e.value,waveColor:"rgba(255, 165, 0, 1)",progressColor:"rgba(192, 192, 192, 1)",media:i.value,normalize:!0,height:60,plugins:[_.create({lineColor:"#ff0000",lineWidth:2,labelBackground:"#555",labelColor:"#fff",labelSize:"11px"})]}),n.on("ready",()=>{s.value=!0,e.value.addEventListener("click",l=>{const o=e.value.getBoundingClientRect(),u=(l.clientX-o.left)/o.width;n.seekTo(u),i.value.play()})}),n.on("error",l=>{console.error("WaveSurfer error:",l),s.value=!1})}catch(l){console.error("Failed to initialize WaveSurfer:",l),s.value=!1}};return q(r,()=>{n&&(n.destroy(),n=null,s.value=!1),r.value&&setTimeout(a,100)},{immediate:!1}),G(()=>{r.value&&a()}),Y(()=>{n&&n.destroy()}),(l,o)=>($(),B("div",rt,[j("div",{ref_key:"waveformContainer",ref:e,class:"waveform-container"},null,512),j("audio",{ref_key:"audioElement",ref:i,src:r.value,controls:"",class:"audio-controls"},null,8,ot),!s.value&&r.value?($(),B("div",at," Waveform visualization unavailable - using audio controls only ")):J("",!0)]))}},dt=U(lt,[["__scopeId","data-v-0e4cb895"]]);export{dt as default};
@@ -0,0 +1 @@
1
+ .on-panda-audio-plugin[data-v-0e4cb895]{width:100%;margin-bottom:10px;margin-top:10px;background-color:#f9f9f9;border-radius:8px;box-shadow:0 2px 4px #0000001a}.waveform-container[data-v-0e4cb895]{width:100%;cursor:pointer}.audio-controls[data-v-0e4cb895]{width:100%}.fallback-message[data-v-0e4cb895]{font-size:.9em;color:#666;font-style:italic;margin-top:.5rem}
@@ -0,0 +1 @@
1
+ .on-panda-image-plugin[data-v-3a06bfde]{max-width:min(100% - 5px,512px);max-height:512px;box-shadow:#00000080 0 0 8px;margin-left:5px;margin-right:5px;cursor:zoom-in}.on-panda-image-plugin.rawSizeImg[data-v-3a06bfde]{max-width:initial;max-height:initial;position:relative;z-index:1}
@@ -0,0 +1 @@
1
+ import{_ as l,c as s,o as i,a as r}from"./index-VDofnkb1.js";const u=["src"],_={__name:"ImagePlugin",props:{content:{type:Object,default:()=>({})}},setup(a){const e=a,n=s(()=>e.content.blob_url||e.content.image_url.url);function c(t){t.target.classList.toggle("rawSizeImg")}function o(){window.open(n.value,"_blank")}return(t,p)=>(i(),r("img",{class:"on-panda-image-plugin",src:n.value,alt:"ON_PANDA_IMAGE",onClick:c,onDblclick:o},null,40,u))}},m=l(_,[["__scopeId","data-v-3a06bfde"]]);export{m as default};