Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release live editor #1570

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ module.exports = {
j: true,
k: true,
param: true,
Props: true,
req: true,
res: true,
str: true,
Expand Down
11 changes: 5 additions & 6 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- uses: actions/cache@v3
- uses: actions/cache@v4
id: yarn-and-build-cache
with:
path: |
Expand All @@ -30,21 +30,20 @@ jobs:
restore-keys: |
${{ runner.os }}-node_modules-build-

- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 18
node-version-file: '.node-version'
cache: 'yarn'

# Install NPM dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
uses: cypress-io/github-action@v3
uses: cypress-io/github-action@v6
with:
build: yarn build
start: yarn preview
wait-on: 'http://localhost:3000'
record: true
headless: true
parallel: true
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
Expand Down
2 changes: 1 addition & 1 deletion .node-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20.17.0
20.18.0
33 changes: 17 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,50 +24,51 @@
"devDependencies": {
"@cypress/snapshot": "2.1.7",
"@fortawesome/fontawesome-free": "^6.5.1",
"@sveltejs/adapter-static": "3.0.4",
"@sveltejs/kit": "2.5.26",
"@sveltejs/vite-plugin-svelte": "^3.0.1",
"@testing-library/svelte": "4.2.3",
"@sveltejs/adapter-static": "3.0.6",
"@sveltejs/kit": "2.8.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/svelte": "^5.2.4",
"@types/dompurify": "^3.0.5",
"@types/lodash-es": "^4.17.12",
"@types/pako": "2.0.3",
"@types/uuid": "9.0.8",
"@typescript-eslint/eslint-plugin": "6.21.0",
"@typescript-eslint/parser": "6.21.0",
"@vitest/ui": "^1.1.3",
"@vitest/ui": "^2.1.4",
"autoprefixer": "^10.4.14",
"c8": "7.14.0",
"chai": "^4.3.7",
"cssnano": "^6.0.0",
"cypress": "12.17.4",
"cypress-localstorage-commands": "2.2.6",
"eslint": "8.57.0",
"eslint": "8.57.1",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-cypress": "2.15.2",
"eslint-plugin-es": "^4.1.0",
"eslint-plugin-no-only-tests": "^3.1.0",
"eslint-plugin-postcss-modules": "^2.0.0",
"eslint-plugin-svelte": "^2.35.1",
"eslint-plugin-svelte": "^2.45.1",
"eslint-plugin-tailwindcss": "^3.13.1",
"eslint-plugin-unicorn": "^50.0.1",
"eslint-plugin-vitest": "^0.5.0",
"esserializer": "^1.3.11",
"husky": "^8.0.3",
"jsdom": "^21.1.2",
"jsdom": "^25.0.1",
"lint-staged": "^15.2.0",
"node-html-parser": "^6.1.5",
"postcss": "^8.4.33",
"postcss-load-config": "5.1.0",
"prettier": "^3.1.0",
"prettier-plugin-svelte": "^3.1.2",
"prettier-plugin-svelte": "^3.2.6",
"prettier-plugin-tailwindcss": "^0.6.0",
"svelte": "^4.2.8",
"svelte-preprocess": "^5.1.3",
"svelte": "^5.0.0",
"svelte-preprocess": "^6.0.0",
"tailwindcss": "^3.4.1",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"vite": "^5.0.11",
"vitest": "^1.1.3",
"typescript": "^5.5.0",
"vite": "^5.4.4",
"vitest": "^2.1.4",
"vitest-dom": "^0.1.1"
},
"dependencies": {
Expand All @@ -79,11 +80,11 @@
"js-base64": "3.7.7",
"lodash-es": "^4.17.21",
"mermaid": "^11.3.0",
"monaco-editor": "0.51.0",
"monaco-editor": "0.52.0",
"pako": "2.1.0",
"plausible-tracker": "^0.3.8",
"random-word-slugs": "0.1.7",
"svg-pan-zoom": "3.6.1",
"svg-pan-zoom": "3.6.2",
"svg2roughjs": "^3.2.0",
"uuid": "9.0.1"
},
Expand Down
42 changes: 21 additions & 21 deletions src/lib/components/Actions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import { pakoSerde } from '$lib/util/serde';
import { stateStore } from '$lib/util/state';
import { logEvent } from '$lib/util/stats';
import { version as FAVersion } from '@fortawesome/fontawesome-free/package.json';
import dayjs from 'dayjs';
import { toBase64 } from 'js-base64';
import { version as FAVersion } from '@fortawesome/fontawesome-free/package.json';

const FONT_AWESOME_URL = `https://cdnjs.cloudflare.com/ajax/libs/font-awesome/${FAVersion}/css/all.min.css`;

Expand Down Expand Up @@ -149,7 +149,7 @@ ${svgString}`);
logEvent('copyMarkdown');
};

let gistURL = '';
let gistURL = $state('');
stateStore.subscribe(({ loader }) => {
if (loader?.type === 'gist') {
// @ts-expect-error Gist will have url
Expand All @@ -165,14 +165,14 @@ ${svgString}`);
logEvent('loadGist');
};

