diff --git a/index.css b/index.css
index dddd5ad0..8612336a 100644
--- a/index.css
+++ b/index.css
@@ -745,6 +745,7 @@ input[type="number"] {
height: 100%;
user-select: text;
caret-color: auto;
+ font-size: 13.33px;
}
input:disabled {
diff --git a/src/App.ts b/src/App.ts
index 2cbe7b6e..fb653f54 100644
--- a/src/App.ts
+++ b/src/App.ts
@@ -25,7 +25,6 @@ import { Keybinds } from "./util/Keybinds"
import { Options } from "./util/Options"
import { extname } from "./util/Path"
import { fpsUpdate } from "./util/Performance"
-import { getBrowser } from "./util/Util"
import { FileHandler } from "./util/file-handler/FileHandler"
declare global {
interface Window {
@@ -317,36 +316,6 @@ function init() {
Please visit your browser settings and enable WebGL.
`
- } else if (getBrowser().includes("Safari")) {
- document.querySelector(
- "body"
- )!.innerHTML = `
-
-
Safari is currently not supported
-
Please use Chrome/Firefox instead.
-
Check the console for more info.
-
-
`
- console.log(
- `SMEditor is not supported for Safari due to various issues involving rendering and sound.
- PIXI.js, the library used in SMEditor, takes an extremely long time to load and does not perform well on Safari.
- Additionally, many audio files cannot be played in Safari.
- If you still want to try loading SMEditor, run the command runSafari()`
- )
- window.runSafari = () => {
- document.querySelector("body")!.innerHTML = `
-
-
-
-
- `
- window.app = new App()
- window.runSafari = undefined
- }
} else {
window.app = new App()
}
diff --git a/src/chart/audio/ChartAudio.ts b/src/chart/audio/ChartAudio.ts
index 540d17cb..6bffb053 100644
--- a/src/chart/audio/ChartAudio.ts
+++ b/src/chart/audio/ChartAudio.ts
@@ -425,7 +425,7 @@ export class ChartAudio {
} catch (e) {
if (this.type == ".ogg") {
// attempt to decode with ogg
- const oggdec = await (await import("../../util/OggDec")).default
+ const oggdec = (await import("../../util/OggDec")).default
try {
resolve(await oggdec.decodeOggData(data))
} catch (err) {
diff --git a/src/gui/widget/DebugWidget.ts b/src/gui/widget/DebugWidget.ts
index 95aedefa..78b1bce6 100644
--- a/src/gui/widget/DebugWidget.ts
+++ b/src/gui/widget/DebugWidget.ts
@@ -6,11 +6,11 @@ import {
Texture,
} from "pixi.js"
import { BetterRoundedRect } from "../../util/BetterRoundedRect"
+import { clamp, roundDigit } from "../../util/Math"
import { Options } from "../../util/Options"
+import { getFPS, getTPS } from "../../util/Performance"
import { Widget } from "./Widget"
import { WidgetManager } from "./WidgetManager"
-import { clamp, roundDigit } from "../../util/Math"
-import { getFPS, getTPS } from "../../util/Performance"
const GRAPH_HEIGHT = 50
@@ -115,8 +115,8 @@ export class DebugWidget extends Widget {
this.fpsBg.width = this.fpsText.width + 10
this.fpsBg.height = this.fpsText.height + 10
if (Options.debug.showTimers) {
- this.fpsBg.y = (GRAPH_HEIGHT + 5) * (this.children.length + 2) - 5
- this.fpsText.y = (GRAPH_HEIGHT + 5) * (this.children.length + 2)
+ this.fpsBg.y = (GRAPH_HEIGHT + 5) * this.graphs.children.length - 5
+ this.fpsText.y = (GRAPH_HEIGHT + 5) * this.graphs.children.length
} else {
this.fpsBg.y = -5
this.fpsText.y = 0
diff --git a/src/util/OggDec.d.ts b/src/util/OggDec.d.ts
index d8e44c21..8aba9aa0 100644
--- a/src/util/OggDec.d.ts
+++ b/src/util/OggDec.d.ts
@@ -1,5 +1,6 @@
+const ogg: OggDec
+export default ogg
+
interface OggDec {
- decodeOggData(data: ArrayBuffer): ArrayBuffer
+ decodeOggData(data: ArrayBuffer): Promise
}
-
-export default oggdec = OggDec
diff --git a/src/util/SafariFileWorker.ts b/src/util/SafariFileWorker.ts
deleted file mode 100644
index b0ea3dbe..00000000
--- a/src/util/SafariFileWorker.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-// WebKit is currently bugged, making this not work at all
-// Thanks Apple
-export class SafariFileWorker {
- private static _worker: Worker
- private static workID = 0
- private static map: Map) => void> =
- new Map()
-
- private static _init() {
- const workerCode = `
- function resolvePath(path) {
- let pathParts = path.split("/")
- pathParts = pathParts.filter(item => item != "." && item != "")
- while (pathParts.indexOf("..") > -1) {
- const ind = pathParts.indexOf("..")
- if (ind == 0) {
- throw Error("Path" + pathParts.join("/") + "is invalid!")
- }
- pathParts.splice(ind - 1, 2)
- }
- return pathParts.join("/")
- }
-
- async function getFileHandle(path, options) {
- try {
- const pathParts = resolvePath(path).split("/")
- const filename = pathParts.pop()
- const dirHandle = await getDirectoryHandle(
- pathParts.join("/"),
- options
- )
- if (!dirHandle) return undefined
- return await dirHandle.getFileHandle(filename, options)
- } catch (err) {
- console.log(err)
- return undefined
- }
- }
-
- async function getDirectoryHandle(path, options, dir){
- dir ||= await navigator.storage.getDirectory()
- if (path == "" || path == ".") return dir
- try {
- const pathParts = resolvePath(path).split("/")
- const dirname = pathParts.shift()
- const dirHandle = await dir.getDirectoryHandle(dirname, options)
- if (!dirHandle) return undefined
- if (pathParts.length == 0) return dirHandle
- return getDirectoryHandle(pathParts.join("/"), options, dirHandle)
- } catch (err) {
- console.log(err)
- return undefined
- }
- }
-
- async function writeFile(path, arrayBuffer) {
- const handle = await getFileHandle(path)
- const accessHandle = await handle.createSyncAccessHandle();
- const writeSize = accessHandle.write(arrayBuffer, { "at": 0 });
- await accessHandle.flush();
- const fileSize = await accessHandle.getSize();
- // Read file content to a buffer.
- const readBuffer = new ArrayBuffer(fileSize);
- const readSize = accessHandle.read(readBuffer, { "at": 0 });
- console.log(readSize)
- await accessHandle.close();
- }
-
- self.onmessage = function(e){
- writeFile(e.data[1],e.data[2]).then(()=>{
- postMessage(e.data[0])
- })
- }
- `
-
- const blob = new Blob([workerCode], { type: "application/javascript" })
- this._worker = new Worker(URL.createObjectURL(blob))
- this._worker.onmessage = e => {
- const id = e.data as number
- console.log("Finished job " + id)
- if (this.map.has(id)) {
- this.map.get(id)!()
- }
- }
- }
-
- private static get worker(): Worker {
- if (!this._worker) this._init()
- return this._worker
- }
-
- static async writeHandle(path: string, data: Blob | string) {
- const id = this.workID++
- console.log("Starting work write " + path + ", id: " + id)
- const promise = new Promise(resolve => this.map.set(id, resolve))
- const encode = new TextEncoder()
- const buffer =
- typeof data == "string" ? encode.encode(data) : await data.arrayBuffer()
- this.worker.postMessage([id, path, buffer])
- return promise
- }
-}
diff --git a/src/util/file-handler/SafariFileWorker.ts b/src/util/file-handler/SafariFileWorker.ts
new file mode 100644
index 00000000..5f13d8e3
--- /dev/null
+++ b/src/util/file-handler/SafariFileWorker.ts
@@ -0,0 +1,110 @@
+// hacky type stuff
+interface SecureFileSystemFileHandle extends FileSystemHandle {
+ readonly kind: "file"
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemFileHandle/createSyncAccessHandle) */
+ createSyncAccessHandle(): Promise
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemFileHandle/createWritable) */
+ createWritable(
+ options?: FileSystemCreateWritableOptions
+ ): Promise
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemFileHandle/getFile) */
+ getFile(): Promise
+}
+
+interface FileSystemSyncAccessHandle {
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemSyncAccessHandle/close) */
+ close(): void
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemSyncAccessHandle/flush) */
+ flush(): void
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemSyncAccessHandle/getSize) */
+ getSize(): number
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemSyncAccessHandle/read) */
+ read(
+ buffer: AllowSharedBufferSource,
+ options?: FileSystemReadWriteOptions
+ ): number
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemSyncAccessHandle/truncate) */
+ truncate(newSize: number): void
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemSyncAccessHandle/write) */
+ write(
+ buffer: AllowSharedBufferSource,
+ options?: FileSystemReadWriteOptions
+ ): number
+}
+
+interface FileSystemReadWriteOptions {
+ at?: number
+}
+
+onmessage = async event => {
+ const [id, path, buffer] = event.data
+ const fileHandle = await getFileHandle(path)
+ if (!fileHandle) {
+ postMessage({
+ id,
+ success: false,
+ error: "Couldn't locate file",
+ })
+ return
+ }
+ const accessHandle = await fileHandle.createSyncAccessHandle()
+ accessHandle.write(buffer)
+ accessHandle.flush()
+ accessHandle.close()
+ postMessage({
+ id,
+ success: true,
+ })
+}
+
+async function getDirectoryHandle(
+ path: string,
+ options?: FileSystemGetFileOptions,
+ dir?: FileSystemDirectoryHandle
+): Promise {
+ dir ||= await navigator.storage.getDirectory()
+ if (path == "" || path == ".") return dir
+ const pathParts = resolvePath(path).split("/")
+ const dirname = pathParts.shift()!
+ try {
+ const dirHandle = await dir.getDirectoryHandle(dirname, options)
+ if (!dirHandle) return undefined
+ if (pathParts.length == 0) return dirHandle
+ return getDirectoryHandle(pathParts.join("/"), options, dirHandle)
+ } catch (err) {
+ console.error(`Failed to get directory ${path} (${dirname}): ` + err)
+ return undefined
+ }
+}
+
+async function getFileHandle(
+ path: string,
+ options?: FileSystemGetFileOptions
+): Promise {
+ try {
+ const pathParts = resolvePath(path).split("/")
+ const filename = pathParts.pop()!
+ const dirHandle = await getDirectoryHandle(pathParts.join("/"), options)
+ if (!dirHandle) return undefined
+ return (await dirHandle.getFileHandle(
+ filename,
+ options
+ )) as SecureFileSystemFileHandle
+ } catch (err) {
+ console.error("Failed to get file " + path + ": " + err)
+ return undefined
+ }
+}
+
+function resolvePath(path: string): string {
+ let pathParts = path.split("/")
+ pathParts = pathParts.filter(item => item != "." && item != "")
+ while (pathParts.indexOf("..") > -1) {
+ const ind = pathParts.indexOf("..")
+ if (ind == 0) {
+ throw Error("Path" + pathParts.join("/") + "is invalid!")
+ }
+ pathParts.splice(ind - 1, 2)
+ }
+ return pathParts.join("/")
+}
diff --git a/src/util/file-handler/SafariFileWriter.ts b/src/util/file-handler/SafariFileWriter.ts
new file mode 100644
index 00000000..6cc8eeee
--- /dev/null
+++ b/src/util/file-handler/SafariFileWriter.ts
@@ -0,0 +1,39 @@
+export class SafariFileWriter {
+ private static worker = new Worker(
+ new URL("./SafariFileWorker.ts", import.meta.url),
+ {
+ type: "module",
+ }
+ )
+ private static workID = 0
+ private static map: Map<
+ number,
+ [(value: void | PromiseLike) => void, (reason?: any) => void]
+ > = new Map()
+ static {
+ this.worker.onmessage = event => {
+ const data = event.data
+ console.log("finished job " + data.id)
+ console.log(data)
+ if (data.success) {
+ this.map.get(data.id)![0]()
+ } else {
+ this.map.get(data.id)![1](data.reason)
+ }
+ this.map.delete(data.id)
+ }
+ }
+
+ static async writeHandle(path: string, data: Blob | string) {
+ const id = this.workID++
+ console.log("Starting work write " + path + ", id: " + id)
+ const promise = new Promise((resolve, reject) =>
+ this.map.set(id, [resolve, reject])
+ )
+ const encode = new TextEncoder()
+ const buffer =
+ typeof data == "string" ? encode.encode(data) : await data.arrayBuffer()
+ this.worker.postMessage([id, path, buffer], [buffer])
+ return promise
+ }
+}
diff --git a/src/util/file-handler/WebFileHandler.ts b/src/util/file-handler/WebFileHandler.ts
index 73d34746..6b818551 100644
--- a/src/util/file-handler/WebFileHandler.ts
+++ b/src/util/file-handler/WebFileHandler.ts
@@ -6,15 +6,14 @@ import {
import JSZip from "jszip"
import { WaterfallManager } from "../../gui/element/WaterfallManager"
import { basename, dirname, extname } from "../Path"
-import { SafariFileWorker } from "../SafariFileWorker"
-import { getBrowser } from "../Util"
import { BaseFileHandler } from "./FileHandler"
+import { SafariFileWriter } from "./SafariFileWriter"
export class WebFileHandler implements BaseFileHandler {
private root!: FileSystemDirectoryHandle
constructor() {
- if (support.adapter.native && !getBrowser().includes("Safari")) {
+ if (support.adapter.native) {
getOriginPrivateDirectory().then(root => (this.root = root))
} else {
getOriginPrivateDirectory(
@@ -173,8 +172,7 @@ export class WebFileHandler implements BaseFileHandler {
async hasFile(path: string): Promise {
try {
- await this.getFileHandle(path)
- return true
+ return (await this.getFileHandle(path)) !== undefined
} catch (err) {
return false
}
@@ -440,7 +438,7 @@ export class WebFileHandler implements BaseFileHandler {
} else {
const path = await this.root.resolve(handle)
if (!path) return
- await SafariFileWorker.writeHandle(path.join("/"), data)
+ await SafariFileWriter.writeHandle(path.join("/"), data)
}
}
}