Skip to content

Commit

Permalink
Merge pull request #76 from mindofmatthew/settings-editor-json-ts
Browse files Browse the repository at this point in the history
Settings Editor Updates
  • Loading branch information
matthewkaney authored May 8, 2024
2 parents 45afe5c + 8f54b9f commit 5c58c6a
Show file tree
Hide file tree
Showing 12 changed files with 356 additions and 116 deletions.
45 changes: 37 additions & 8 deletions app/desktop/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,23 @@ import { autoUpdater } from "electron-updater";

autoUpdater.checkForUpdatesAndNotify();

import { GHCI } from "@management/lang-tidal";
import { StateManagement } from "@core/state";

import { GHCI, TidalSettingsSchema } from "@management/lang-tidal";
import { Filesystem } from "./filesystem";
import { wrapIPC } from "./ipcMain";

import { menu } from "./menu";

const filesystem = new Filesystem();

const createWindow = () => {
const settingsPath = resolve(app.getPath("userData"), "settings.json");

const createWindow = (
configuration: StateManagement<typeof TidalSettingsSchema>
) => {
const tidal = new GHCI(configuration);

const window = new BrowserWindow({
show: false,
width: 800,
Expand All @@ -29,8 +37,6 @@ const createWindow = () => {
},
});

const tidal = new GHCI(resolve(app.getPath("userData")));

let listeners: (() => void)[] = [];
let docsListeners: { [id: string]: typeof listeners } = {};

Expand Down Expand Up @@ -127,11 +133,19 @@ const createWindow = () => {

listeners.push(
menu.on("settings", async () => {
let settingsDoc = filesystem.loadDoc(tidal.settingsPath, "{}");
let settingsDoc = filesystem.loadDoc(settingsPath, "{}");

settingsDoc.on("status", ({ saved }) => {
if (saved === true) {
tidal.reloadSettings();
try {
let settingsText = settingsDoc.content?.doc.toString();

if (typeof settingsText === "string") {
configuration.update(JSON.parse(settingsText));
}
} catch (error) {
console.log("Error updating settings");
}
}
});
})
Expand Down Expand Up @@ -175,8 +189,23 @@ const createWindow = () => {
});
};

app.whenReady().then(() => {
createWindow();
import { readFile } from "fs/promises";

app.whenReady().then(async () => {
const settings = new StateManagement(TidalSettingsSchema);

// Try loading settings
let settingsData = {};

try {
settingsData = JSON.parse(await readFile(settingsPath, "utf-8"));
} catch (err) {
// TODO: Throw some sort of error? For now, just fall back to the empty object
}

settings.update(settingsData);

createWindow(settings);

// app.on("activate", () => {
// if (BrowserWindow.getAllWindows().length === 0) createWindow();
Expand Down
7 changes: 6 additions & 1 deletion app/desktop/src/renderer/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { basicSetup } from "@core/extensions/basicSetup";
import { oneDark } from "@core/extensions/theme/theme";
import { tidal } from "@management/lang-tidal/editor";

import { settings } from "@core/extensions/settings/editor";

import { LayoutView } from "@core/extensions/layout";
import { console as electronConsole } from "@core/extensions/console";
// import { peer } from "@core/extensions/peer";
Expand Down Expand Up @@ -61,6 +63,9 @@ export class Editor {
});

api.onOpen(({ id, path }) => {
// TODO: This is a hacky heuristic
let languageMode = path?.endsWith("settings.json") ? settings() : tidal();

let offContent = api.onContent(id, ({ doc: docJSON, version, saved }) => {
let doc = Text.of(docJSON);

Expand All @@ -70,7 +75,7 @@ export class Editor {
view: new EditorTabView(layout, id, api, {
doc,
extensions: [
tidal(),
languageMode,
evaluation(api.evaluate),
basicSetup,
oneDark,
Expand Down
1 change: 0 additions & 1 deletion app/web/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { EditorTabView } from "@core/extensions/layout/tabs/editor";
import { ElectronAPI } from "@core/api";
import { console as electronConsole } from "@core/extensions/console";
import { peer } from "@core/extensions/peer";
import { SettingsPage } from "@core/extensions/settings";
// import { toolbar } from "@core/extensions/toolbar";
import { fileSync } from "../../desktop/src/renderer/file";

Expand Down
14 changes: 14 additions & 0 deletions core/extensions/settings/editor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { autocompletion } from "@codemirror/autocomplete";
import { json } from "@codemirror/lang-json";
import { jsonSchema } from "codemirror-json-schema";

import { TidalSettingsSchema } from "packages/languages/tidal/settings";

export function settings() {
return [
autocompletion(),
json(),
// TODO: Figure out how to get all the JSON Schema extensions to work together
jsonSchema(TidalSettingsSchema),
];
}
9 changes: 0 additions & 9 deletions core/extensions/settings/index.ts

This file was deleted.

32 changes: 32 additions & 0 deletions core/state/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { EventEmitter } from "@core/events";

import { getDefaults, getValid } from "./schema";

import { SettingsSchema, FromSchema } from "./schema";

interface StateEvents<S extends SettingsSchema> {
change: FromSchema<S>;
}

export class StateManagement<S extends SettingsSchema> extends EventEmitter<
StateEvents<S>
> {
private defaults: FromSchema<S>;
private data: Partial<FromSchema<S>>;

constructor(private schema: S, initial: any = {}) {
super();

this.defaults = getDefaults(schema);
this.data = getValid(schema, initial);
}

update(data: any) {
this.data = getValid(this.schema, data);
this.emit("change", this.getData());
}

getData(): FromSchema<S> {
return { ...this.defaults, ...this.data };
}
}
83 changes: 83 additions & 0 deletions core/state/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { JSONSchema, FromSchema as FromJSONSchema } from "json-schema-to-ts";

export interface SettingsSchema {
properties: Readonly<Record<string, JSONSchema & object>>;
}

export type FromSchema<S extends SettingsSchema> = FromJSONSchema<
S & { type: "object" }
> &
object;

export function getDefaults<
S extends SettingsSchema,
SchemaData = FromSchema<S>
>(schema: S): SchemaData {
const defaults: any = {};

for (let key in schema.properties) {
let valueOptions = schema.properties[key];

switch (valueOptions.type) {
case "number":
defaults[key] = valueOptions.default ?? 0;
break;
case "string":
defaults[key] = valueOptions.default ?? "";
break;
case "boolean":
defaults[key] = valueOptions.default ?? false;
break;
case "array":
defaults[key] = [];
break;
}
}

return defaults;
}

export function getValid<S extends SettingsSchema, SchemaData = FromSchema<S>>(
schema: S,
data: any
): Partial<SchemaData> {
const validData: any = {};

function getValidPrimitive(schema: JSONSchema & object, value: any) {
if (schema.type === "number" && typeof value === "number") {
return value;
} else if (schema.type === "string" && typeof value === "string") {
return value;
} else if (schema.type === "boolean" && typeof value === "boolean") {
return value;
}
}

if (typeof data === "object") {
for (let key in data) {
if (key in schema.properties) {
let prop = schema.properties[key];
if (
prop.type === "number" ||
prop.type === "string" ||
prop.type === "boolean"
) {
const value = getValidPrimitive(prop, data[key]);
if (value !== undefined) {
validData[key] = value;
}
} else if (prop.type === "array" && typeof prop.items === "object") {
const value = data[key];
if (Array.isArray(value)) {
const arraySchema = prop.items;
validData[key] = value.filter((v) =>
getValidPrimitive(arraySchema, v)
);
}
}
}
}
}

return validData;
}
Loading

0 comments on commit 5c58c6a

Please sign in to comment.