mirror of
https://github.com/misode/misode.github.io.git
synced 2026-05-02 07:55:29 +00:00
Make Spyglass a singleton object
This commit is contained in:
@@ -22,7 +22,7 @@ export function ErrorPanel({ error, prefix, reportable, onDismiss, body: body_,
|
||||
const [stack, setStack] = useState<string | undefined>(undefined)
|
||||
|
||||
const gen = getGenerator(getCurrentUrl())
|
||||
const source = gen ? spyglass?.getFile(spyglass.getUnsavedFileUri(gen)).doc?.getText() : undefined
|
||||
const source = gen ? spyglass?.getFileContents(spyglass.getUnsavedFileUri(version, gen)) : undefined
|
||||
const name = (prefix ?? '') + (error instanceof Error ? error.message : error)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -21,7 +21,7 @@ interface Props {
|
||||
export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
||||
const { locale } = useLocale()
|
||||
const { version, changeVersion, changeTargetVersion } = useVersion()
|
||||
const { spyglass, spyglassLoading } = useSpyglass()
|
||||
const { spyglass } = useSpyglass()
|
||||
const { projects, project, file, updateProject, updateFile, closeFile } = useProject()
|
||||
const [error, setError] = useState<Error | string | null>(null)
|
||||
const [errorBoundary, errorRetry] = useErrorBoundary()
|
||||
@@ -34,17 +34,14 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
||||
|
||||
const uri = useMemo(() => {
|
||||
// TODO: return different uri when project file is open
|
||||
return spyglass?.getUnsavedFileUri(gen)
|
||||
}, [spyglass, gen.id])
|
||||
return spyglass?.getUnsavedFileUri(version, gen)
|
||||
}, [spyglass, version, gen])
|
||||
|
||||
const [currentPreset, setCurrentPreset] = useSearchParam('preset')
|
||||
const [sharedSnippetId, setSharedSnippetId] = useSearchParam(SHARE_KEY)
|
||||
const ignoreChange = useRef(false)
|
||||
|
||||
const { value: docAndNode } = useAsync(async () => {
|
||||
if (spyglassLoading || !spyglass || !uri) {
|
||||
return AsyncCancel
|
||||
}
|
||||
let data: unknown = undefined
|
||||
if (currentPreset && sharedSnippetId) {
|
||||
setSharedSnippetId(undefined)
|
||||
@@ -86,10 +83,10 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) {
|
||||
data = file.data
|
||||
}
|
||||
// TODO: if data is undefined, set to generator's default
|
||||
const docAndNode = await spyglass.setFileContents(uri, data ? JSON.stringify(data) : undefined)
|
||||
const docAndNode = await spyglass.setFileContents(version, uri, data ? JSON.stringify(data) : undefined)
|
||||
Analytics.setGenerator(gen.id)
|
||||
return docAndNode
|
||||
}, [gen.id, version, sharedSnippetId, currentPreset, project.name, file?.id, spyglass, spyglassLoading])
|
||||
}, [gen.id, version, sharedSnippetId, currentPreset, project.name, file?.id, spyglass])
|
||||
|
||||
const { doc } = docAndNode ?? {}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { DocAndNode } from '@spyglassmc/core'
|
||||
import { fileUtil } from '@spyglassmc/core'
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
|
||||
import { useLocale } from '../../contexts/index.js'
|
||||
import { useLocale, useVersion } from '../../contexts/index.js'
|
||||
import { useDocAndNode } from '../../contexts/Spyglass.jsx'
|
||||
import { useLocalStorage } from '../../hooks/index.js'
|
||||
import { getSourceFormats, getSourceIndent, getSourceIndents, parseSource, sortData, stringifySource } from '../../services/index.js'
|
||||
@@ -28,6 +28,7 @@ type SourcePanelProps = {
|
||||
}
|
||||
export function SourcePanel({ spyglass, docAndNode, doCopy, doDownload, doImport, copySuccess, onError }: SourcePanelProps) {
|
||||
const { locale } = useLocale()
|
||||
const { version } = useVersion()
|
||||
const [indent, setIndent] = useState(Store.getIndent())
|
||||
const [format, setFormat] = useState(Store.getFormat())
|
||||
const [sort, setSort] = useLocalStorage('misode_output_sort', 'schema')
|
||||
@@ -77,7 +78,7 @@ export function SourcePanel({ spyglass, docAndNode, doCopy, doDownload, doImport
|
||||
if (!spyglass || !docAndNode) return
|
||||
try {
|
||||
const data = await parseSource(value, format)
|
||||
await spyglass.setFileContents(docAndNode.doc.uri, JSON.stringify(data))
|
||||
await spyglass.setFileContents(version, docAndNode.doc.uri, JSON.stringify(data))
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
e.message = `Error importing: ${e.message}`
|
||||
@@ -88,7 +89,7 @@ export function SourcePanel({ spyglass, docAndNode, doCopy, doDownload, doImport
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}, [spyglass, docAndNode, text, indent, format, sort, highlighting])
|
||||
}, [spyglass, version, docAndNode, text, indent, format, sort, highlighting])
|
||||
|
||||
useEffect(() => {
|
||||
if (highlighting) {
|
||||
|
||||
@@ -3,19 +3,20 @@ import type { ComponentChildren } from 'preact'
|
||||
import { createContext } from 'preact'
|
||||
import type { Inputs } from 'preact/hooks'
|
||||
import { useContext, useEffect, useState } from 'preact/hooks'
|
||||
import { useAsync } from '../hooks/useAsync.js'
|
||||
import { Spyglass } from '../services/Spyglass.js'
|
||||
import { useVersion } from './Version.jsx'
|
||||
|
||||
interface SpyglassContext {
|
||||
spyglass?: Spyglass,
|
||||
spyglassLoading: boolean,
|
||||
spyglass: Spyglass,
|
||||
}
|
||||
|
||||
const SpyglassContext = createContext<SpyglassContext | undefined>(undefined)
|
||||
|
||||
export function useSpyglass(): SpyglassContext {
|
||||
return useContext(SpyglassContext) ?? { spyglassLoading: true }
|
||||
const ctx = useContext(SpyglassContext)
|
||||
if (ctx === undefined) {
|
||||
throw new Error('Cannot use Spyglass context')
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
export function watchSpyglassUri(
|
||||
@@ -23,10 +24,10 @@ export function watchSpyglassUri(
|
||||
handler: (docAndNode: DocAndNode) => void,
|
||||
inputs: Inputs = [],
|
||||
) {
|
||||
const { spyglass, spyglassLoading } = useSpyglass()
|
||||
const { spyglass } = useSpyglass()
|
||||
|
||||
useEffect(() => {
|
||||
if (!uri || !spyglass || spyglassLoading) {
|
||||
if (!uri || !spyglass) {
|
||||
return
|
||||
}
|
||||
spyglass.watchFile(uri, handler)
|
||||
@@ -51,15 +52,10 @@ export function useDocAndNode(original: DocAndNode | undefined, inputs: Inputs =
|
||||
}
|
||||
|
||||
export function SpyglassProvider({ children }: { children: ComponentChildren }) {
|
||||
const { version } = useVersion()
|
||||
|
||||
const { value: spyglass, loading: spyglassLoading } = useAsync(() => {
|
||||
return Spyglass.initialize(version)
|
||||
}, [version])
|
||||
const [spyglass] = useState(new Spyglass())
|
||||
|
||||
const value: SpyglassContext = {
|
||||
spyglass,
|
||||
spyglassLoading,
|
||||
}
|
||||
|
||||
return <SpyglassContext.Provider value={value}>
|
||||
|
||||
@@ -16,50 +16,38 @@ import { fetchBlockStates, fetchRegistries, fetchVanillaMcdoc, getVersionChecksu
|
||||
import type { VersionId } from './Versions.js'
|
||||
|
||||
export class Spyglass {
|
||||
private static readonly INSTANCES = new Map<VersionId, Promise<Spyglass>>()
|
||||
|
||||
private readonly instances = new Map<VersionId, Promise<core.Service>>()
|
||||
private readonly watchers = new Map<string, ((docAndNode: core.DocAndNode) => void)[]>()
|
||||
|
||||
private constructor(
|
||||
private readonly service: core.Service,
|
||||
private readonly version: ConfigVersion,
|
||||
) {
|
||||
this.service.project.on('documentUpdated', (e) => {
|
||||
const uriWatchers = this.watchers.get(e.doc.uri) ?? []
|
||||
for (const handler of uriWatchers) {
|
||||
handler(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public async setFileContents(uri: string, contents?: string) {
|
||||
public async setFileContents(versionId: VersionId, uri: string, contents?: string) {
|
||||
const service = await this.getService(versionId)
|
||||
if (contents !== undefined) {
|
||||
await this.service.project.externals.fs.writeFile(uri, contents)
|
||||
await service.project.externals.fs.writeFile(uri, contents)
|
||||
} else {
|
||||
try {
|
||||
const buffer = await this.service.project.externals.fs.readFile(uri)
|
||||
const buffer = await service.project.externals.fs.readFile(uri)
|
||||
contents = new TextDecoder().decode(buffer)
|
||||
} catch (e) {
|
||||
contents = '{}'
|
||||
}
|
||||
}
|
||||
await this.service.project.onDidOpen(uri, 'json', 1, contents)
|
||||
const docAndNode = await this.service.project.ensureClientManagedChecked(uri)
|
||||
await service.project.onDidOpen(uri, 'json', 1, contents)
|
||||
const docAndNode = await service.project.ensureClientManagedChecked(uri)
|
||||
if (!docAndNode) {
|
||||
throw new Error('[Spyglass setFileContents] Cannot get doc and node')
|
||||
}
|
||||
return docAndNode
|
||||
}
|
||||
|
||||
public getFile(uri: string): Partial<core.DocAndNode> {
|
||||
return this.service.project.getClientManaged(uri) ?? {}
|
||||
public getFileContents(_uri: string): string | undefined {
|
||||
return undefined // TODO
|
||||
}
|
||||
|
||||
public getUnsavedFileUri(gen: ConfigGenerator) {
|
||||
public getUnsavedFileUri(versionId: VersionId, gen: ConfigGenerator) {
|
||||
if (gen.id === 'pack_mcmeta') {
|
||||
return 'file:///project/pack.mcmeta'
|
||||
}
|
||||
return `file:///project/data/draft/${genPath(gen, this.version.id)}/unsaved.json`
|
||||
return `file:///project/data/draft/${genPath(gen, versionId)}/unsaved.json`
|
||||
}
|
||||
|
||||
public watchFile(uri: string, handler: (docAndNode: core.DocAndNode) => void) {
|
||||
@@ -73,8 +61,8 @@ export class Spyglass {
|
||||
uriWatchers.splice(index, 1)
|
||||
}
|
||||
|
||||
public static async initialize(versionId: VersionId) {
|
||||
const instance = this.INSTANCES.get(versionId)
|
||||
private async getService(versionId: VersionId) {
|
||||
const instance = this.instances.get(versionId)
|
||||
if (instance) {
|
||||
return instance
|
||||
}
|
||||
@@ -107,9 +95,15 @@ export class Spyglass {
|
||||
})
|
||||
await service.project.ready()
|
||||
await service.project.cacheService.save()
|
||||
return new Spyglass(service, version)
|
||||
service.project.on('documentUpdated', (e) => {
|
||||
const uriWatchers = this.watchers.get(e.doc.uri) ?? []
|
||||
for (const handler of uriWatchers) {
|
||||
handler(e)
|
||||
}
|
||||
})
|
||||
return service
|
||||
})()
|
||||
this.INSTANCES.set(versionId, promise)
|
||||
this.instances.set(versionId, promise)
|
||||
return promise
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user