let iUrl: string;
let svgUrl: string;
let krokiUrl: string;
let mdCode: string;
let imagemodeselected = 'auto';
let userimagesize = 1080;
let iUrl: string | undefined = $state();
let svgUrl: string | undefined = $state();
let krokiUrl: string | undefined = $state();
let mdCode: string | undefined = $state();
let imagemodeselected = $state('auto');
let userimagesize = $state(1080);

let isNetlify = false;
let isNetlify = $state(false);
if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) {
isNetlify = true;
}
Expand All @@ -187,32 +187,32 @@ ${svgString}`);
<Card title="Actions" isOpen={false}>
<div class="m-2 flex flex-wrap gap-2">
{#if isClipboardAvailable()}
<button class="action-btn w-full" on:click={onCopyClipboard}
><i class="far fa-copy mr-2" /> Copy Image to clipboard
<button class="action-btn w-full" onclick={onCopyClipboard}
><i class="far fa-copy mr-2"></i> Copy Image to clipboard
</button>
{/if}
<button id="downloadPNG" class="action-btn flex-grow" on:click={onDownloadPNG}>
<i class="fas fa-download mr-2" /> PNG
<button id="downloadPNG" class="action-btn flex-grow" onclick={onDownloadPNG}>
<i class="fas fa-download mr-2"></i> PNG
</button>
<button id="downloadSVG" class="action-btn flex-grow" on:click={onDownloadSVG}>
<i class="fas fa-download mr-2" /> SVG
<button id="downloadSVG" class="action-btn flex-grow" onclick={onDownloadSVG}>
<i class="fas fa-download mr-2"></i> SVG
</button>
{#if rendererUrl}
<a target="_blank" rel="noreferrer" class="flex-grow" href={iUrl}>
<button class="action-btn w-full">
<i class="fas fa-external-link-alt mr-2" /> PNG
<i class="fas fa-external-link-alt mr-2"></i> PNG
</button>
</a>
<a target="_blank" rel="noreferrer" class="flex-grow" href={svgUrl}>
<button class="action-btn w-full">
<i class="fas fa-external-link-alt mr-2" /> SVG
<i class="fas fa-external-link-alt mr-2"></i> SVG
</button>
</a>
{/if}
{#if krokiRendererUrl}
<a target="_blank" rel="noreferrer" class="flex-grow" href={krokiUrl}>
<button class="action-btn w-full">
<i class="fas fa-external-link-alt mr-2" /> Kroki
<i class="fas fa-external-link-alt mr-2"></i> Kroki
</button>
</a>
{/if}
Expand Down Expand Up @@ -244,9 +244,9 @@ ${svgString}`);

