From 334df85877aa3b0e0159a8c390230fc7ac939a9d Mon Sep 17 00:00:00 2001 From: Dannii Willis Date: Wed, 17 Jul 2024 13:34:09 +1000 Subject: [PATCH] Can now save/restore using a new Svelte dialog --- src/dialog/browser/browser.ts | 22 +++- src/dialog/browser/common.ts | 100 +++++++++++++++++ src/dialog/browser/download.ts | 11 +- src/dialog/browser/interface.ts | 39 +++---- src/dialog/browser/storage.ts | 44 +++++--- src/dialog/browser/ui/Dialog.svelte | 124 ++++++++++++++++++++++ src/dialog/browser/ui/FileList.svelte | 48 +++++++++ src/dialog/browser/ui/FileListItem.svelte | 40 +++++++ src/dialog/common/cache.ts | 6 +- src/dialog/common/common.ts | 2 +- src/dialog/common/interface.ts | 4 +- src/dialog/node/async.ts | 4 +- src/dialog/node/cheap.ts | 6 +- src/dialog/node/node-streaming.ts | 6 +- src/glkapi/common.ts | 2 +- src/glkapi/filerefs.ts | 6 +- src/glkapi/glkapi.ts | 17 +-- src/glkapi/interface.ts | 6 +- src/glkapi/lib_constants.ts | 2 +- src/glkapi/streams.ts | 6 +- src/glkapi/windows.ts | 6 +- src/glkote/cheap/cheap.ts | 2 +- src/glkote/common/glkote.ts | 4 +- src/glkote/web/input.ts | 2 +- src/glkote/web/transcript-recorder.ts | 2 +- src/glkote/web/windows.ts | 2 +- tsconfig.json | 3 +- 27 files changed, 420 insertions(+), 96 deletions(-) create mode 100644 src/dialog/browser/common.ts create mode 100644 src/dialog/browser/ui/Dialog.svelte create mode 100644 src/dialog/browser/ui/FileList.svelte create mode 100644 src/dialog/browser/ui/FileListItem.svelte diff --git a/src/dialog/browser/browser.ts b/src/dialog/browser/browser.ts index 877bcf4..032ee6b 100644 --- a/src/dialog/browser/browser.ts +++ b/src/dialog/browser/browser.ts @@ -9,13 +9,15 @@ https://github.com/curiousdannii/asyncglk */ -import {AsyncDialog, DialogDirectories, DialogOptions} from '../common/interface.js' -import {DownloadOptions, DownloadProvider, ProgressCallback} from './download.js' -import {Provider} from './interface.js' +import type {AsyncDialog, DialogDirectories, DialogOptions} from '../common/interface.js' +import {type DownloadOptions, DownloadProvider, type ProgressCallback} from './download.js' +import type {Provider} from './interface.js' import {WebStorageProvider} from './storage.js' +import DialogUI from './ui/Dialog.svelte' export class BrowserDialog implements AsyncDialog { 'async' = true as const + private dialog: DialogUI | undefined private dirs: DialogDirectories = { storyfile: '', system_cwd: '/usr', @@ -26,12 +28,15 @@ export class BrowserDialog implements AsyncDialog { private providers: Provider[] = [] async init(options: DialogOptions & DownloadOptions): Promise { + this.dialog = new DialogUI({ + target: document.body, + }) this.downloader = new DownloadProvider(options) // TODO: ensure that localStorage is wrapped in a try/catch in case it's disabled this.providers = [ this.downloader, new WebStorageProvider('/tmp', sessionStorage), - new WebStorageProvider('/', localStorage), + new WebStorageProvider('/', localStorage, true), ] for (const [i, provider] of this.providers.entries()) { @@ -51,7 +56,14 @@ export class BrowserDialog implements AsyncDialog { } async prompt(extension: string, save: boolean): Promise { - return prompt('Filename') + const dir_browser = await this.providers[0].browse() + return this.dialog!.prompt({ + dir: '/usr', + dir_browser, + save, + submit_label: save ? 'Save' : 'Restore', + title: 'Filename', + }) } set_storyfile_dir(path: string): Partial { diff --git a/src/dialog/browser/common.ts b/src/dialog/browser/common.ts new file mode 100644 index 0000000..d3e9acc --- /dev/null +++ b/src/dialog/browser/common.ts @@ -0,0 +1,100 @@ +/* + +Common implementations +====================== + +Copyright (c) 2024 Dannii Willis +MIT licenced +https://github.com/curiousdannii/asyncglk + +*/ + +import path from 'path-browserify-esm' + +import type {Provider, DirBrowser, FileData, DirEntry} from './interface.js' + +export class NullProvider implements Provider { + next: Provider = this + async browse(): Promise { + return new CachingDirBrowser({}, this) + } + async delete(_path: string) { + return null + } + async exists(_path: string) { + return null + } + async read(_path: string) { + return null + } + async write(_path: string, _data: Uint8Array) { + return null + } +} + +interface NestableDirEntry extends DirEntry { + children?: NestableDirEntry[] + dir: boolean + name: string + meta?: FileData +} + +/** A caching directory browser that receives the list of files once and remembers for as long as the dialog is open */ +export class CachingDirBrowser implements DirBrowser { + files: NestableDirEntry = { + children: [], + dir: true, + full_path: '/usr', + name: 'usr', + } + provider: Provider + + constructor(files: Record, provider: Provider) { + this.provider = provider + for (const [file_path, meta] of Object.entries(files)) { + if (file_path.startsWith('/usr/')) { + const parsed_path = path.parse(file_path) + const dirs = parsed_path.dir.substring(1).split('/') + dirs.shift() + // Find the directory for this file, creating it if necessary + let dir_entry = this.files + for (const subdir of dirs) { + let new_subdir = dir_entry.children!.find(child => child.name === subdir) + if (!new_subdir) { + new_subdir = { + children: [], + dir: true, + full_path: dir_entry.full_path + '/' + subdir, + name: subdir, + } + dir_entry.children!.push(new_subdir) + } + dir_entry = new_subdir + } + dir_entry.children!.push({ + dir: false, + full_path: file_path, + name: parsed_path.base, + meta, + }) + } + } + } + + async browse(dir_path: string): Promise { + if (!dir_path.startsWith('/usr')) { + throw new Error('Can only browse /usr') + } + const parsed_path = path.parse(dir_path) + const dirs = parsed_path.dir.substring(1).split('/') + dirs.shift() + let dir_entry = this.files + for (const subdir of dirs) { + dir_entry = dir_entry.children!.find(child => child.name === subdir)! + if (!dir_entry) { + throw new Error('Invalid directory state') + } + } + return dir_entry.children! + } +} \ No newline at end of file diff --git a/src/dialog/browser/download.ts b/src/dialog/browser/download.ts index ca9fd83..27e8258 100644 --- a/src/dialog/browser/download.ts +++ b/src/dialog/browser/download.ts @@ -11,7 +11,8 @@ https://github.com/curiousdannii/asyncglk // The download provider stores its own files just in a map (maybe to be cached in the future), but if files are written next to them, then they need to be done so in another provider -import {DirEntry, NullProvider, Provider} from './interface.js' +import {NullProvider} from './common.js' +import {type DirBrowser, type Provider} from './interface.js' import {utf8decoder} from '../../common/misc.js' export interface DownloadOptions { @@ -41,6 +42,10 @@ export class DownloadProvider implements Provider { return path } + async browse(): Promise { + return this.next.browse() + } + async delete(path: string): Promise { if (this.store.has(path)) { this.store.delete(path) @@ -59,10 +64,6 @@ export class DownloadProvider implements Provider { } } - async list(path: string): Promise { - throw new Error('Should not be browsing the download provider') - } - async read(path: string): Promise { if (this.store.has(path)) { return this.store.get(path)! diff --git a/src/dialog/browser/interface.ts b/src/dialog/browser/interface.ts index 4451d46..c3b1d66 100644 --- a/src/dialog/browser/interface.ts +++ b/src/dialog/browser/interface.ts @@ -10,53 +10,38 @@ https://github.com/curiousdannii/asyncglk */ /** A provider handles part of the filesystem, and can cascade down to another provider for files it doesn't handle */ -// Inspired by Koa export interface Provider { /** A link to the next provider */ next: Provider + /** Get a `DirBrowser` instance for browsing */ + browse(): Promise /** Delete a file */ delete(path: string): Promise /** Check if a file exists */ exists(path: string): Promise - /** Directory listing */ - list(path: string): Promise /** Read a file */ read(path: string): Promise /** Write a file */ write(path: string, data: Uint8Array): Promise } +/** Browse a directory; may cache all the files or request each time you change directory */ +export interface DirBrowser { + browse(path: string): Promise +} + export interface DirEntry { dir: boolean + full_path: string name: string + meta?: FileData } export interface FileData { - accessed: number - created: number + atime: number etag?: string - data: Uint8Array 'last-modified'?: string - modified: number - path_prefix: string + mtime: number + path_prefix?: string story_id?: string -} - -export class NullProvider implements Provider { - next: Provider = this - async delete(_path: string) { - return null - } - async exists(_path: string) { - return null - } - async list(_path: string) { - return null - } - async read(_path: string) { - return null - } - async write(_path: string, _data: Uint8Array) { - return null - } } \ No newline at end of file diff --git a/src/dialog/browser/storage.ts b/src/dialog/browser/storage.ts index e704372..2fbd3e3 100644 --- a/src/dialog/browser/storage.ts +++ b/src/dialog/browser/storage.ts @@ -11,9 +11,10 @@ https://github.com/curiousdannii/asyncglk import {decode as base32768_decode, encode as base32768_encode} from 'base32768' -import {DirEntry, FileData, NullProvider, Provider} from './interface.js' +import {CachingDirBrowser, NullProvider} from './common.js' +import type {DirBrowser, FileData, Provider} from './interface.js' -type WebStorageFileMetadata = Pick +type WebStorageFileMetadata = Pick const METADATA_KEY = 'dialog_metadata' @@ -24,17 +25,29 @@ const enum MetadataUpdateOperation { } export class WebStorageProvider implements Provider { + private browseable: boolean next = new NullProvider() - prefix: string - store: Storage + private prefix: string + private store: Storage - constructor(prefix: string, store: Storage) { + constructor(prefix: string, store: Storage, browseable?: boolean) { + this.browseable = browseable ?? false this.prefix = prefix this.store = store // TODO: upgrade storage } + async browse(): Promise { + if (this.browseable) { + const metadata = this.get_metadata() + return new CachingDirBrowser(metadata, this) + } + else { + return this.next.browse() + } + } + async delete(path: string): Promise { if (path.startsWith(this.prefix)) { this.store.removeItem(path) @@ -54,10 +67,6 @@ export class WebStorageProvider implements Provider { } } - async list(path: string): Promise { - throw new Error('Not implemented yet') - } - async read(path: string): Promise { if (path.startsWith(this.prefix)) { const res = this.store.getItem(path) @@ -84,25 +93,28 @@ export class WebStorageProvider implements Provider { } } - update_metadata(path: string, op: MetadataUpdateOperation) { + private get_metadata(): Record { + return JSON.parse(this.store.getItem(METADATA_KEY) || '{}') + } + + private update_metadata(path: string, op: MetadataUpdateOperation) { const now = Date.now() - const metadata: Record = JSON.parse(this.store.getItem(METADATA_KEY) || '{}') + const metadata = this.get_metadata() switch (op) { case MetadataUpdateOperation.DELETE: delete metadata[path] break case MetadataUpdateOperation.READ: - metadata[path].accessed = now + metadata[path].atime = now break case MetadataUpdateOperation.WRITE: if (!metadata[path]) { metadata[path] = { - accessed: now, - created: now, - modified: now, + atime: now, + mtime: now, } } - metadata[path].modified = now + metadata[path].mtime = now } this.store.setItem(METADATA_KEY, JSON.stringify(metadata)) } diff --git a/src/dialog/browser/ui/Dialog.svelte b/src/dialog/browser/ui/Dialog.svelte new file mode 100644 index 0000000..084e02e --- /dev/null +++ b/src/dialog/browser/ui/Dialog.svelte @@ -0,0 +1,124 @@ + + + + + + + +
+
+

