Compare commits
No commits in common. "07300eb96f8e0a4b4867baf06d670452d922f528" and "3068c719cd5a8e6004fdd0ea13533d78371ff50c" have entirely different histories.
07300eb96f
...
3068c719cd
|
|
@ -1,4 +1,4 @@
|
||||||
import { Hype } from '@because/hype'
|
import { Hype } from 'hype'
|
||||||
|
|
||||||
const app = new Hype
|
const app = new Hype
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Hype } from '@because/hype'
|
import { Hype } from 'hype'
|
||||||
import { define, stylesToCSS } from '@because/forge'
|
import { define, stylesToCSS } from '@because/forge'
|
||||||
|
|
||||||
const app = new Hype()
|
const app = new Hype()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Hype } from '@because/hype'
|
import { Hype } from 'hype'
|
||||||
|
|
||||||
const app = new Hype
|
const app = new Hype
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -276,20 +276,18 @@ async function getApp(name: string) {
|
||||||
console.log(color.green(`✓ Downloaded ${name}`))
|
console.log(color.green(`✓ Downloaded ${name}`))
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAppPackage(): { name?: string; scripts?: { toes?: string } } | null {
|
function isApp(): boolean {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf-8'))
|
const pkg = JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf-8'))
|
||||||
} catch {
|
return !!pkg?.scripts?.toes
|
||||||
return null
|
} catch (e) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isApp = () => !!getAppPackage()?.scripts?.toes
|
|
||||||
|
|
||||||
function resolveAppName(name?: string): string | undefined {
|
function resolveAppName(name?: string): string | undefined {
|
||||||
if (name) return name
|
if (name) return name
|
||||||
const pkg = getAppPackage()
|
if (isApp()) return basename(process.cwd())
|
||||||
if (pkg?.scripts?.toes) return pkg.name || basename(process.cwd())
|
|
||||||
console.error('No app specified and current directory is not a toes app')
|
console.error('No app specified and current directory is not a toes app')
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
@ -650,22 +648,6 @@ async function rmApp(arg?: string) {
|
||||||
program
|
program
|
||||||
.name('toes')
|
.name('toes')
|
||||||
.version('0.0.1', '-v, --version')
|
.version('0.0.1', '-v, --version')
|
||||||
.addHelpText('beforeAll', (ctx) => {
|
|
||||||
if (ctx.command === program) {
|
|
||||||
return color.bold().cyan('\n🐾 Toes') + color.gray(' - personal web appliance\n')
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
})
|
|
||||||
.configureOutput({
|
|
||||||
writeOut: (str) => {
|
|
||||||
const colored = str
|
|
||||||
.replace(/^(Usage:)/gm, color.yellow('$1'))
|
|
||||||
.replace(/^(Commands:)/gm, color.yellow('$1'))
|
|
||||||
.replace(/^(Options:)/gm, color.yellow('$1'))
|
|
||||||
.replace(/^(Arguments:)/gm, color.yellow('$1'))
|
|
||||||
process.stdout.write(colored)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('info')
|
.command('info')
|
||||||
|
|
|
||||||
|
|
@ -403,11 +403,6 @@ const FormActions = define('FormActions', {
|
||||||
let newAppError = ''
|
let newAppError = ''
|
||||||
let newAppCreating = false
|
let newAppCreating = false
|
||||||
|
|
||||||
// Delete App confirmation
|
|
||||||
let deleteAppError = ''
|
|
||||||
let deleteAppDeleting = false
|
|
||||||
let deleteAppTarget: App | null = null
|
|
||||||
|
|
||||||
async function createNewApp(input: HTMLInputElement) {
|
async function createNewApp(input: HTMLInputElement) {
|
||||||
const name = input.value.trim().toLowerCase().replace(/\s+/g, '-')
|
const name = input.value.trim().toLowerCase().replace(/\s+/g, '-')
|
||||||
|
|
||||||
|
|
@ -489,82 +484,6 @@ function openNewAppModal() {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete App confirmation modal
|
|
||||||
async function deleteApp(input: HTMLInputElement) {
|
|
||||||
if (!deleteAppTarget) return
|
|
||||||
|
|
||||||
const expected = `sudo rm ${deleteAppTarget.name}`
|
|
||||||
const value = input.value.trim()
|
|
||||||
|
|
||||||
if (value !== expected) {
|
|
||||||
deleteAppError = `Type "${expected}" to confirm`
|
|
||||||
rerenderModal()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteAppDeleting = true
|
|
||||||
deleteAppError = ''
|
|
||||||
rerenderModal()
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch(`/api/sync/apps/${deleteAppTarget.name}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
})
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error(`Failed to delete app: ${res.statusText}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Success - close modal and clear selection
|
|
||||||
if (selectedApp === deleteAppTarget.name) {
|
|
||||||
selectedApp = null
|
|
||||||
localStorage.removeItem('selectedApp')
|
|
||||||
}
|
|
||||||
closeModal()
|
|
||||||
} catch (err) {
|
|
||||||
deleteAppError = err instanceof Error ? err.message : 'Failed to delete app'
|
|
||||||
deleteAppDeleting = false
|
|
||||||
rerenderModal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function openDeleteAppModal(app: App) {
|
|
||||||
deleteAppError = ''
|
|
||||||
deleteAppDeleting = false
|
|
||||||
deleteAppTarget = app
|
|
||||||
|
|
||||||
const expected = `sudo rm ${app.name}`
|
|
||||||
|
|
||||||
openModal('Delete App', () => (
|
|
||||||
<Form onSubmit={(e: Event) => {
|
|
||||||
e.preventDefault()
|
|
||||||
const input = (e.target as HTMLFormElement).querySelector('input') as HTMLInputElement
|
|
||||||
deleteApp(input)
|
|
||||||
}}>
|
|
||||||
<p style={{ margin: '0 0 16px', color: theme('colors-textMuted') }}>
|
|
||||||
This will <strong style={{ color: theme('colors-error') }}>permanently delete</strong> <strong>{app.name}</strong> from the server.
|
|
||||||
</p>
|
|
||||||
<FormField>
|
|
||||||
<FormLabel for="delete-confirm">Type "{expected}" to confirm</FormLabel>
|
|
||||||
<FormInput
|
|
||||||
id="delete-confirm"
|
|
||||||
type="text"
|
|
||||||
placeholder={expected}
|
|
||||||
autofocus
|
|
||||||
/>
|
|
||||||
{deleteAppError && <FormError>{deleteAppError}</FormError>}
|
|
||||||
</FormField>
|
|
||||||
<FormActions>
|
|
||||||
<Button type="button" onClick={closeModal} disabled={deleteAppDeleting}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button type="submit" variant="danger" disabled={deleteAppDeleting}>
|
|
||||||
{deleteAppDeleting ? 'Deleting...' : 'Delete App'}
|
|
||||||
</Button>
|
|
||||||
</FormActions>
|
|
||||||
</Form>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actions - call API then let SSE update the state
|
// Actions - call API then let SSE update the state
|
||||||
const startApp = (name: string) => fetch(`/api/apps/${name}/start`, { method: 'POST' })
|
const startApp = (name: string) => fetch(`/api/apps/${name}/start`, { method: 'POST' })
|
||||||
const stopApp = (name: string) => fetch(`/api/apps/${name}/stop`, { method: 'POST' })
|
const stopApp = (name: string) => fetch(`/api/apps/${name}/stop`, { method: 'POST' })
|
||||||
|
|
@ -605,8 +524,8 @@ const AppDetail = ({ app }: { app: App }) => (
|
||||||
{app.name}
|
{app.name}
|
||||||
</MainTitle>
|
</MainTitle>
|
||||||
<HeaderActions>
|
<HeaderActions>
|
||||||
{/* <Button>Settings</Button> */}
|
<Button>Settings</Button>
|
||||||
<Button variant="danger" onClick={() => openDeleteAppModal(app)}>Delete</Button>
|
<Button variant="danger">Delete</Button>
|
||||||
</HeaderActions>
|
</HeaderActions>
|
||||||
</MainHeader>
|
</MainHeader>
|
||||||
<MainContent>
|
<MainContent>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user