enso/app/gui2/mock/MockFSWrapper.vue
Kaz Wesley d7689b3357
Show images in documentation (#10205)
Render markdown image syntax in documentation. Relative URLs are fetched from the project directory; absolute URLs are hotlinked from wherever they point.

Syntax and URL resolution:
```
# In New_Project27/src/Main.enso

## Main method docs
- Image at `New_Project27/src/image.jpg`: ![Image](image.jpg)
- Image at `New_Project27/image.png`: ![Image](../image.png)
- Image at `New_Project27/src/image.jpg`: ![Image](/src/image.jpg)
- Image at `New_Project27/image.png`: ![Image](/image.png)
main =
42
```

https://github.com/enso-org/enso/assets/1047859/3f873f3f-31b1-44bf-ae3e-2f467f2d546d

Closes #10058.

# Important Notes
Stacked on #10064.
2024-06-10 14:15:36 +00:00

96 lines
2.8 KiB
Vue

<script setup lang="ts">
import { useProjectStore } from '@/stores/project'
import { mockFsDirectoryHandle } from '@/util/convert/fsAccess'
import { MockWebSocket, type WebSocketHandler } from '@/util/net'
import { type Path as LSPath } from 'shared/languageServerTypes'
import { watchEffect } from 'vue'
import { mockDataWSHandler } from './dataServer'
const projectStore = useProjectStore()
interface FileTree {
[name: string]: FileTree | string | ArrayBuffer
}
const props = defineProps<{
files: FileTree | undefined
directory: FileSystemDirectoryHandle | undefined
/** The path of the root directory. */
prefix?: string[] | undefined
}>()
let resolveDataWsHandler: ((handler: WebSocketHandler) => void) | undefined
let dataWsHandler: Promise<WebSocketHandler> = new Promise((resolve) => {
resolveDataWsHandler = resolve
})
function setDataWsHandler(handler: WebSocketHandler) {
if (resolveDataWsHandler) {
resolveDataWsHandler(handler)
resolveDataWsHandler = undefined
} else {
dataWsHandler = Promise.resolve(handler)
}
}
MockWebSocket.addMock('data', async (data, send) => {
;(await dataWsHandler)(data, send)
})
watchEffect(async (onCleanup) => {
let maybeDirectory = props.files ? mockFsDirectoryHandle(props.files, '(root)') : props.directory
if (!maybeDirectory) return
const prefixLength = props.prefix?.length ?? 0
const directory = maybeDirectory
const ls = await projectStore.lsRpcConnection
const maybeProjectRoot = await projectStore.projectRootId
if (!maybeProjectRoot) return
const projectRoot = maybeProjectRoot
async function walkFiles(
dir: FileSystemDirectoryHandle,
segments: string[],
cb: (path: LSPath) => void,
) {
for await (const [name, dirOrFile] of dir.entries()) {
const newSegments = [...segments, name]
if (dirOrFile.kind === 'directory') walkFiles(dirOrFile, newSegments, cb)
else {
cb({
rootId: projectRoot,
segments: newSegments.slice(prefixLength),
})
}
}
}
walkFiles(directory, props.prefix ?? [], (path) =>
ls.emit('file/event', [{ kind: 'Added', path }]),
)
onCleanup(() => {
walkFiles(directory, props.prefix ?? [], (path) =>
ls.emit('file/event', [{ kind: 'Removed', path }]),
)
})
setDataWsHandler(
mockDataWSHandler(async (segments) => {
segments = segments.slice(prefixLength)
if (!segments.length) return
let file
try {
let dir = directory
for (const segment of segments.slice(0, -1)) {
dir = await dir.getDirectoryHandle(segment)
}
const fileHandle = await dir.getFileHandle(segments.at(-1)!)
file = await fileHandle.getFile()
} catch {
return
}
return await file?.arrayBuffer()
}),
)
})
</script>
<template>
<slot></slot>
</template>