{title}

+ +
+ + {#if saving} +
+ +
+ {/if} +
+ + +
+
+
\ No newline at end of file diff --git a/src/dialog/browser/ui/FileList.svelte b/src/dialog/browser/ui/FileList.svelte new file mode 100644 index 0000000..1412515 --- /dev/null +++ b/src/dialog/browser/ui/FileList.svelte @@ -0,0 +1,48 @@ + + + + +
+ {#each files as file, i} + + {/each} +
\ No newline at end of file diff --git a/src/dialog/browser/ui/FileListItem.svelte b/src/dialog/browser/ui/FileListItem.svelte new file mode 100644 index 0000000..16b6d15 --- /dev/null +++ b/src/dialog/browser/ui/FileListItem.svelte @@ -0,0 +1,40 @@ + + + + + \ No newline at end of file diff --git a/src/dialog/common/cache.ts b/src/dialog/common/cache.ts index 49c8e2a..23f022a 100644 --- a/src/dialog/common/cache.ts +++ b/src/dialog/common/cache.ts @@ -11,10 +11,10 @@ https://github.com/curiousdannii/asyncglk import {debounce} from 'lodash-es' -import {Array_to_BEBuffer, GlkTypedArray, is_unicode_array, utf8encoder} from '../../common/misc.js' -import {FileRef} from '../../common/protocol.js' +import {Array_to_BEBuffer, type GlkTypedArray, is_unicode_array, utf8encoder} from '../../common/misc.js' +import type {FileRef} from '../../common/protocol.js' import {filemode_Read, filemode_Write, seekmode_End, seekmode_Start} from '../../glkapi/constants.js' -import {AutosaveData, ClassicSyncDialog, ClassicStreamingDialog, DialogOptions} from './interface.js' +import type {AutosaveData, ClassicSyncDialog, ClassicStreamingDialog, DialogOptions} from './interface.js' /** A file buffer which stores its data in its preferred stream format, only converting back to a Uint8Array when required */ export class FileBuffer { diff --git a/src/dialog/common/common.ts b/src/dialog/common/common.ts index b131203..546368a 100644 --- a/src/dialog/common/common.ts +++ b/src/dialog/common/common.ts @@ -9,7 +9,7 @@ https://github.com/curiousdannii/asyncglk */ -import {FileType} from '../../common/protocol.js' +import type {FileType} from '../../common/protocol.js' /** File extensions for Glk file types */ export function filetype_to_extension(filetype: FileType): string { diff --git a/src/dialog/common/interface.ts b/src/dialog/common/interface.ts index 82c3c2e..7e3fd58 100644 --- a/src/dialog/common/interface.ts +++ b/src/dialog/common/interface.ts @@ -9,8 +9,8 @@ https://github.com/curiousdannii/asyncglk */ -import {FileRef} from '../../common/protocol.js' -import {GlkOte} from '../../glkote/common/glkote.js' +import type {FileRef} from '../../common/protocol.js' +import type {GlkOte} from '../../glkote/common/glkote.js' export type Dialog = AsyncDialog | ClassicSyncDialog | ClassicStreamingDialog diff --git a/src/dialog/node/async.ts b/src/dialog/node/async.ts index f73d947..9b36c06 100644 --- a/src/dialog/node/async.ts +++ b/src/dialog/node/async.ts @@ -15,8 +15,8 @@ import MuteStream from 'mute-stream' import os from 'os' import {path_native_to_posix, path_posix_to_native} from '../common/common.js' -import {AsyncDialog, DialogOptions} from '../common/interface.js' -import {get_stdio, HackableReadline} from '../../glkote/cheap/stdio.js' +import type {AsyncDialog, DialogOptions} from '../common/interface.js' +import {get_stdio, type HackableReadline} from '../../glkote/cheap/stdio.js' export class CheapAsyncDialog implements AsyncDialog { 'async' = true as const diff --git a/src/dialog/node/cheap.ts b/src/dialog/node/cheap.ts index f540179..df70008 100644 --- a/src/dialog/node/cheap.ts +++ b/src/dialog/node/cheap.ts @@ -12,12 +12,12 @@ https://github.com/curiousdannii/asyncglk import MuteStream from 'mute-stream' import os from 'os' -import {FileRef} from '../../common/protocol.js' +import type {FileRef} from '../../common/protocol.js' import {filters_for_usage} from '../common/common.js' -import {ClassicStreamingDialog} from '../common/interface.js' +import type {ClassicStreamingDialog} from '../common/interface.js' import NodeStreamingDialog from './node-streaming.js' -import {get_stdio, HackableReadline} from '../../glkote/cheap/stdio.js' +import {get_stdio, type HackableReadline} from '../../glkote/cheap/stdio.js' export class CheapStreamingDialog extends NodeStreamingDialog implements ClassicStreamingDialog { classname = 'CleapDialog' diff --git a/src/dialog/node/node-streaming.ts b/src/dialog/node/node-streaming.ts index ec7edd0..1bb514c 100644 --- a/src/dialog/node/node-streaming.ts +++ b/src/dialog/node/node-streaming.ts @@ -15,9 +15,9 @@ import fs from 'fs' import os from 'os' import path from 'path' -import {FileRef} from '../../common/protocol.js' -import {GlkOte} from '../../glkote/common/glkote.js' -import {AutosaveData, ClassicFileStream, ClassicStreamingDialog, DialogOptions} from '../common/interface.js' +import type {FileRef} from '../../common/protocol.js' +import type {GlkOte} from '../../glkote/common/glkote.js' +import type {AutosaveData, ClassicFileStream, ClassicStreamingDialog, DialogOptions} from '../common/interface.js' import { filemode_Read, diff --git a/src/glkapi/common.ts b/src/glkapi/common.ts index edf522d..d8afe62 100644 --- a/src/glkapi/common.ts +++ b/src/glkapi/common.ts @@ -9,7 +9,7 @@ https://github.com/curiousdannii/asyncglk */ -import {GlkTypedArray} from '../common/misc.js' +import type {GlkTypedArray} from '../common/misc.js' export function copy_array(source: GlkTypedArray, target: number[], length: number) { const copy_length = Math.min(length, target.length) diff --git a/src/glkapi/filerefs.ts b/src/glkapi/filerefs.ts index a2894f7..ad36904 100644 --- a/src/glkapi/filerefs.ts +++ b/src/glkapi/filerefs.ts @@ -9,12 +9,12 @@ https://github.com/curiousdannii/asyncglk */ -import {GlkTypedArray} from '../common/misc.js' -import {FileRef as DialogFileRef} from '../common/protocol.js' +import type {GlkTypedArray} from '../common/misc.js' +import type {FileRef as DialogFileRef} from '../common/protocol.js' import {CachingDialogWrapper, FileBuffer} from '../dialog/common/cache.js' import {fileusage_TextMode} from './constants.js' -import {GlkFref} from './interface.js' +import type {GlkFref} from './interface.js' export class FileRef implements GlkFref { binary: boolean diff --git a/src/glkapi/glkapi.ts b/src/glkapi/glkapi.ts index 0575682..5ab8584 100644 --- a/src/glkapi/glkapi.ts +++ b/src/glkapi/glkapi.ts @@ -11,23 +11,24 @@ https://github.com/curiousdannii/asyncglk import {cloneDeep} from 'lodash-es' -import {Blorb, ImageInfo} from '../blorb/blorb.js' +import {Blorb, type ImageInfo} from '../blorb/blorb.js' import {DEFAULT_METRICS, PACKAGE_VERSION} from '../common/constants.js' -import {BEBuffer_to_Array, GlkTypedArray, GlkTypedArrayConstructor, utf8decoder} from '../common/misc.js' +import {BEBuffer_to_Array, type GlkTypedArray, type GlkTypedArrayConstructor, utf8decoder} from '../common/misc.js' import * as Protocol from '../common/protocol.js' -import {BufferWindowImage, FileRef as DialogFileRef, ImageOperation, NormalisedMetrics, SpecialInput, TerminatorCode, WindowStyles} from '../common/protocol.js' +import type {BufferWindowImage, FileRef as DialogFileRef, ImageOperation, NormalisedMetrics, SpecialInput, TerminatorCode, WindowStyles} from '../common/protocol.js' import {CachingDialogWrapper} from '../dialog/common/cache.js' -import {GlkOte, GlkOteOptions} from '../glkote/common/glkote.js' +import type {GlkOte, GlkOteOptions} from '../glkote/common/glkote.js' -import {copy_array, TimerData} from './common.js' +import {copy_array, type TimerData} from './common.js' import * as Const from './constants.js' import {evtype_Arrange, evtype_CharInput, evtype_Hyperlink, evtype_LineInput, evtype_MouseInput, evtype_None, evtype_Redraw, evtype_Timer, filemode_Read, filemode_ReadWrite, filemode_Write, filemode_WriteAppend, fileusage_SavedGame, fileusage_TypeMask, gestalt_CharInput, gestalt_CharOutput, gestalt_CharOutput_ExactPrint, gestalt_DateTime, gestalt_DrawImage, gestalt_GarglkText, gestalt_Graphics, gestalt_GraphicsCharInput, gestalt_GraphicsTransparency, gestalt_HyperlinkInput, gestalt_Hyperlinks, gestalt_LineInput, gestalt_LineInputEcho, gestalt_LineTerminatorKey, gestalt_LineTerminators, gestalt_MouseInput, gestalt_ResourceStream, gestalt_Timer, gestalt_Unicode, gestalt_UnicodeNorm, gestalt_Version, keycode_Escape, keycode_Func1, keycode_Func12, keycode_Left, keycode_MAXVAL, keycode_Unknown, seekmode_End, stylehint_BackColor, stylehint_Indentation, stylehint_Justification, stylehint_NUMHINTS, stylehint_Oblique, stylehint_ParaIndentation, stylehint_Proportional, stylehint_ReverseColor, stylehint_Size, stylehint_TextColor, stylehint_Weight, style_NUMSTYLES, winmethod_Above, winmethod_Below, winmethod_Border, winmethod_BorderMask, winmethod_DirMask, winmethod_DivisionMask, winmethod_Fixed, winmethod_Left, winmethod_NoBorder, winmethod_Proportional, winmethod_Right, wintype_AllTypes, wintype_Blank, wintype_Graphics, wintype_Pair, wintype_TextBuffer, wintype_TextGrid, zcolor_Current, zcolor_Default} from './constants.js' import {FileRef} from './filerefs.js' import * as Interface from './interface.js' -import {DidNotReturn, GlkArray, GlkApiOptions, GlkByteArray, GlkSchannel, GlkWordArray, RefBoxArg, RefStructArg, RefStructValue} from './interface.js' +import type {GlkArray, GlkApiOptions, GlkByteArray, GlkSchannel, GlkWordArray, RefBoxArg, RefStructArg, RefStructValue} from './interface.js' +import {DidNotReturn} from './interface.js' import {CSS_STYLE_PROPERTIES, FILE_MODES, FILE_TYPES, IMAGE_ALIGNMENTS, KEY_NAMES_TO_CODES, MAX_LATIN1, QUESTION_MARK, STYLE_NAMES, TERMINATOR_KEYS, TERMINATOR_KEYS_TO_CODES} from './lib_constants.js' -import {ArrayBackedStream, FileStream, NullStream, Stream} from './streams.js' -import {BlankWindow, BufferWindow, GraphicsWindow, GridWindow, PairWindow, TextWindow, Window, WindowBox} from './windows.js' +import {ArrayBackedStream, FileStream, NullStream, type Stream} from './streams.js' +import {BlankWindow, BufferWindow, GraphicsWindow, GridWindow, PairWindow, TextWindow, type Window, type WindowBox} from './windows.js' export class RefBox implements Interface.RefBox { private value: RefStructValue = 0 diff --git a/src/glkapi/interface.ts b/src/glkapi/interface.ts index e203af0..083e53d 100644 --- a/src/glkapi/interface.ts +++ b/src/glkapi/interface.ts @@ -10,9 +10,9 @@ https://github.com/curiousdannii/asyncglk */ import {Blorb} from '../blorb/blorb.js' -import {FileRef} from '../common/protocol.js' -import {Dialog} from '../dialog/common/interface.js' -import {GlkOte} from '../glkote/common/glkote.js' +import type {FileRef} from '../common/protocol.js' +import type {Dialog} from '../dialog/common/interface.js' +import type {GlkOte} from '../glkote/common/glkote.js' import * as Const from './constants.js' diff --git a/src/glkapi/lib_constants.ts b/src/glkapi/lib_constants.ts index 3916850..a5e7e30 100644 --- a/src/glkapi/lib_constants.ts +++ b/src/glkapi/lib_constants.ts @@ -9,7 +9,7 @@ https://github.com/curiousdannii/asyncglk */ -import {BufferWindowImage, FileMode, FileType, SpecialKeyCode, TerminatorCode} from '../common/protocol.js' +import type {BufferWindowImage, FileMode, FileType, SpecialKeyCode, TerminatorCode} from '../common/protocol.js' import {keycode_Delete, keycode_Down, keycode_End, keycode_Escape, keycode_Func1, keycode_Func2, keycode_Func3, keycode_Func4, keycode_Func5, keycode_Func6, keycode_Func7, keycode_Func8, keycode_Func9, keycode_Func10, keycode_Func11, keycode_Func12, keycode_Home, keycode_Left, keycode_PageDown, keycode_PageUp, keycode_Return, keycode_Right, keycode_Tab, keycode_Up} from './constants.js' diff --git a/src/glkapi/streams.ts b/src/glkapi/streams.ts index d1531f0..6fd2591 100644 --- a/src/glkapi/streams.ts +++ b/src/glkapi/streams.ts @@ -9,13 +9,13 @@ https://github.com/curiousdannii/asyncglk */ -import {GlkTypedArray, is_unicode_array} from '../common/misc.js' +import {type GlkTypedArray, is_unicode_array} from '../common/misc.js' import {filemode_Write, seekmode_Current, seekmode_End} from './constants.js' import {FileRef} from './filerefs.js' -import {GlkArray, GlkStream, RefStructArg} from './interface.js' +import type {GlkArray, GlkStream, RefStructArg} from './interface.js' import {GLK_NULL, MAX_LATIN1, QUESTION_MARK} from './lib_constants.js' -import {Window} from './windows.js' +import type {Window} from './windows.js' export type Stream = ArrayBackedStream | FileStream | NullStream | WindowStream type StreamType = 'array' | 'null' | 'window' diff --git a/src/glkapi/windows.ts b/src/glkapi/windows.ts index 02e49aa..fd93006 100644 --- a/src/glkapi/windows.ts +++ b/src/glkapi/windows.ts @@ -11,11 +11,11 @@ https://github.com/curiousdannii/asyncglk import {cloneDeep} from 'lodash-es' -import {BufferWindowImage, ContentUpdate, GraphicsWindowOperation, InputUpdate, TextRun, WindowStyles, WindowUpdate as SizeUpdate} from '../common/protocol.js' +import type {BufferWindowImage, ContentUpdate, GraphicsWindowOperation, InputUpdate, TextRun, WindowStyles, WindowUpdate as SizeUpdate} from '../common/protocol.js' import {winmethod_Above, winmethod_BorderMask, winmethod_DirMask, winmethod_DivisionMask, winmethod_Fixed, winmethod_Left, winmethod_Right, wintype_Blank, wintype_Graphics, wintype_Pair, wintype_TextBuffer, wintype_TextGrid} from './constants.js' -import {GlkArray, GlkWindow} from './interface.js' -import {Stream, WindowStream} from './streams.js' +import type {GlkArray, GlkWindow} from './interface.js' +import {type Stream, WindowStream} from './streams.js' export type Window = BlankWindow | BufferWindow | GraphicsWindow | GridWindow | PairWindow type WindowTypes = 'blank' | 'buffer' | 'graphics' | 'grid' | 'pair' diff --git a/src/glkote/cheap/cheap.ts b/src/glkote/cheap/cheap.ts index b0775eb..e6f5ac5 100644 --- a/src/glkote/cheap/cheap.ts +++ b/src/glkote/cheap/cheap.ts @@ -13,7 +13,7 @@ import MuteStream from 'mute-stream' import * as readline from 'readline' import * as TTY from 'tty' -import {get_stdio, HackableReadline} from './stdio.js' +import {get_stdio, type HackableReadline} from './stdio.js' import * as GlkOte from '../common/glkote.js' import * as protocol from '../../common/protocol.js' diff --git a/src/glkote/common/glkote.ts b/src/glkote/common/glkote.ts index be99c5b..3734759 100644 --- a/src/glkote/common/glkote.ts +++ b/src/glkote/common/glkote.ts @@ -13,8 +13,8 @@ import {Blorb} from '../../blorb/blorb.js' import * as Constants from '../../common/constants.js' import * as protocol from '../../common/protocol.js' import {filetype_to_extension} from '../../dialog/common/common.js' -import {Dialog} from '../../dialog/common/interface.js' -import {GlkApi} from '../../glkapi/interface.js' +import type {Dialog} from '../../dialog/common/interface.js' +import type {GlkApi} from '../../glkapi/interface.js' export interface GlkOte { classname: string, diff --git a/src/glkote/web/input.ts b/src/glkote/web/input.ts index 064bf2e..ababff1 100644 --- a/src/glkote/web/input.ts +++ b/src/glkote/web/input.ts @@ -14,7 +14,7 @@ import {KEY_CODE_DOWN, KEY_CODE_RETURN, KEY_CODE_UP, KEY_CODES_TO_NAMES, OFFSCRE import * as protocol from '../../common/protocol.js' import {is_pinch_zoomed} from './shared.js' -import {apply_text_run_styles, Window} from './windows.js' +import {apply_text_run_styles, type Window} from './windows.js' const MAX_HISTORY_LENGTH = 25 diff --git a/src/glkote/web/transcript-recorder.ts b/src/glkote/web/transcript-recorder.ts index a6cab6b..8fb1af9 100644 --- a/src/glkote/web/transcript-recorder.ts +++ b/src/glkote/web/transcript-recorder.ts @@ -10,7 +10,7 @@ https://github.com/curiousdannii/asyncglk */ import * as protocol from '../../common/protocol.js' -import {GlkOteOptions, TranscriptRecordingData} from '../common/glkote.js' +import type {GlkOteOptions, TranscriptRecordingData} from '../common/glkote.js' import Windows from './windows.js' diff --git a/src/glkote/web/windows.ts b/src/glkote/web/windows.ts index 1d50c7a..8ad66a0 100644 --- a/src/glkote/web/windows.ts +++ b/src/glkote/web/windows.ts @@ -16,7 +16,7 @@ import {NBSP} from '../../common/constants.js' import * as protocol from '../../common/protocol.js' import {TextInput} from './input.js' -import {create, DOM, EventFunc, is_pinch_zoomed} from './shared.js' +import {create, DOM, type EventFunc, is_pinch_zoomed} from './shared.js' import WebGlkOte from './web.js' export type Window = BufferWindow | GraphicsWindow | GridWindow diff --git a/tsconfig.json b/tsconfig.json index a018e8a..154ba69 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,8 @@ "outDir": "dist", "rootDir": "src", "strict": true, - "target": "es2020" + "target": "es2020", + "verbatimModuleSyntax": true }, "include": [ "src/**/*"