refactor: move studio to frontends workspace

- Move studio from root to frontends/studio/
- Add owner-tools frontend for live blog admin UI
- Add shared ui component library
- Set up npm workspaces for frontends
- Add enhanced code block extension for editor

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Josh 2026-01-12 01:59:56 +02:00
parent c662e41b97
commit bef5dd4437
108 changed files with 8650 additions and 441 deletions

View file

@ -1,74 +0,0 @@
import { atom, map, computed, onMount } from 'nanostores'
import { createFetcherStore, createBlogMutatorStore } from './fetcher'
import type { InteractionConfig } from '../types'
const defaultConfig: InteractionConfig = {
comments_enabled: false,
reactions_enabled: false,
reaction_mode: 'emoji',
reaction_emojis: '👍,❤️,🎉,🚀',
upvote_icon: 'arrow',
reactions_require_auth: false,
}
export const $interactionsData = createFetcherStore<InteractionConfig>(['/api/studio/interaction-config'])
export const $interactions = map<InteractionConfig>({ ...defaultConfig })
export const $initialInteractions = atom<InteractionConfig | null>(null)
export const $hasInteractionChanges = computed(
[$interactions, $initialInteractions],
(current, initial) => initial !== null && JSON.stringify(current) !== JSON.stringify(initial)
)
export const $changedInteractionFields = computed(
[$interactions, $initialInteractions],
(current, initial) => {
if (!initial) return []
const changes: string[] = []
const labels: Record<string, string> = {
comments_enabled: 'Comments',
reactions_enabled: 'Reactions',
reaction_mode: 'Reaction mode',
reaction_emojis: 'Emojis',
upvote_icon: 'Upvote icon',
reactions_require_auth: 'Auth required',
}
for (const key of Object.keys(current) as (keyof InteractionConfig)[]) {
if (current[key] !== initial[key]) {
changes.push(labels[key] || key)
}
}
return changes
}
)
onMount($interactionsData, () => {
$interactionsData.listen(({ data }) => {
if (data) {
const merged = { ...defaultConfig, ...data }
$interactions.set(merged)
$initialInteractions.set(merged)
}
})
})
export const $saveInteractions = createBlogMutatorStore<InteractionConfig>(
async ({ data: config }) => {
const previous = $initialInteractions.get()
$initialInteractions.set({ ...config })
const res = await fetch('/api/studio/interaction-config', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config),
})
if (!res.ok) {
$initialInteractions.set(previous)
throw new Error('Failed to save interactions')
}
return config
}
)