diff --git a/package-lock.json b/package-lock.json index 4e2891eb..f036daa6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3189,84 +3189,6 @@ "node": ">= 10.0.0" } }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", - "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", - "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", - "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", - "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", - "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", - "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@neuralnomads/codenomad": { "resolved": "packages/server", "link": true @@ -3307,47 +3229,6 @@ "node": ">= 8" } }, - "node_modules/@opencode-ai/plugin": { - "version": "1.14.19", - "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.14.19.tgz", - "integrity": "sha512-g0C8Viocybmet7nBqJK/1xrQnacRS1f30VmqRTPScPmWz+4knIZzc2TEQp8+920sN8rB6BuoGwfBUVRXJmavhQ==", - "license": "MIT", - "dependencies": { - "@opencode-ai/sdk": "1.14.19", - "effect": "4.0.0-beta.48", - "zod": "4.1.8" - }, - "peerDependencies": { - "@opentui/core": ">=0.1.101", - "@opentui/solid": ">=0.1.101" - }, - "peerDependenciesMeta": { - "@opentui/core": { - "optional": true - }, - "@opentui/solid": { - "optional": true - } - } - }, - "node_modules/@opencode-ai/plugin/node_modules/@opencode-ai/sdk": { - "version": "1.14.19", - "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.14.19.tgz", - "integrity": "sha512-9sTGsi8/HlBBeaWfsUjdJ2yi/SqpRvqSld0IFXc3ldaPb1w1uIPvgCGzhlHYQtqatXxSaX5lTN7zpudMaE21aw==", - "license": "MIT", - "dependencies": { - "cross-spawn": "7.0.6" - } - }, - "node_modules/@opencode-ai/plugin/node_modules/zod": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz", - "integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@opencode-ai/sdk": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.2.6.tgz", @@ -3952,12 +3833,6 @@ "solid-js": "^1.8.6" } }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, "node_modules/@suid/base": { "version": "0.11.0", "license": "MIT", @@ -6028,7 +5903,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -6213,24 +6088,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/effect": { - "version": "4.0.0-beta.48", - "resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.48.tgz", - "integrity": "sha512-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw==", - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "fast-check": "^4.6.0", - "find-my-way-ts": "^0.1.6", - "ini": "^6.0.0", - "kubernetes-types": "^1.30.0", - "msgpackr": "^1.11.9", - "multipasta": "^0.2.7", - "toml": "^4.1.1", - "uuid": "^13.0.0", - "yaml": "^2.8.3" - } - }, "node_modules/ejs": { "version": "3.1.10", "dev": true, @@ -6742,28 +6599,6 @@ "license": "MIT", "optional": true }, - "node_modules/fast-check": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.7.0.tgz", - "integrity": "sha512-NsZRtqvSSoCP0HbNjUD+r1JH8zqZalyp6gLY9e7OYs7NK9b6AHOs2baBFeBG7bVNsuoukh89x2Yg3rPsul8ziQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT", - "dependencies": { - "pure-rand": "^8.0.0" - }, - "engines": { - "node": ">=12.17.0" - } - }, "node_modules/fast-content-type-parse": { "version": "1.1.0", "license": "MIT" @@ -6994,12 +6829,6 @@ "node": ">=14" } }, - "node_modules/find-my-way-ts": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/find-my-way-ts/-/find-my-way-ts-0.1.6.tgz", - "integrity": "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==", - "license": "MIT" - }, "node_modules/find-up": { "version": "4.1.0", "license": "MIT", @@ -7809,15 +7638,6 @@ "version": "2.0.4", "license": "ISC" }, - "node_modules/ini": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", - "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -8487,12 +8307,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/kubernetes-types": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/kubernetes-types/-/kubernetes-types-1.30.0.tgz", - "integrity": "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==", - "license": "Apache-2.0" - }, "node_modules/lazy-val": { "version": "1.0.5", "dev": true, @@ -8956,43 +8770,6 @@ "version": "2.1.3", "license": "MIT" }, - "node_modules/msgpackr": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.10.tgz", - "integrity": "sha512-iCZNq+HszvF+fC3anCm4nBmWEnbeIAfpDs6IStAEKhQ2YSgkjzVG2FF9XJqwwQh5bH3N9OUTUt4QwVN6MLMLtA==", - "license": "MIT", - "optionalDependencies": { - "msgpackr-extract": "^3.0.2" - } - }, - "node_modules/msgpackr-extract": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", - "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-gyp-build-optional-packages": "5.2.2" - }, - "bin": { - "download-msgpackr-prebuilds": "bin/download-prebuilds.js" - }, - "optionalDependencies": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" - } - }, - "node_modules/multipasta": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/multipasta/-/multipasta-0.2.7.tgz", - "integrity": "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA==", - "license": "MIT" - }, "node_modules/mz": { "version": "2.7.0", "dev": true, @@ -9068,21 +8845,6 @@ "node": ">= 6.13.0" } }, - "node_modules/node-gyp-build-optional-packages": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", - "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", - "license": "MIT", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.1" - }, - "bin": { - "node-gyp-build-optional-packages": "bin.js", - "node-gyp-build-optional-packages-optional": "optional.js", - "node-gyp-build-optional-packages-test": "build-test.js" - } - }, "node_modules/node-releases": { "version": "2.0.27", "dev": true, @@ -9689,22 +9451,6 @@ "node": ">=6" } }, - "node_modules/pure-rand": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.0.tgz", - "integrity": "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, "node_modules/qrcode": { "version": "1.5.4", "license": "MIT", @@ -11477,15 +11223,6 @@ "node": ">=0.6" } }, - "node_modules/toml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/toml/-/toml-4.1.1.tgz", - "integrity": "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw==", - "license": "MIT", - "engines": { - "node": ">=20" - } - }, "node_modules/tr46": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", @@ -11951,19 +11688,6 @@ "dev": true, "license": "MIT" }, - "node_modules/uuid": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", - "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist-node/bin/uuid" - } - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "dev": true, @@ -13463,7 +13187,44 @@ "version": "0.5.0", "license": "MIT", "dependencies": { - "@opencode-ai/plugin": "1.14.19" + "@opencode-ai/plugin": "1.3.7" + } + }, + "packages/opencode-config/node_modules/@opencode-ai/plugin": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.3.7.tgz", + "integrity": "sha512-pVBIcYtHiniQ93Gj/KRkhrIz1oIAwGRifb7+dfGWdHRy00gr9DyEHFYmgHcBYgfrBavZrWw2xmqEDJdjdBuC7g==", + "license": "MIT", + "dependencies": { + "@opencode-ai/sdk": "1.3.7", + "zod": "4.1.8" + }, + "peerDependencies": { + "@opentui/core": ">=0.1.92", + "@opentui/solid": ">=0.1.92" + }, + "peerDependenciesMeta": { + "@opentui/core": { + "optional": true + }, + "@opentui/solid": { + "optional": true + } + } + }, + "packages/opencode-config/node_modules/@opencode-ai/sdk": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.3.7.tgz", + "integrity": "sha512-ugkta0v0dMZchN15QGmqHb9zf35k+K1VM9wt3x4ZRJ6GxKAs0XlCmQPQJflgV9YSedNxjkgTud0GCCIWUSiUOg==", + "license": "MIT" + }, + "packages/opencode-config/node_modules/zod": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz", + "integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" } }, "packages/server": { diff --git a/packages/ui/src/components/file-viewer/monaco-file-viewer.tsx b/packages/ui/src/components/file-viewer/monaco-file-viewer.tsx index 4816f225..539174f3 100644 --- a/packages/ui/src/components/file-viewer/monaco-file-viewer.tsx +++ b/packages/ui/src/components/file-viewer/monaco-file-viewer.tsx @@ -9,6 +9,7 @@ interface MonacoFileViewerProps { scopeKey: string path: string content: string + wordWrap?: "on" | "off" onSave?: (content: string) => void onContentChange?: (content: string) => void } @@ -84,6 +85,11 @@ export function MonacoFileViewer(props: MonacoFileViewerProps) { monaco.editor.setTheme(isDark() ? "vs-dark" : "vs") }) + createEffect(() => { + if (!ready() || !editor) return + editor.updateOptions({ wordWrap: props.wordWrap === "on" ? "on" : "off" }) + }) + createEffect(() => { if (!ready() || !monaco || !editor) return const languageId = inferMonacoLanguageId(monaco, props.path) diff --git a/packages/ui/src/components/instance/shell/right-panel/RightPanel.tsx b/packages/ui/src/components/instance/shell/right-panel/RightPanel.tsx index 0a9ba0b7..26afb3f6 100644 --- a/packages/ui/src/components/instance/shell/right-panel/RightPanel.tsx +++ b/packages/ui/src/components/instance/shell/right-panel/RightPanel.tsx @@ -42,6 +42,7 @@ import { RIGHT_PANEL_CHANGES_DIFF_WORD_WRAP_KEY, RIGHT_PANEL_CHANGES_LIST_OPEN_NONPHONE_KEY, RIGHT_PANEL_CHANGES_LIST_OPEN_PHONE_KEY, + RIGHT_PANEL_FILES_WORD_WRAP_KEY, RIGHT_PANEL_CHANGES_SPLIT_WIDTH_KEY, RIGHT_PANEL_FILES_LIST_OPEN_NONPHONE_KEY, RIGHT_PANEL_FILES_LIST_OPEN_PHONE_KEY, @@ -131,6 +132,9 @@ const RightPanel: Component = (props) => { const [diffWordWrapMode, setDiffWordWrapMode] = createSignal( readStoredEnum(RIGHT_PANEL_CHANGES_DIFF_WORD_WRAP_KEY, ["on", "off"] as const) ?? "on", ) + const [filesWordWrapMode, setFilesWordWrapMode] = createSignal( + readStoredEnum(RIGHT_PANEL_FILES_WORD_WRAP_KEY, ["on", "off"] as const) ?? "off", + ) const [changesSplitWidth, setChangesSplitWidth] = createSignal(320) const [filesSplitWidth, setFilesSplitWidth] = createSignal(320) @@ -254,6 +258,11 @@ const RightPanel: Component = (props) => { window.localStorage.setItem(RIGHT_PANEL_CHANGES_DIFF_WORD_WRAP_KEY, diffWordWrapMode()) }) + createEffect(() => { + if (typeof window === "undefined") return + window.localStorage.setItem(RIGHT_PANEL_FILES_WORD_WRAP_KEY, filesWordWrapMode()) + }) + const clampSplitWidth = (value: number) => { const min = 200 const maxByDrawer = Math.max(min, Math.floor(props.rightDrawerWidth() * 0.65)) @@ -912,6 +921,7 @@ const RightPanel: Component = (props) => { browserSelectedError={browserSelectedError} browserSelectedDirty={browserSelectedDirty} browserSelectedSaving={browserSelectedSaving} + wordWrapMode={filesWordWrapMode} parentPath={browserParentPath} scopeKey={browserScopeKey} onLoadEntries={(path: string) => void loadBrowserEntries(path)} @@ -919,6 +929,7 @@ const RightPanel: Component = (props) => { onRefresh={() => void refreshFilesTab()} onSave={(content: string) => void saveBrowserFile(content)} onContentChange={(content: string) => handleBrowserFileChange(content)} + onWordWrapModeChange={setFilesWordWrapMode} listOpen={filesListOpen} onToggleList={toggleFilesList} splitWidth={filesSplitWidth} diff --git a/packages/ui/src/components/instance/shell/right-panel/tabs/FilesTab.tsx b/packages/ui/src/components/instance/shell/right-panel/tabs/FilesTab.tsx index 36b1156f..b9f6ceec 100644 --- a/packages/ui/src/components/instance/shell/right-panel/tabs/FilesTab.tsx +++ b/packages/ui/src/components/instance/shell/right-panel/tabs/FilesTab.tsx @@ -1,16 +1,23 @@ import { For, Show, Suspense, createEffect, createMemo, createSignal, lazy, type Accessor, type Component, type JSX } from "solid-js" import type { FileNode } from "@opencode-ai/sdk/v2/client" -import { Copy, RefreshCw, Save, Search } from "lucide-solid" +import { Copy, RefreshCw, Save, Search, WrapText } from "lucide-solid" import SplitFilePanel from "../components/SplitFilePanel" +import { Markdown } from "../../../../markdown" import { copyToClipboard } from "../../../../../lib/clipboard" import { showToastNotification } from "../../../../../lib/notifications" +import { useTheme } from "../../../../../lib/theme" const LazyMonacoFileViewer = lazy(() => import("../../../../file-viewer/monaco-file-viewer").then((module) => ({ default: module.MonacoFileViewer })), ) +function isMarkdownPath(path: string | null | undefined): boolean { + if (!path) return false + return /\.(md|markdown|mdown|mkdn)$/i.test(path) +} + interface FilesTabProps { t: (key: string, vars?: Record) => string @@ -25,6 +32,7 @@ interface FilesTabProps { browserSelectedError: Accessor browserSelectedDirty: Accessor browserSelectedSaving: Accessor + wordWrapMode: Accessor<"on" | "off"> parentPath: Accessor scopeKey: Accessor @@ -34,6 +42,7 @@ interface FilesTabProps { onRefresh: () => void onSave: (content: string) => void onContentChange: (content: string) => void + onWordWrapModeChange: (mode: "on" | "off") => void listOpen: Accessor onToggleList: () => void @@ -45,6 +54,9 @@ interface FilesTabProps { const FilesTab: Component = (props) => { const [filterQuery, setFilterQuery] = createSignal("") + const { isDark } = useTheme() + const [markdownPreviewEnabled, setMarkdownPreviewEnabled] = createSignal(false) + let markdownPreviewRef: HTMLDivElement | undefined createEffect(() => { props.browserPath() @@ -78,6 +90,14 @@ const FilesTab: Component = (props) => { const listEmptyMessage = () => normalizedQuery() ? props.t("instanceShell.filesShell.search.empty") : props.t("instanceShell.filesShell.listEmpty") + const selectedMarkdownFile = createMemo(() => isMarkdownPath(props.browserSelectedPath())) + const showingMarkdownPreview = createMemo(() => selectedMarkdownFile() && markdownPreviewEnabled()) + + createEffect(() => { + if (!selectedMarkdownFile()) { + setMarkdownPreviewEnabled(false) + } + }) const handleSave = () => { const content = props.browserSelectedContent() if (content !== undefined && content !== null) { @@ -94,6 +114,11 @@ const FilesTab: Component = (props) => { }) } + createEffect(() => { + if (!showingMarkdownPreview()) return + requestAnimationFrame(() => markdownPreviewRef?.focus()) + }) + const FileList: Component = () => ( <>
@@ -182,6 +207,13 @@ const FilesTab: Component = (props) => { ) + const handleMarkdownPreviewKeyDown = (event: KeyboardEvent) => { + if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== "s") return + if (props.browserSelectedSaving() || !props.browserSelectedDirty()) return + event.preventDefault() + handleSave() + } + const renderContent = (): JSX.Element => { const headerDisplayedPath = () => props.browserSelectedPath() || props.browserPath() @@ -192,7 +224,7 @@ const FilesTab: Component = (props) => { const renderViewer = () => (
-
+
= (props) => { } > {(payload) => ( - - {props.t("instanceInfo.loading")} -
+ + {props.t("instanceInfo.loading")} +
+ } + > + + } > - - +
markdownPreviewRef?.focus()} + > + +
+ )} } @@ -262,13 +310,33 @@ const FilesTab: Component = (props) => { {(err) => {err()}}
+ +