{#if rendererUrl}
<div class="flex w-full items-center gap-2">
<input class="input" id="markdown" type="text" value={mdCode} on:click={onCopyMarkdown} />
<input class="input" id="markdown" type="text" value={mdCode} onclick={onCopyMarkdown} />
<label for="markdown">
<button class="btn btn-primary btn-md flex-auto" on:click={onCopyMarkdown}>
<button class="btn btn-primary btn-md flex-auto" onclick={onCopyMarkdown}>
Copy Markdown
</button>
</label>
Expand All @@ -261,7 +261,7 @@ ${svgString}`);
bind:value={gistURL}
placeholder="Enter Gist URL" />
<label for="gist">
<button class="btn btn-primary btn-md flex-auto" on:click={loadGist}> Load Gist </button>
<button class="btn btn-primary btn-md flex-auto" onclick={loadGist}> Load Gist </button>
</label>
</div>
{#if isNetlify}
Expand Down
48 changes: 36 additions & 12 deletions src/lib/components/Card/Card.svelte
Original file line number Diff line number Diff line change
@@ -1,33 +1,57 @@
<script lang="ts">
import type { Tab } from '$lib/types';
import type { Snippet } from 'svelte';
import { slide } from 'svelte/transition';
import Tabs from './Tabs.svelte';
export let isCloseable = true;
export let isOpen = true;
export let tabs: Tab[] = [];
export let activeTabID = '';
export let title: string;
$: isOpen = isCloseable ? isOpen : true;
$: isTabsShown = isOpen && tabs.length > 0;

interface Props {
isClosable?: boolean;
isOpen?: boolean;
tabs?: Tab[];
activeTabID?: string;
title: string;
onselect?: (tab: Tab) => void;
actions?: Snippet;
children?: Snippet;
}

let {
isClosable = true,
isOpen = true,
tabs = [],
activeTabID = '',
title,
onselect,
actions,
children
}: Props = $props();

const toggleCardOpen = () => {
if (isClosable) {
isOpen = !isOpen;
}
};

let isTabsShown = $derived(isOpen && tabs.length > 0);
</script>

<div class="card m-2 flex flex-grow flex-col overflow-hidden rounded shadow-2xl">
<div
role="toolbar"
tabindex="0"
class="bg-primary p-2 {isTabsShown ? 'pb-0' : ''} flex-none cursor-pointer"
on:click={() => (isOpen = !isOpen)}
on:keypress={() => (isOpen = !isOpen)}>
onclick={toggleCardOpen}
onkeypress={toggleCardOpen}>
<div class="flex justify-between">
<Tabs on:select {tabs} bind:isOpen {title} {isCloseable} {activeTabID} />
<Tabs {onselect} {tabs} bind:isOpen {title} {isClosable} {activeTabID} />
<div class="flex items-center gap-x-4 {isTabsShown ? '-mt-2' : ''}">
<slot name="actions" />
{@render actions?.()}
</div>
</div>
</div>
{#if isOpen}
<div class="card-body flex-grow overflow-auto p-0 text-base-content" transition:slide>
<slot />
{@render children?.()}
</div>
{/if}
</div>
53 changes: 31 additions & 22 deletions src/lib/components/Card/Tabs.svelte
Original file line number Diff line number Diff line change
@@ -1,44 +1,53 @@
<script lang="ts">
import type { Tab, TabEvents } from '$lib/types';
import { createEventDispatcher } from 'svelte';
import type { Tab } from '$lib/types';
import { fade } from 'svelte/transition';
export let isCloseable = true;
export let tabs: Tab[];
export let title: string;
export let isOpen = false;
export let activeTabID: string;

let {
isClosable = true,
tabs,
title,
isOpen = $bindable(false),
activeTabID = $bindable(),
onselect
}: {
isClosable?: boolean;
tabs: Tab[];
title: string;
isOpen?: boolean;
activeTabID: string;
onselect?: (tab: Tab) => void;
} = $props();

if (!activeTabID && tabs.length > 0) {
activeTabID = tabs[0].id;
}
const dispatch = createEventDispatcher<TabEvents>();

const toggleTabs = (tab: Tab) => {
activeTabID = tab.id;
dispatch('select', tab);
return (event: Event) => {
event.stopPropagation();
activeTabID = tab.id;
onselect?.(tab);
};
};
</script>

<div class="flex cursor-default">
<span
role="menubar"
tabindex="0"
class="mr-2 font-semibold"
on:click|stopPropagation={() => (isOpen = !isOpen)}
on:keypress|stopPropagation={() => (isOpen = !isOpen)}>
{#if isCloseable}
<i class="fas fa-chevron-right icon" class:isOpen />
<span role="menubar" tabindex="0" class="mr-2 font-semibold">
{#if isClosable}
<i class="fas fa-chevron-right icon mr-1" class:isOpen></i>
{/if}
{title}</span>
{title}
</span>
{#if isOpen && tabs}
<ul class="tabs" transition:fade>
{#each tabs as tab}
<div
role="tab"
tabindex="0"
class="tab tab-lifted {activeTabID === tab.id ? 'tab-active' : 'text-primary-content'}"
on:click|stopPropagation={() => toggleTabs(tab)}
on:keypress|stopPropagation={() => toggleTabs(tab)}>
<i class="mr-1 {tab.icon}" />
onclick={toggleTabs(tab)}
onkeypress={toggleTabs(tab)}>
<i class="mr-1 {tab.icon}"></i>
{tab.title}
</div>
{/each}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/Card/card.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { cleanup, render } from '@testing-library/svelte';
import { describe, expect, it, afterEach } from 'vitest';
import { afterEach, describe, expect, it } from 'vitest';
import Card from './Card.svelte';

describe('card.svelte', () => {
Expand Down
Loading
Loading