Compare commits

..

5 commits

Author SHA1 Message Date
efbb7c0b11
chore: added support for preview
All checks were successful
Docs / build (push) Successful in 2m31s
2025-01-21 00:05:53 +03:00
2a3d7f1b7e
feat: support variable declarations in console 2025-01-20 16:38:45 +03:00
163bd11e7d
fix: devtools improvements 2025-01-20 16:16:21 +03:00
0dbedc27f3
fix: less truncation for monaco 2025-01-20 15:46:13 +03:00
5b941bb5a9
fix: avoid fouc for dark theme 2025-01-20 15:30:15 +03:00
10 changed files with 125 additions and 18 deletions

View file

@ -7,7 +7,8 @@
"scripts": {
"dev": "pnpm -r --parallel dev",
"build": "tsc -b && pnpm -r build",
"build:vendor": "cd vendor && bash build-patched-chobitsu.sh"
"build:vendor": "cd vendor && bash build-patched-chobitsu.sh",
"preview": "pnpm run -r --parallel preview"
},
"devDependencies": {
"@antfu/eslint-config": "^3.13.0",
@ -26,5 +27,10 @@
"typescript": "^5.7.3",
"vite": "^5.4.11",
"vite-plugin-solid": "^2.11.0"
},
"pnpm": {
"patchedDependencies": {
"monaco-editor@0.52.0": "patches/monaco-editor@0.52.0.patch"
}
}
}

View file

