Skip to content

Commit

Permalink
improve icon preview
Browse files Browse the repository at this point in the history
  • Loading branch information
heapwolf committed Mar 17, 2024
1 parent f01773f commit 6994414
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 222 deletions.
150 changes: 14 additions & 136 deletions src/components/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,6 @@ import Tonic from '@socketsupply/tonic'

import { resizePNG } from '../lib/icon.js'

function escapeCommitMessage (commitMessage) {
return commitMessage
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}

function rgbaToHex (rgbaString) {
const rgbaValues = rgbaString.match(/\d+/g)

Expand Down Expand Up @@ -64,122 +55,31 @@ class AppEditor extends Tonic {
this.editor.setValue(s)
}

async click (e) {
const el = Tonic.match(e.target, '[data-event]')
if (!el) return

const { event, value } = el.dataset

const pickerOpts = {
types: [
{
description: 'Images',
accept: {
'image/*': ['.png']
}
}
],
excludeAcceptAllOption: true,
multiple: false
}

if (event === 'size') {
const [fileHandle] = await window.showOpenFilePicker(pickerOpts)

/* const kFileSystemHandleFullName = Object
.getOwnPropertySymbols(data)
.find(s => s.description === 'kFileSystemHandleFullName')
const pathToFile = fileHandle[kFileSystemHandleFullName]
*/

const file = fileHandle.getFile()
const buf = await file.arrayBuffer()

if (value === 'all') {
const imagePreview = this.querySelector('.image-preview')
const blob = new Blob([buf], { type: 'image/png' })
const url = URL.createObjectURL(blob)
;[...imagePreview.querySelectorAll('img')].forEach(img => (img.src = url))
return
}

const blob = await resizePNG(buf, parseInt(value))

el.src = URL.createObjectURL(blob)
}
}

get selection () {
this.editor.getModel().getValueInRange(this.editor.getSelection())
}

async writeToDisk (projectNode, data) {
const app = document.querySelector('app-view')
const preview = document.querySelector('app-preview')
const app = this.props.parent

try {
await fs.promises.writeFile(projectNode.id, data)
} catch (err) {
console.error(`Unable to write to ${dest}`, err)
}

this.props.parent.reloadPreviewWindows()
app.reloadPreviewWindows()
}

async loadProjectNode (projectNode) {
if (!projectNode) return

const parent = this.props.parent
const ext = path.extname(projectNode.id)
const type = await lookup(ext.slice(1))
const elImagePreview = document.querySelector('.image-preview')
elImagePreview.style.display = 'none'

if (type.length) {
if (/image/.test(type[0].mime)) {
elImagePreview.style.display = 'grid'
// Display a preview for this type.
return
}
}

this.projectNode = projectNode

const fileName = projectNode.label
const imagePreview = this.querySelector('.image-preview')

if (projectNode.isDirectory && projectNode.parent.id === 'root') {
// in this case we can read all the patch files in the database
// and present them to the user in the form of "patch requests".

/*
* Each patch file has a header like this, so we can parse it, present
* it, and the user can decide if they want to apply the patch or not.
*
* From 97d72567aeb446e2b58262e88623f77ad0f7044f Mon Sep 17 00:00:00 2001
* From: heapwolf <[email protected]>
* Date: Thu, 14 Mar 2024 10:48:03 +0100
* Subject: [PATCH] fix write method and preview script
*
*/
}

if (projectNode.isDirectory && fileName === 'icons') {
const iconPath = path.join(projectNode.id, 'icon.png')
const data = await fs.promises.readFile(iconPath)
const blob = new Blob([data], { type: 'image/png' })
const url = URL.createObjectURL(blob)
;[...imagePreview.querySelectorAll('img')].forEach(img => (img.src = url))
imagePreview.classList.add('show')
return
}

imagePreview.classList.remove('show')
const app = this.props.parent

if (!projectNode.isDirectory && this.editor) {
const ext = path.extname(projectNode.id)

const mappings = parent.state.settings.extensionLanguageMappings
const mappings = app.state.settings.extensionLanguageMappings
const lang = mappings[ext] || ext.slice(1)
monaco.editor.setModelLanguage(this.editor.getModel(), lang)
let data = await fs.promises.readFile(projectNode.id, 'utf8')
Expand Down Expand Up @@ -274,13 +174,13 @@ class AppEditor extends Tonic {
}

async refreshSettings () {
const parent = this.props.parent
this.editor.updateOptions(parent.state.settings?.editorOptions || {})
const app = this.props.parent
this.editor.updateOptions(app.state.settings?.editorOptions || {})
}

connected () {
let theme
const parent = this.props.parent
const app = this.props.parent

this.editor = monaco.editor.create(this.querySelector('.editor'), {
value: '',
Expand All @@ -298,21 +198,23 @@ class AppEditor extends Tonic {
const model = this.editor.getModel()

model.onDidChangeContent(async () => {
if (!this.projectNode) return
const currentProject = app.state.currentProject
if (!currentProject) return

const value = this.editor.getValue()
const coTerminal = document.querySelector('app-terminal')

if (this.projectNode.label === 'settings.json' && this.projectNode.parent.id === 'root') {
if (currentProject.label === 'settings.json' && currentProject.parent.id === 'root') {

try {
this.props.parent.state.settings = JSON.parse(value)
app.state.settings = JSON.parse(value)
} catch (err) {
coTerminal.error(`Unable to parse settings file (${err.message})`)
return
}

coTerminal.info(`Settings file updated.`)
parent.activatePreviewWindows()
app.activatePreviewWindows()
}

clearTimeout(this.debouncePropertiesRerender)
Expand All @@ -321,7 +223,7 @@ class AppEditor extends Tonic {
coProperties.reRender()
}, 1024)

this.writeToDisk(this.projectNode, value)
this.writeToDisk(currentProject, value)
})

window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
Expand All @@ -335,30 +237,6 @@ class AppEditor extends Tonic {

render () {
return this.html`
<div class="image-preview">
<div class="top">
<h1>Icon Preview</h1>
<tonic-button data-event="size" data-value="all">Update</tonic-button>
</div>
<div class="bottom">
<div class="size size-128">
<img data-event="size" data-value="128">
<label>128x128</label>
</div>
<div class="size size-64">
<img data-event="size" data-value="64">
<label>64x64</label>
</div>
<div class="size size-32">
<img data-event="size" data-value="32">
<label>32x32</label>
</div>
<div class="size size-16">
<img data-event="size" data-value="16">
<label>16x16</label>
</div>
</div>
</div>
<div class="editor"></div>
`
}
Expand Down
132 changes: 132 additions & 0 deletions src/components/image-preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import fs from 'socket:fs'
import path from 'socket:path'

import Tonic from '@socketsupply/tonic'

import * as ini from '../lib/ini.js'

class AppImagePreview extends Tonic {
async click (e) {
const el = Tonic.match(e.target, '[data-event]')
if (!el) return

const { event, value } = el.dataset

const pickerOpts = {
types: [
{
description: 'Images',
accept: {
'image/*': ['.png']
}
}
],
excludeAcceptAllOption: true,
multiple: false
}

if (event === 'size') {
const [fileHandle] = await window.showOpenFilePicker(pickerOpts)

/* const kFileSystemHandleFullName = Object
.getOwnPropertySymbols(data)
.find(s => s.description === 'kFileSystemHandleFullName')
const pathToFile = fileHandle[kFileSystemHandleFullName]
*/

const file = fileHandle.getFile()
const buf = await file.arrayBuffer()

if (value === 'all') {
const imagePreview = this.querySelector('.image-preview')
const blob = new Blob([buf], { type: 'image/png' })
const url = URL.createObjectURL(blob)
;[...imagePreview.querySelectorAll('img')].forEach(img => (img.src = url))
return
}

const blob = await resizePNG(buf, parseInt(value))

el.src = URL.createObjectURL(blob)
}
}

show () {
this.classList.add('show')
}

hide () {
this.classList.remove('show')
}

async load (projectNode) {
this.state.pathToFile = projectNode.id
this.reRender()
}

async render () {
const app = this.props.parent
const settings = app.state.settings
const currentProject = app.state.currentProject

let src = ''

if (!currentProject) return this.html``

const cwd = currentProject?.id

try {
const pathToConfigFile = path.join(cwd, 'socket.ini')
src = await fs.promises.readFile(pathToConfigFile, 'utf8')
} catch (err) {
const notifications = document.querySelector('#notifications')
notifications?.create({
type: 'error',
title: 'Error',
message: err.message
})
}

const getSizes = platform => ini
.get(src, platform, 'icon_sizes')
.replace(/"/g, '')
.split(' ')
.map(pair => {
let { 0: size, 1: scale } = pair.split('@')
scale = parseInt(scale)

const src = this.state.pathToFile.replace(path.HOME, '/user/home')
const scaled = size * scale

return this.html`
<div class="size">
<img data-event="size" width="${scaled}px" height="${scaled}px" src="${src}">
<label>${pair}</label>
</div>
`
})

return this.html`
<div class="top">
<h1>Icon Preview</h1>
<tonic-button data-event="size" data-value="all">Update</tonic-button>
</div>
<div class="bottom">
<h2>MacOS</h2>
<div class="icon-grid">${getSizes('mac')}</div>
<h2>iOS</h2>
<div class="icon-grid">${getSizes('ios')}</div>
<h2>Linux</h2>
<div class="icon-grid">${getSizes('linux')}</div>
<h2>Windows</h2>
<div class="icon-grid">${getSizes('win')}</div>
</div>
`
}
}

export { AppImagePreview }
export default AppImagePreview
17 changes: 17 additions & 0 deletions src/components/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ class AppProject extends Tonic {

const projectNode = this.getProjectNode(node)

// Check if the project has changed, refresh the props component
if (this.state.currentProject !== projectNode.id) {
this.props.parent.state.currentProject = projectNode
this.props.parent.reloadPreviewWindows()
Expand All @@ -474,6 +475,22 @@ class AppProject extends Tonic {

this.state.currentProject = projectNode.id

// Check if this is an image type that we can present
const ext = path.extname(node.id)
const type = await lookup(ext.slice(1))
const coImagePreview = document.querySelector('app-image-preview')

if (type.length) {
if (/image/.test(type[0].mime)) {
coImagePreview.load(node)
coImagePreview.show()
return
}
}

coImagePreview.hide()

// Load the code editor
const coEditor = document.querySelector('app-editor')
coEditor.loadProjectNode(node)
}
Expand Down
Loading

0 comments on commit 6994414

Please sign in to comment.