Skip to content

Commit

Permalink
Import/export themes
Browse files Browse the repository at this point in the history
  • Loading branch information
tillvit committed Sep 20, 2024
1 parent 28c3853 commit 5188c37
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 11 deletions.
4 changes: 0 additions & 4 deletions app/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -1945,10 +1945,6 @@ input[type="range"] {
gap: 10px;
}

input[type="text"].pref-search-bar {
flex: 0;
}

.pref-scrollers {
display: flex;
gap: 20px;
Expand Down
66 changes: 64 additions & 2 deletions app/src/gui/window/ThemeSelectionWindow.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { showOpenFilePicker } from "file-system-access"
import { App } from "../../App"
import { DEFAULT_THEMES, THEME_GRID_PROPS } from "../../data/ThemeData"
import { Options } from "../../util/Options"
import { basename } from "../../util/Path"
import { Themes } from "../../util/Theme"
import { WaterfallManager } from "../element/WaterfallManager"
import { Icons } from "../Icons"
import { ConfirmationWindow } from "./ConfirmationWindow"
import { ThemeEditorWindow } from "./ThemeEditorWindow"
Expand Down Expand Up @@ -36,6 +39,9 @@ export class ThemeSelectionWindow extends Window {
const padding = document.createElement("div")
padding.classList.add("padding")

const searchContainer = document.createElement("div")
searchContainer.classList.add("pref-search")

const searchBar = document.createElement("input")
searchBar.classList.add("pref-search-bar")
searchBar.type = "text"
Expand All @@ -44,14 +50,15 @@ export class ThemeSelectionWindow extends Window {
searchBar.oninput = () => {
this.filterGrid(searchBar.value)
}
searchContainer.appendChild(searchBar)

const grid = document.createElement("div")
grid.classList.add("theme-grid")
this.grid = grid

const optionTray = this.createOptionTray()

padding.replaceChildren(searchBar, grid, optionTray)
padding.replaceChildren(searchContainer, grid, optionTray)

this.viewElement.appendChild(padding)
}
Expand Down Expand Up @@ -128,7 +135,60 @@ export class ThemeSelectionWindow extends Window {
this.actions.del = del
optionTray.appendChild(del)

// import/export
const imp = document.createElement("button")
imp.appendChild(Icons.getIcon("UPLOAD", 16))
imp.appendChild(document.createTextNode("Import"))
imp.onclick = async () => {
const fileHandlers = await showOpenFilePicker({
_preferPolyfill: false,
excludeAcceptAllOption: false,
multiple: true,
accepts: [{ extensions: ["txt"] }],
})
fileHandlers.forEach(handle => {
handle
.getFile()
.then(file => file.text())
.then(text => {
const theme = Themes.parseThemeText(text)
let themeName = basename(handle.name, ".txt")
if (!theme) {
WaterfallManager.createFormatted(
"Failed to load theme " + themeName,
"error"
)
return
}
themeName = this.getNonConflictingName(themeName)
Themes.createUserTheme(themeName, theme)
this.loadGrid()
})
})
}
this.actions.imp = imp
optionTray.appendChild(imp)

const exp = document.createElement("button")
exp.appendChild(Icons.getIcon("DOWNLOAD", 16))
exp.appendChild(document.createTextNode("Export"))
exp.onclick = () => {
const str = Themes.exportCurrentTheme({ spaces: true })
const file = new File([str], Options.general.theme + ".txt", {
type: "text/plain",
})
const a = document.createElement("a")
const url = URL.createObjectURL(file)

a.href = url
a.download = file.name
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
window.URL.revokeObjectURL(url)
}
this.actions.exp = exp
exp.disabled = true
optionTray.appendChild(exp)

return optionTray
}
Expand Down Expand Up @@ -204,6 +264,7 @@ export class ThemeSelectionWindow extends Window {
this.actions.edit.disabled = isDefault
this.actions.del.disabled = isDefault
this.actions.copy.disabled = false
this.actions.exp.disabled = false
}

container.dataset.id = id
Expand All @@ -217,6 +278,7 @@ export class ThemeSelectionWindow extends Window {
this.actions.edit.disabled = isDefault
this.actions.del.disabled = isDefault
this.actions.copy.disabled = false
this.actions.exp.disabled = false
}
}
}
Expand Down
31 changes: 26 additions & 5 deletions app/src/util/Theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class Themes {
}

private static validateTheme(theme: ThemeString): Theme {
const newTheme = DEFAULT_THEMES["default"]
const newTheme = { ...DEFAULT_THEMES["default"] }
if (typeof theme !== "object") return newTheme
for (const prop of THEME_VAR_WHITELIST) {
if (theme[prop] === undefined) continue
Expand Down Expand Up @@ -119,20 +119,31 @@ export class Themes {
) as ThemeString
}

static exportCurrentTheme(code = false) {
if (code) {
static exportCurrentTheme(options?: { code?: boolean; spaces?: boolean }) {
options = {
code: false,
spaces: false,
...options,
}
if (options.code) {
return JSON.stringify(
Object.fromEntries(
Object.entries(this.currentTheme).map(([prop, col]) => [
prop,
`^new Color('${col.toHexa()}')^`,
])
)
),
null,
options.spaces ? 2 : 0
)
.replaceAll(`"^`, "")
.replaceAll(`^"`, "")
}
return JSON.stringify(this.convertThemeToString(this.currentTheme))
return JSON.stringify(
this.convertThemeToString(this.currentTheme),
null,
options.spaces ? 2 : 0
)
}

static createUserTheme(id: string, base?: Theme) {
Expand Down Expand Up @@ -165,4 +176,14 @@ export class Themes {
delete this._userThemes[id]
this._saveUserThemes()
}

static parseThemeText(text: string) {
let theme
try {
theme = JSON.parse(text)
} catch (_) {
return null
}
return this.validateTheme(theme)
}
}

0 comments on commit 5188c37

Please sign in to comment.