@ -8,6 +8,12 @@
<script defer src="https://zond.tei.su/script.js" data-website-id="e1726072-b61b-49ab-a4ee-decb8e6740be"></script>
</head>
<body>
<script>
const theme = localStorage.getItem('kb-color-mode')
if (theme === 'dark' || ((!theme || theme === 'system') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.style.colorScheme = 'dark'
}
</script>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>

View file

@ -52,6 +52,7 @@ const compilerOptions: languages.typescript.CompilerOptions = {
jsx: languages.typescript.JsxEmit.Preserve,
allowNonTsExtensions: true,
allowImportingTsExtensions: true,
noErrorTruncation: true,
}
languages.typescript.typescriptDefaults.setCompilerOptions(compilerOptions)

View file

@ -1,3 +1,4 @@
import { useColorMode } from '@kobalte/core'
import { createEffect, createSignal, onCleanup } from 'solid-js'
import { cn } from '../../lib/utils.ts'
@ -10,21 +11,50 @@ const HTML = `
<script src="https://unpkg.com/@ungap/custom-elements/es.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/chii@1.12.3/public/front_end/entrypoints/chii_app/chii_app.js"></script>
<body class="undocked" id="-blink-dev-tools">
<script>
if (window.location.hash.includes('dark=true')) {
document.documentElement.style.colorScheme = 'dark'
}
</script>
<style id="inject-css">
:root {
--sys-color-base-container: hsl(0 0% 100%);
--sys-color-cdt-base: hsl(0 0% 100%);
--sys-color-on-base-divider: hsl(240 5.9% 90%);
--sys-color-divider: hsl(240 5.9% 90%);
--sys-color-neutral-outline: hsl(240 5.9% 90%);
}
.-theme-with-dark-background {
--sys-color-base-container: hsl(240 10% 3.9%);
--sys-color-cdt-base: hsl(240 10% 3.9%);
--sys-color-on-base-divider: hsl(240 3.7% 15.9%);
--sys-color-divider: hsl(240 3.7% 15.9%);
--sys-color-neutral-outline: hsl(240 3.7% 15.9%);
--sys-color-on-surface-subtle: hsl(0 0 98%);
--sys-color-state-hover-on-subtle: hsl(0 0% 98% / 0.08);
}
</style>
<style id="inject-css-tab-pane">
.tabbed-pane-header-tab {
border-bottom: 2px solid transparent;
border-left: 0;
border-right: 0;
display: flex;
justify-content: center;
}
.tabbed-pane-header-tab.selected {
color: var(--sys-color-on-surface-subtle);
border-bottom: 2px solid var(--sys-color-on-surface-subtle);
border-left: 0;
border-right: 0;
}
.tabbed-pane-left-toolbar,
.tabbed-pane-right-toolbar,
.tabbed-pane-tab-slider,
.tabbed-pane-header-tab:not(#tab-console, #tab-sources) {
display: none;
}
</style>
`
const INJECTED_SCRIPT = `
@ -40,11 +70,18 @@ async function waitForElement(selector, container, waitForShadowRoot = false) {
}
function hideBySelector(root, selector) {
const el = root.shadowRoot.querySelector(selector);
const el = root.querySelector(selector);
if (!el) return;
el.style.display = 'none';
}
function hideBySelectorAll(root, selector) {
const els = root.querySelectorAll(selector);
for (const el of els) {
el.style.display = 'none';
}
}
async function focusConsole(tabbedPane) {
const consoleTab = await waitForElement('#tab-console', tabbedPane.shadowRoot);
@ -54,16 +91,17 @@ async function focusConsole(tabbedPane) {
}
(async () => {
localStorage.setItem('disableSelfXssWarning', 'true')
const tabbedPane = await waitForElement('.tabbed-pane', document.body);
await focusConsole(tabbedPane);
hideBySelector(tabbedPane, '.tabbed-pane-header');
tabbedPane.shadowRoot.appendChild(document.querySelector('#inject-css-tab-pane'));
const consoleToolbar = await waitForElement('.console-main-toolbar', document.body);
hideBySelector(consoleToolbar, 'devtools-issue-counter');
hideBySelector(consoleToolbar.shadowRoot, 'devtools-issue-counter');
const injectCss = await waitForElement('#inject-css', document.body);
const rootView = await waitForElement('.root-view', document.body);
rootView.appendChild(injectCss);
rootView.appendChild(document.querySelector('#inject-css'));
// forward some keyboard shortcuts to the parent window
document.addEventListener('keydown', (e) => {
@ -88,6 +126,8 @@ export function Devtools(props: {
}) {
const [innerRef, setInnerRef] = createSignal<HTMLIFrameElement | undefined>()
const { colorMode } = useColorMode()
const url = URL.createObjectURL(new Blob([HTML], { type: 'text/html' }))
onCleanup(() => URL.revokeObjectURL(url))
@ -101,6 +141,7 @@ export function Devtools(props: {
function handleLoad() {
const _innerRef = innerRef()
if (!_innerRef) return
const script = document.createElement('script')
script.textContent = INJECTED_SCRIPT
_innerRef.contentWindow?.document.body.appendChild(script)
@ -110,7 +151,7 @@ export function Devtools(props: {
<div class={cn('relative', props.class)}>
<iframe
ref={setInnerRef}
src={`${url}#?embedded=${encodeURIComponent(location.origin)}`}
src={`${url}#?embedded=${encodeURIComponent(location.origin)}&dark=${colorMode() === 'dark'}`}
title="Devtools"
class="absolute inset-0 block size-full"
onLoad={handleLoad}

View file

@ -13,6 +13,9 @@ export default defineConfig((env): UserConfig => {
optimizeDeps: {
exclude: ['@mtcute/wasm'],
},
preview: {
port: 3000,
},
build: {
rollupOptions: {
external: ['node:fs/promises', 'node:crypto'],

View file

@ -9,10 +9,6 @@ if (!window.parent || window.parent === window) {
throw new Error('Not in iframe')
}
if (new URL(document.referrer).origin !== import.meta.env.VITE_HOST_ORIGIN) {
throw new Error(`Invalid origin: this page must be loaded in an iframe from ${import.meta.env.VITE_HOST_ORIGIN}`)
}
const worker = new ReplWorker()
registerWorker(worker)

View file

@ -12,6 +12,9 @@ export default defineConfig((env): UserConfig => {
server: {
port: 3001,
},
preview: {
port: 3001,
},
optimizeDeps: {
exclude: ['@mtcute/wasm'],
},

View file

@ -0,0 +1,26 @@
diff --git a/dev/vs/language/typescript/tsWorker.js b/dev/vs/language/typescript/tsWorker.js
index cb194f17086a8fa0c6c15a125f3403af1a8e106f..7d6042767cc5a09262bd2ad9e313b2f252b28e3e 100644
--- a/dev/vs/language/typescript/tsWorker.js
+++ b/dev/vs/language/typescript/tsWorker.js
@@ -19471,7 +19471,7 @@ ${lanes.join("\n")}
init_ts2();
resolvingEmptyArray = [];
externalHelpersModuleNameText = "tslib";
- defaultMaximumTruncationLength = 160;
+ defaultMaximumTruncationLength = 1600;
noTruncationMaximumTruncationLength = 1e6;
stringWriter = createSingleLineStringWriter();
getScriptTargetFeatures = /* @__PURE__ */ memoize(
diff --git a/esm/vs/language/typescript/ts.worker.js b/esm/vs/language/typescript/ts.worker.js
index 2858333c5e9fe2cf3e59848c966be36031afac3d..549429bc362fdb86c4be8c15d77ccc7881d526a0 100644
--- a/esm/vs/language/typescript/ts.worker.js
+++ b/esm/vs/language/typescript/ts.worker.js
@@ -19453,7 +19453,7 @@ ${lanes.join("\n")}
init_ts2();
resolvingEmptyArray = [];
externalHelpersModuleNameText = "tslib";
- defaultMaximumTruncationLength = 160;
+ defaultMaximumTruncationLength = 1600;
noTruncationMaximumTruncationLength = 1e6;
stringWriter = createSingleLineStringWriter();
getScriptTargetFeatures = /* @__PURE__ */ memoize(

View file

@ -4,6 +4,11 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
patchedDependencies:
monaco-editor@0.52.0:
hash: etwqtd55ssq6d52kxjt3dxlzre
path: patches/monaco-editor@0.52.0.patch
importers:
.:
@ -100,13 +105,13 @@ importers:
version: 2.3.0
monaco-editor:
specifier: 0.52.0
version: 0.52.0
version: 0.52.0(patch_hash=etwqtd55ssq6d52kxjt3dxlzre)
monaco-editor-core:
specifier: 0.52.0
version: 0.52.0
monaco-editor-textmate:
specifier: ^4.0.0
version: 4.0.0(monaco-editor@0.52.0)(monaco-textmate@3.0.1(onigasm@2.2.5))
version: 4.0.0(monaco-editor@0.52.0(patch_hash=etwqtd55ssq6d52kxjt3dxlzre))(monaco-textmate@3.0.1(onigasm@2.2.5))
monaco-textmate:
specifier: ^3.0.1
version: 3.0.1(onigasm@2.2.5)
@ -5102,12 +5107,12 @@ snapshots:
monaco-editor-core@0.52.0: {}
monaco-editor-textmate@4.0.0(monaco-editor@0.52.0)(monaco-textmate@3.0.1(onigasm@2.2.5)):
monaco-editor-textmate@4.0.0(monaco-editor@0.52.0(patch_hash=etwqtd55ssq6d52kxjt3dxlzre))(monaco-textmate@3.0.1(onigasm@2.2.5)):
dependencies:
monaco-editor: 0.52.0
monaco-editor: 0.52.0(patch_hash=etwqtd55ssq6d52kxjt3dxlzre)
monaco-textmate: 3.0.1(onigasm@2.2.5)
monaco-editor@0.52.0: {}
monaco-editor@0.52.0(patch_hash=etwqtd55ssq6d52kxjt3dxlzre): {}
monaco-textmate@3.0.1(onigasm@2.2.5):
dependencies:

24
vendor/chobitsu.patch vendored
View file

@ -36,17 +36,35 @@ index 1dd6e97..0ea8300 100644
runIfWaitingForDebugger: noop,
})
diff --git a/src/lib/evaluate.ts b/src/lib/evaluate.ts
index 0732a13..f0abcce 100644
index 0732a13..b8f41ae 100644
--- a/src/lib/evaluate.ts
+++ b/src/lib/evaluate.ts
@@ -44,14 +44,14 @@ export function setGlobal(name: string, val: any) {
@@ -44,15 +44,34 @@ export function setGlobal(name: string, val: any) {
global[name] = val
}
-export default function evaluate(expression: string) {
+const createdInEvalIdents: Set<string> = new Set()
+export default async function evaluate(expression: string) {
let ret
+ // try to find variables that are declared in the expression
+ const vars = expression.match(/(?:;|^|\s)(?:const|let|var)\s+([a-zA-Z0-9_$]+)\s*=/g) ?? []
+ let varsInjectScript = ''
+ for (const var_ of vars) {
+ const name = var_.split(' ')[1].split('=')[0]
+ if (name in window && !createdInEvalIdents.has(name)) {
+ // avoid overriding existing globals
+ continue
+ }
+
+ createdInEvalIdents.add(name)
+ // todo: we should probably also handle multiple declarations but who even uses that?
+ varsInjectScript += `try{window[${JSON.stringify(name)}]=${name}}catch{};`
+ }
+ if (varsInjectScript) {
+ expression += ';' + varsInjectScript
+ }
injectGlobal()
try {
- ret = eval.call(window, `(${expression})`)
@ -55,8 +73,10 @@ index 0732a13..f0abcce 100644
- ret = eval.call(window, expression)
+ ret = await eval.call(window, `(async () => {${expression}})()`)
}
+
clearGlobal()
return ret
diff --git a/src/lib/objManager.ts b/src/lib/objManager.ts
index ff6a9e3..d79cdd6 100644
--- a/src/lib/objManager.ts