diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index eed0982..8ab8c04 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -21,6 +21,12 @@ jobs: # This is important to fetch the changes to the previous commit fetch-depth: 0 + steps: + - uses: pnpm/action-setup@v4 + with: + version: 9 + run_install: true + - name: Prettier Action uses: creyD/prettier_action@v4.3 with: diff --git a/.prettierrc b/.prettierrc index dddf1b3..cf830cf 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,10 +1,22 @@ { - "trailingComma": "all", - "tabWidth": 2, - "printWidth": 100, - "semi": false, - "singleQuote": true, - "useTabs": false, - "arrowParens": "avoid", - "bracketSpacing": true + "trailingComma": "all", + "tabWidth": 2, + "printWidth": 100, + "semi": false, + "singleQuote": true, + "useTabs": false, + "arrowParens": "avoid", + "bracketSpacing": true, + "plugins": ["@trivago/prettier-plugin-sort-imports"], + "importOrder": [ + "^solid-js(.*)$", + "^solid-(.*)$", + "^shiki(.*)$", + "^~/components/ui/(.*)$", + "^~/(.*)$", + "^[./]" + ], + "importOrderGroupNamespaceSpecifiers": true, + "importOrderSeparation": true, + "importOrderSortSpecifiers": true } diff --git a/package.json b/package.json index 313bad5..4dc6f92 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "postinstall": "pnpm build", "dev": "pnpm run --filter ./playgrounds/app dev", - "build": "cd packages/shikicode && pnpm build && cd ../shiki-magic-move && pnpm build && cd ../../playgrounds/app && pnpm build", + "build": "cd packages/shikicode && pnpm build && cd ../shiki-magic-move && pnpm build", "test": "turbo run test --filter=./packages/*", "typecheck": "turbo run typecheck --filter=./packages/*", "build-test": "turbo run build test --filter=./packages/*", @@ -22,6 +22,7 @@ }, "devDependencies": { "@changesets/cli": "^2.26.0", + "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/node": "^18.11.18", "concurrently": "^7.6.0", "jsdom": "^21.0.0", diff --git a/packages/shiki-magic-move/src/solid/ShikiMagicMove.tsx b/packages/shiki-magic-move/src/solid/ShikiMagicMove.tsx index 7dcdb91..6f5fb18 100644 --- a/packages/shiki-magic-move/src/solid/ShikiMagicMove.tsx +++ b/packages/shiki-magic-move/src/solid/ShikiMagicMove.tsx @@ -19,31 +19,40 @@ export interface ShikiMagicMoveProps { } export function ShikiMagicMove(props: ShikiMagicMoveProps) { - const codeToTokens = (code: string, lineNumbers?: boolean) => - codeToKeyedTokens( - props.highlighter, - code, - { - lang: props.lang, - theme: props.theme, - }, - lineNumbers, - ) + const codeToTokens = createMemo(() => { + return (code: string, lineNumbers?: boolean) => + codeToKeyedTokens( + props.highlighter, + code, + { + lang: props.lang, + theme: props.theme, + }, + lineNumbers, + ) + }) - const machine = createMagicMoveMachine((code, lineNumbers) => codeToTokens(code, lineNumbers)) + const machine = createMemo(() => { + const newCodeToTokens = codeToTokens() + return createMagicMoveMachine((code, lineNumbers) => newCodeToTokens(code, lineNumbers)) + }) const result = createMemo(() => { const lineNumbers = props.options?.lineNumbers ?? false if ( - props.code === machine.current.code && - props.theme === machine.current.themeName && - props.lang === machine.current.lang && - lineNumbers === machine.current.lineNumbers + props.code === machine().current.code && + props.theme === machine().current.themeName && + props.lang === machine().current.lang && + lineNumbers === machine().current.lineNumbers ) { - return machine + return machine() + } + try { + return machine().commit(props.code, props.options) + // eslint-disable-next-line unused-imports/no-unused-vars + } catch (e) { + return machine() } - - return machine.commit(props.code, props.options) }) return ( diff --git a/packages/shiki-magic-move/src/solid/ShikiMagicMoveRenderer.tsx b/packages/shiki-magic-move/src/solid/ShikiMagicMoveRenderer.tsx index c1cfc35..ea30dbb 100644 --- a/packages/shiki-magic-move/src/solid/ShikiMagicMoveRenderer.tsx +++ b/packages/shiki-magic-move/src/solid/ShikiMagicMoveRenderer.tsx @@ -21,15 +21,16 @@ export interface ShikiMagicMoveRendererProps { * A wrapper component to `MagicMoveRenderer` */ export function ShikiMagicMoveRenderer(props: ShikiMagicMoveRendererProps) { - let container: HTMLPreElement - let renderer: Renderer + // eslint-disable-next-line no-undef-init, prefer-const + let container: HTMLPreElement | undefined = undefined + // eslint-disable-next-line no-undef-init + let renderer: Renderer | undefined = undefined const [isMounted, setIsMounted] = createSignal(false) createEffect(() => { - if (!container) return - if (!isMounted()) { + if (container !== undefined && !isMounted()) { // Remove previous content - container.innerHTML = '' + ;(container as HTMLPreElement).innerHTML = '' setIsMounted(true) renderer = new Renderer(container) } @@ -37,16 +38,21 @@ export function ShikiMagicMoveRenderer(props: ShikiMagicMoveRendererProps) { createEffect(() => { async function render() { - if (!renderer) return + if (!renderer) { + return + } Object.assign(renderer.options, props.options) if (props.animate === undefined || props.animate === true) { - if (props.previous) renderer.replace(props.previous) + if (props.previous) { + renderer.replace(props.previous) + } props.onStart?.() await renderer.render(props.tokens) props.onEnd?.() + // eslint-disable-next-line style/brace-style } else { renderer.replace(props.tokens) } @@ -57,7 +63,6 @@ export function ShikiMagicMoveRenderer(props: ShikiMagicMoveRendererProps) { return (
=18" }, "devDependencies": { + "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/jsonwebtoken": "^9.0.7", "drizzle-kit": "^0.26.2", "tsx": "^4.19.1" diff --git a/playgrounds/app/src/app.css b/playgrounds/app/src/app.css index 75b00aa..f963b25 100644 --- a/playgrounds/app/src/app.css +++ b/playgrounds/app/src/app.css @@ -102,9 +102,7 @@ body { @apply bg-background text-foreground; - font-feature-settings: - 'rlig' 1, - 'calt' 1; + font-feature-settings: 'rlig' 1, 'calt' 1; } } @@ -148,3 +146,27 @@ #app { @apply min-h-screen flex flex-col; } + +.collapsible__content { + overflow: hidden; + animation: slideUp 300ms ease-out; +} +.collapsible__content[data-expanded] { + animation: slideDown 300ms ease-out; +} +@keyframes slideDown { + from { + height: 0; + } + to { + height: var(--kb-collapsible-content-height); + } +} +@keyframes slideUp { + from { + height: var(--kb-collapsible-content-height); + } + to { + height: 0; + } +} diff --git a/playgrounds/app/src/app.tsx b/playgrounds/app/src/app.tsx index 5e8df30..5479ae9 100644 --- a/playgrounds/app/src/app.tsx +++ b/playgrounds/app/src/app.tsx @@ -1,11 +1,14 @@ -import { Suspense } from 'solid-js' +import '@fontsource/bungee-inline' +import '@fontsource/roboto' import { Router } from '@solidjs/router' import { FileRoutes } from '@solidjs/start/router' + +import { Suspense } from 'solid-js' + import { Toaster } from 'solid-sonner' + import Header from '~/components/Header' -import Footer from './components/Footer' -import '@fontsource/bungee-inline' -import '@fontsource/roboto' + import './app.css' export default function App() { diff --git a/playgrounds/app/src/components/Editor.tsx b/playgrounds/app/src/components/Editor.tsx index a081b87..fa7598e 100644 --- a/playgrounds/app/src/components/Editor.tsx +++ b/playgrounds/app/src/components/Editor.tsx @@ -1,21 +1,57 @@ -import { interpolate, interpolateColors, Easing } from 'remotion' +import wasmURL from '@ffmpeg/core/wasm?url' +import coreURL from '@ffmpeg/core?url' +import { FFmpeg } from '@ffmpeg/ffmpeg' +import { fetchFile, toBlobURL } from '@ffmpeg/util' +import { useNavigate } from '@solidjs/router' +import clsx from 'clsx' +import { openDB } from 'idb' import { encode } from 'modern-gif' import workerUrl from 'modern-gif/worker?url' +import { Easing, interpolate, interpolateColors } from 'remotion' + +import { Show, createEffect, createMemo, createSignal, onCleanup, onMount } from 'solid-js' +import { SetStoreFunction } from 'solid-js/store' + +import { FaSolidCaretDown, FaSolidCaretUp } from 'solid-icons/fa' +import { HiOutlineCog } from 'solid-icons/hi' +import { toast } from 'solid-sonner' + +import type { HighlighterGeneric } from 'shiki' +import { bundledLanguages, bundledThemes, createHighlighter } from 'shiki' import 'shiki-magic-move/dist/style.css' +import { ShikiMagicMove } from 'shiki-magic-move/solid' +import { MagicMoveElement } from 'shiki-magic-move/types' + import { - ComboboxItem, - ComboboxItemLabel, - ComboboxItemIndicator, + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '~/components/ui/accordion' +import { Button } from '~/components/ui/button' +import { Checkbox } from '~/components/ui/checkbox' +import { Collapsible, CollapsibleContent } from '~/components/ui/collapsible' +import { + Combobox, + ComboboxContent, ComboboxControl, ComboboxInput, + ComboboxItem, + ComboboxItemIndicator, + ComboboxItemLabel, ComboboxTrigger, - ComboboxContent, - Combobox, } from '~/components/ui/combobox' -import { Button } from '~/components/ui/button' -import { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/ui/tabs' -import { TextField, TextFieldInput } from '~/components/ui/text-field' -import { MagicMoveElement } from 'shiki-magic-move/types' +import { Dialog, DialogContent, DialogFooter } from '~/components/ui/dialog' +import { Label } from '~/components/ui/label' +import { ProgressCircle } from '~/components/ui/progress-circle' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '~/components/ui/select' +import { Separator } from '~/components/ui/separator' import { Slider, SliderFill, @@ -24,38 +60,14 @@ import { SliderTrack, SliderValueLabel, } from '~/components/ui/slider' -import clsx from 'clsx' -import { Checkbox } from '~/components/ui/checkbox' -import { Label } from '~/components/ui/label' -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '~/components/ui/dialog' -import { - createEffect, - createMemo, - createResource, - createSignal, - onCleanup, - Setter, - Show, -} from 'solid-js' -import { createHighlighter, bundledThemes, bundledLanguages } from 'shiki' -import { ShikiMagicMove } from 'shiki-magic-move/solid' -import { AnimationFrameConfig, SnippetSettings } from '~/types' -import { authFetch } from '~/lib/utils' -import { useNavigate } from '@solidjs/router' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/ui/tabs' +import { TextField, TextFieldInput } from '~/components/ui/text-field' + import { authToken } from '~/lib/store' -import { toast } from 'solid-sonner' -import { Separator } from './ui/separator' -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select' -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from './ui/accordion' +import { authFetch } from '~/lib/utils' +import { AnimationFrameConfig, SnippetSettings } from '~/types' + import { ShikiCodeBlock } from './ShikiCodeBlock' -import { SetStoreFunction } from 'solid-js/store' const animationSeconds = 1 const animationFPS = 30 @@ -106,14 +118,29 @@ export default function Editor(props: EditorProps) { const [isShowingGifDialog, setIsShowingGifDialog] = createSignal(false) const [title, setTitle] = createSignal(props.snippetSettings.title) const [isSaving, setIsSaving] = createSignal(false) + const [highlighter, setHighlighter] = createSignal| undefined>() + + const [isShowingFfmpegDialog, setIsShowingFfmpegDialog] = createSignal(false) + const [ffmpegLoaded, setFfmpegLoaded] = createSignal(false) + const [isDownloadingFfmpeg, setIsDownloadingFfmpeg] = createSignal(false) + const [isGeneratingVideo, setIsGeneratingVideo] = createSignal(false) + const [videoProgress, setVideoProgress] = createSignal(0) + const ffmpeg = new FFmpeg() + const [settingsCollapsed, setSettingsCollapsed] = createSignal(false) + + onMount(() => { + if (document.body.clientWidth < 768) { + setSettingsCollapsed(true) + } + }) - const [highlighter] = createResource(async () => { - const newHighlighter = await createHighlighter({ - themes: Object.keys(bundledThemes), - langs: Object.keys(bundledLanguages), + createEffect(() => { + createHighlighter({ + themes: [props.snippetSettings.theme], + langs: [props.snippetSettings.language], + }).then(newHighlighter => { + setHighlighter(newHighlighter) }) - - return newHighlighter }) createEffect(() => { @@ -127,6 +154,8 @@ export default function Editor(props: EditorProps) { props.snippetSettings.codeLeft !== '' && props.snippetSettings.codeRight !== '' && !isResizing() && + !isShowingGifDialog() && + !isShowingFfmpegDialog() && isLooping() ) { if (toggled()) { @@ -251,413 +280,427 @@ export default function Editor(props: EditorProps) { return ( <> - --- -- -props.setSnippetSettings('theme', newTheme || '')} - placeholder="Search a theme..." - itemComponent={props => ( - -- - )} - > -{props.item.rawValue} -- - -- - - - -- -props.setSnippetSettings('language', newLanguage || '')} - placeholder="Search a Language..." - itemComponent={props => ( - -- - )} - > -{props.item.rawValue} -- - -- - - - - + +