Compare commits
No commits in common. "2a6523b9f132c51ce94bef9a90f58a1c2047dbbc" and "f3a8fa17e6742221f1c47c1dfe8d4473410b5efe" have entirely different histories.
2a6523b9f1
...
f3a8fa17e6
@ -1,5 +0,0 @@
|
|||||||
.svelte-kit/
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
node_modules/
|
|
||||||
test-results/
|
|
24
.gitignore
vendored
24
.gitignore
vendored
@ -1,24 +0,0 @@
|
|||||||
node_modules
|
|
||||||
|
|
||||||
# Output
|
|
||||||
.output
|
|
||||||
.vercel
|
|
||||||
/.svelte-kit
|
|
||||||
/build
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Env
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
!.env.test
|
|
||||||
|
|
||||||
# Vite
|
|
||||||
vite.config.js.timestamp-*
|
|
||||||
vite.config.ts.timestamp-*
|
|
||||||
|
|
||||||
# tests
|
|
||||||
test-results
|
|
@ -1,4 +0,0 @@
|
|||||||
# Package Managers
|
|
||||||
package-lock.json
|
|
||||||
pnpm-lock.yaml
|
|
||||||
yarn.lock
|
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"useTabs": false,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "all",
|
|
||||||
"printWidth": 100,
|
|
||||||
"plugins": ["prettier-plugin-svelte"],
|
|
||||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
|
||||||
}
|
|
29
.vscode/settings.json
vendored
29
.vscode/settings.json
vendored
@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"cSpell.words": [
|
|
||||||
"devcontainers",
|
|
||||||
"doctypes",
|
|
||||||
"esbenp",
|
|
||||||
"iconify",
|
|
||||||
"pandoc",
|
|
||||||
"pdoc",
|
|
||||||
"rehype",
|
|
||||||
"resumarkdown",
|
|
||||||
"résumé",
|
|
||||||
"spacebar",
|
|
||||||
"tablist",
|
|
||||||
"testid",
|
|
||||||
"texlive",
|
|
||||||
"textbox"
|
|
||||||
],
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"editor.insertSpaces": true,
|
|
||||||
"editor.tabSize": 2,
|
|
||||||
"files.insertFinalNewline": true,
|
|
||||||
"svelte.enable-ts-plugin": true,
|
|
||||||
"[javascript][json][jsonc][typescript]": {
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
||||||
},
|
|
||||||
"[svelte]": {
|
|
||||||
"editor.defaultFormatter": "svelte.svelte-vscode"
|
|
||||||
}
|
|
||||||
}
|
|
17
Dockerfile
17
Dockerfile
@ -1,17 +0,0 @@
|
|||||||
# This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
# obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
FROM node:22.11.0-bookworm-slim AS builder
|
|
||||||
WORKDIR /app
|
|
||||||
COPY . .
|
|
||||||
RUN npm install && npm run build
|
|
||||||
|
|
||||||
FROM node:22.11.0-bookworm-slim AS runner
|
|
||||||
LABEL maintainer="Nicola Clark <nicola@slottedspoon.dev>"
|
|
||||||
EXPOSE 3000
|
|
||||||
WORKDIR /app
|
|
||||||
COPY package.json package-lock.json ./
|
|
||||||
COPY --from=builder /app/build ./build
|
|
||||||
RUN npm ci --omit dev
|
|
||||||
CMD ["node", "build"]
|
|
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024 Nicola Clark
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
22
README.md
22
README.md
@ -1,23 +1,3 @@
|
|||||||
# resumarkdown
|
# resumarkdown
|
||||||
|
|
||||||
Create a resume with Markdown and CSS
|
Create a résumé with Markdown and CSS
|
||||||
|
|
||||||
## Previous location
|
|
||||||
|
|
||||||
This repository was previously hosted on
|
|
||||||
[GitHub](https://github.com/tweakdeveloper/resumarkdown). Due to GitHub's
|
|
||||||
expansion of Copilot (see [the License section](#license) for further details),
|
|
||||||
it has been moved to this self-hosted repository.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This repository's code was licensed under the terms of
|
|
||||||
[the MIT license](LICENSE.up-to-ea30f9) up to and including commit ea30f9. You
|
|
||||||
may apply this license's terms to all work that appears in the repository prior
|
|
||||||
to this commit ("this commit" referring to the commit immediately following
|
|
||||||
ea30f9).
|
|
||||||
|
|
||||||
From this commit forward, the code in this repo is licensed under the terms of
|
|
||||||
the [Mozilla Public License 2.0](https://mozilla.org/MPL/2.0/). This change was
|
|
||||||
made due to GitHub's lack of transparency vis-à-vis Copilot's use of public
|
|
||||||
repositories for the purposes of training their LLM.
|
|
3891
package-lock.json
generated
3891
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
51
package.json
51
package.json
@ -1,51 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "resumarkdown",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"private": true,
|
|
||||||
"license": "MPL-2.0",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite dev --host",
|
|
||||||
"build": "vite build",
|
|
||||||
"preview": "vite preview",
|
|
||||||
"test": "npm run test:integration && npm run test:unit",
|
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
||||||
"test:integration": "playwright test",
|
|
||||||
"test:unit": "vitest",
|
|
||||||
"lint": "prettier --check .",
|
|
||||||
"format": "prettier --write ."
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"hast": "^0.0.2",
|
|
||||||
"hastscript": "^9.0.0",
|
|
||||||
"iconify-icon": "^2.1.0",
|
|
||||||
"rehype-document": "^7.0.3",
|
|
||||||
"rehype-sanitize": "^6.0.0",
|
|
||||||
"rehype-stringify": "^10.0.1",
|
|
||||||
"remark-parse": "^11.0.0",
|
|
||||||
"remark-rehype": "^11.1.1",
|
|
||||||
"ua-parser-js": "^1.0.39",
|
|
||||||
"unified": "^11.0.5"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@playwright/test": "^1.28.1",
|
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
|
||||||
"@sveltejs/adapter-node": "^5.2.9",
|
|
||||||
"@sveltejs/kit": "^2.5.27",
|
|
||||||
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.0",
|
|
||||||
"@types/node": "^22.9.0",
|
|
||||||
"@types/ua-parser-js": "^0.7.39",
|
|
||||||
"@types/user-agents": "^1.0.4",
|
|
||||||
"less": "^4.2.0",
|
|
||||||
"prettier": "^3.1.1",
|
|
||||||
"prettier-plugin-svelte": "^3.2.6",
|
|
||||||
"svelte": "^5.0.0-next.0",
|
|
||||||
"svelte-check": "^4.0.0",
|
|
||||||
"svelte-preprocess": "^6.0.3",
|
|
||||||
"typescript": "^5.5.0",
|
|
||||||
"user-agents": "^1.1.325",
|
|
||||||
"vite": "^5.4.4",
|
|
||||||
"vitest": "^2.0.0"
|
|
||||||
},
|
|
||||||
"type": "module"
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { PlaywrightTestConfig } from '@playwright/test';
|
|
||||||
|
|
||||||
const config: PlaywrightTestConfig = {
|
|
||||||
webServer: {
|
|
||||||
command: 'npm run build && npm run preview',
|
|
||||||
port: 4173,
|
|
||||||
},
|
|
||||||
expect: {
|
|
||||||
timeout: 2.5 * 1000,
|
|
||||||
},
|
|
||||||
testDir: 'tests',
|
|
||||||
testMatch: /(.+\.)?(test|spec)\.[jt]s/,
|
|
||||||
timeout: 5 * 1000,
|
|
||||||
use: {
|
|
||||||
viewport: {
|
|
||||||
height: 900,
|
|
||||||
width: 1600,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
19
src/app.d.ts
vendored
19
src/app.d.ts
vendored
@ -1,19 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// See https://kit.svelte.dev/docs/types#app
|
|
||||||
// for information about these interfaces
|
|
||||||
declare global {
|
|
||||||
namespace App {
|
|
||||||
// interface Error {}
|
|
||||||
// interface Locals {}
|
|
||||||
// interface PageData {}
|
|
||||||
// interface PageState {}
|
|
||||||
// interface Platform {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {};
|
|
17
src/app.html
17
src/app.html
@ -1,17 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<!--
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
-->
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
%sveltekit.head%
|
|
||||||
</head>
|
|
||||||
<body data-sveltekit-preload-data="hover">
|
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
25
src/app.less
25
src/app.less
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import 'styles.less';
|
|
||||||
|
|
||||||
@import (css) url('https://fonts.googleapis.com/css2?family=Rubik:wght@400..700&display=swap');
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: @fonts-sans;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-weight: @font-w-bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { describe, it, expect } from 'vitest';
|
|
||||||
|
|
||||||
describe('sum test', () => {
|
|
||||||
it('adds 1 + 2 to equal 3', () => {
|
|
||||||
expect(1 + 2).toBe(3);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,23 +0,0 @@
|
|||||||
<!--
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
interface Props {
|
|
||||||
code: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { code = $bindable() }: Props = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<textarea bind:value={code}></textarea>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
textarea {
|
|
||||||
.container-content();
|
|
||||||
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,42 +0,0 @@
|
|||||||
<!--
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { type Snippet } from 'svelte';
|
|
||||||
|
|
||||||
import { pane as navStore, type Pane } from '$lib/stores/nav.js';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
pane: Pane;
|
|
||||||
testid?: string;
|
|
||||||
children?: Snippet;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { pane, testid, children }: Props = $props();
|
|
||||||
|
|
||||||
// don't show pane if we're not the current pane
|
|
||||||
let hidden: boolean = $derived(pane !== $navStore);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div id={`pane-${pane}`} data-testid={testid} role="tabpanel" class:hidden>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
div {
|
|
||||||
.container();
|
|
||||||
|
|
||||||
grid-area: editor;
|
|
||||||
|
|
||||||
&.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: @sizes[lg]) {
|
|
||||||
border-right: 2px solid @color-dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,33 +0,0 @@
|
|||||||
<!--
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { type Snippet } from 'svelte';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
children: Snippet;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { children }: Props = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<h1>{@render children()}</h1>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
h1 {
|
|
||||||
background-color: @color-dark;
|
|
||||||
font-size: @font-s-lg;
|
|
||||||
color: @color-light;
|
|
||||||
padding: @padding-md;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
grid-area: headline;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,72 +0,0 @@
|
|||||||
<!--
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { type Snippet } from 'svelte';
|
|
||||||
|
|
||||||
import { pane, type Pane } from '$lib/stores/nav.js';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
destination: Pane;
|
|
||||||
children: Snippet;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { destination, children }: Props = $props();
|
|
||||||
|
|
||||||
let selected: boolean = $derived(destination === $pane);
|
|
||||||
|
|
||||||
function handleKey({ key }: KeyboardEvent) {
|
|
||||||
if (key === ' ' || key === 'Enter' || key === 'Spacebar') {
|
|
||||||
navigate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function navigate() {
|
|
||||||
pane.set(destination);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<li
|
|
||||||
aria-controls={`pane-${destination}`}
|
|
||||||
aria-selected={selected}
|
|
||||||
role="tab"
|
|
||||||
tabindex="0"
|
|
||||||
class:selected
|
|
||||||
onclick={navigate}
|
|
||||||
onkeyup={handleKey}
|
|
||||||
>
|
|
||||||
{@render children()}
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
li {
|
|
||||||
.selectable();
|
|
||||||
|
|
||||||
@separator: 1px solid @color-dark;
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
background-color: darken(@color-light, 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus,
|
|
||||||
&:hover {
|
|
||||||
background-color: darken(@color-light, 10%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(:last-of-type) {
|
|
||||||
border-bottom: @separator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: @sizes[lg]) {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
&:not(:last-of-type) {
|
|
||||||
border-bottom: none;
|
|
||||||
border-right: @separator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,34 +0,0 @@
|
|||||||
<!--
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
interface Props {
|
|
||||||
navOpen: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { navOpen = $bindable() }: Props = $props();
|
|
||||||
|
|
||||||
// if nav is open, we should "hide" it on press. if not, we should "show" it.
|
|
||||||
let action: string = $derived(navOpen ? 'hide' : 'show');
|
|
||||||
|
|
||||||
function toggle() {
|
|
||||||
navOpen = !navOpen;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<button onclick={toggle}>{action} navigation</button>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
button {
|
|
||||||
background-color: lighten(@color-dark, 10%);
|
|
||||||
border: none;
|
|
||||||
color: @color-light;
|
|
||||||
font-size: @font-s-md;
|
|
||||||
font-weight: @font-w-semibold;
|
|
||||||
padding: @padding-sm;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,56 +0,0 @@
|
|||||||
<!--
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { type Snippet } from 'svelte';
|
|
||||||
import { slide } from 'svelte/transition';
|
|
||||||
|
|
||||||
import NavToggle from './nav-toggle.svelte';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
mobile?: boolean;
|
|
||||||
children: Snippet;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { mobile = true, children }: Props = $props();
|
|
||||||
|
|
||||||
let open: boolean = $state(false);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
{#if mobile}
|
|
||||||
<NavToggle bind:navOpen={open} />
|
|
||||||
{/if}
|
|
||||||
{#if open || !mobile}
|
|
||||||
<ul role="tablist" transition:slide={{ duration: 300 }}>
|
|
||||||
{@render children()}
|
|
||||||
</ul>
|
|
||||||
{/if}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
nav {
|
|
||||||
grid-area: navtree;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
@border: 2px solid @color-dark;
|
|
||||||
|
|
||||||
border-bottom: @border;
|
|
||||||
box-sizing: border-box;
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
@media screen and (min-width: @sizes[lg]) {
|
|
||||||
align-items: stretch;
|
|
||||||
border-right: @border;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,102 +0,0 @@
|
|||||||
<!--
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import 'iconify-icon';
|
|
||||||
|
|
||||||
import { pane } from '$lib/stores/nav.js';
|
|
||||||
import renderPreview from '$lib/render-preview';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
mobile?: boolean;
|
|
||||||
markdown: string;
|
|
||||||
stylesheet: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { mobile = true, markdown, stylesheet }: Props = $props();
|
|
||||||
|
|
||||||
let hidden: boolean = $derived(mobile ? $pane !== 'preview' : true);
|
|
||||||
|
|
||||||
let output: Promise<string> = $derived(renderPreview(markdown, stylesheet));
|
|
||||||
|
|
||||||
let previewFrame = $state<HTMLIFrameElement | undefined>();
|
|
||||||
|
|
||||||
async function performRender(event: MouseEvent) {
|
|
||||||
event.preventDefault();
|
|
||||||
previewFrame?.contentWindow?.postMessage('print');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<main class:hidden class:mobile data-testid="preview-pane">
|
|
||||||
{#await output}
|
|
||||||
<p>processing...</p>
|
|
||||||
{:then result}
|
|
||||||
<iframe title="résumé preview" srcdoc={result} bind:this={previewFrame}></iframe>
|
|
||||||
<button type="submit" onclick={performRender}>
|
|
||||||
<span>download</span>
|
|
||||||
<iconify-icon icon="ion:download-outline" height="1.25em"></iconify-icon>
|
|
||||||
</button>
|
|
||||||
{:catch err}
|
|
||||||
<p style:color="red">error: {err}!</p>
|
|
||||||
{/await}
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
main {
|
|
||||||
.container();
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
gap: @padding-lg-y;
|
|
||||||
grid-area: preview;
|
|
||||||
|
|
||||||
button {
|
|
||||||
.selectable();
|
|
||||||
|
|
||||||
background-color: lighten(@color-dark, 10%);
|
|
||||||
border: 1px solid @color-dark;
|
|
||||||
border-radius: @border-r-xl;
|
|
||||||
color: @color-light;
|
|
||||||
font-size: @font-s-sm;
|
|
||||||
font-weight: @font-w-semibold;
|
|
||||||
padding: @padding-md;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
background-color: lighten(@color-dark, 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: lighten(@color-dark, 15%);
|
|
||||||
}
|
|
||||||
|
|
||||||
& > iconify-icon,
|
|
||||||
& > span {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
iconify-icon {
|
|
||||||
height: 1.25em;
|
|
||||||
width: 1.25em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe {
|
|
||||||
.container-content();
|
|
||||||
}
|
|
||||||
|
|
||||||
&.mobile {
|
|
||||||
grid-area: editor;
|
|
||||||
|
|
||||||
&.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: @sizes[lg]) {
|
|
||||||
gap: @padding-xl-y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { type Element as HastElement, type Root as HastRoot } from 'hast';
|
|
||||||
import { h } from 'hastscript';
|
|
||||||
import rehypeDocument from 'rehype-document';
|
|
||||||
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
|
|
||||||
import rehypeStringify from 'rehype-stringify';
|
|
||||||
import remarkParse from 'remark-parse';
|
|
||||||
import remarkRehype from 'remark-rehype';
|
|
||||||
import { unified, type Plugin } from 'unified';
|
|
||||||
|
|
||||||
const printScript = `
|
|
||||||
window.addEventListener('message', (evt) => {
|
|
||||||
if (evt.data === 'print') {
|
|
||||||
window.print();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
`;
|
|
||||||
|
|
||||||
const removePrintMarginsStyle = `
|
|
||||||
@page {
|
|
||||||
margin-bottom: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const injectPrintScript: Plugin<[], HastRoot> = function () {
|
|
||||||
return (tree) => {
|
|
||||||
const printScriptNode = h('script', printScript);
|
|
||||||
const htmlNode = tree.children.find(
|
|
||||||
(node) => node.type === 'element' && node.tagName === 'html',
|
|
||||||
) as HastElement;
|
|
||||||
const bodyNode = htmlNode.children.find(
|
|
||||||
(node) => node.type === 'element' && node.tagName === 'body',
|
|
||||||
) as HastElement;
|
|
||||||
bodyNode.children.push(printScriptNode);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const injectRemovePrintMarginsStyle: Plugin<[], HastRoot> = function () {
|
|
||||||
return (tree) => {
|
|
||||||
const removePrintMarginsStyleNode = h('style', removePrintMarginsStyle);
|
|
||||||
const htmlNode = tree.children.find(
|
|
||||||
(node) => node.type === 'element' && node.tagName === 'html',
|
|
||||||
) as HastElement;
|
|
||||||
const headNode = htmlNode.children.find(
|
|
||||||
(node) => node.type === 'element' && node.tagName === 'head',
|
|
||||||
) as HastElement;
|
|
||||||
headNode.children.push(removePrintMarginsStyleNode);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderPreview = async (markdown: string, stylesheet: string): Promise<string> => {
|
|
||||||
let allowedTags: string[] = ['body', 'head', 'html', 'style'];
|
|
||||||
if (defaultSchema.tagNames) {
|
|
||||||
allowedTags = [...defaultSchema.tagNames, ...allowedTags];
|
|
||||||
}
|
|
||||||
return String(
|
|
||||||
await unified()
|
|
||||||
.use(remarkParse)
|
|
||||||
.use(remarkRehype)
|
|
||||||
.use(rehypeDocument, { style: stylesheet })
|
|
||||||
.use(rehypeSanitize, {
|
|
||||||
...defaultSchema,
|
|
||||||
allowDoctypes: true,
|
|
||||||
tagNames: allowedTags,
|
|
||||||
})
|
|
||||||
.use(injectRemovePrintMarginsStyle)
|
|
||||||
.use(injectPrintScript)
|
|
||||||
.use(rehypeStringify)
|
|
||||||
.process(markdown),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default renderPreview;
|
|
@ -1,11 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { writable } from 'svelte/store';
|
|
||||||
|
|
||||||
export type Pane = 'content' | 'style' | 'preview';
|
|
||||||
|
|
||||||
export const pane = writable<Pane>('content');
|
|
@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { type RequestEvent } from '@sveltejs/kit';
|
|
||||||
|
|
||||||
import { UAParser } from 'ua-parser-js';
|
|
||||||
|
|
||||||
export async function load({ request }: RequestEvent) {
|
|
||||||
const ua = request.headers.get('user-agent');
|
|
||||||
|
|
||||||
if (!ua) {
|
|
||||||
return { mobile: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
const { device } = UAParser(ua);
|
|
||||||
return { mobile: device.type === 'mobile' };
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
<!--
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { type Snippet } from 'svelte';
|
|
||||||
|
|
||||||
import '../app.less';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
children: Snippet;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { children }: Props = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{@render children()}
|
|
@ -1,143 +0,0 @@
|
|||||||
<!--
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
import type { PageData } from './$types';
|
|
||||||
|
|
||||||
import { pane } from '$lib/stores/nav.js';
|
|
||||||
|
|
||||||
import CodeInput from '$lib/components/code-input.svelte';
|
|
||||||
import Editor from '$lib/components/editor.svelte';
|
|
||||||
import Headline from '$lib/components/headline.svelte';
|
|
||||||
import NavItem from '$lib/components/nav-item.svelte';
|
|
||||||
import NavTree from '$lib/components/nav-tree.svelte';
|
|
||||||
import Preview from '$lib/components/preview.svelte';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
data: PageData;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { data }: Props = $props();
|
|
||||||
|
|
||||||
let mobile: boolean = $state(data.mobile);
|
|
||||||
|
|
||||||
function checkIsDesktop() {
|
|
||||||
// TODO: figure out how to remove hard-coded value here
|
|
||||||
mobile = !window.matchMedia('screen and (min-width: 1280px)').matches;
|
|
||||||
|
|
||||||
if (!mobile && get(pane) === 'preview') {
|
|
||||||
pane.set('content');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
checkIsDesktop();
|
|
||||||
|
|
||||||
window.addEventListener('resize', checkIsDesktop);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('resize', checkIsDesktop);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
let markdown: string = $state('');
|
|
||||||
let stylesheet: string = $state('');
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>resumarkdown</title>
|
|
||||||
</svelte:head>
|
|
||||||
<div>
|
|
||||||
<Headline>resumarkdown</Headline>
|
|
||||||
<NavTree {mobile}>
|
|
||||||
<NavItem destination="content">content</NavItem>
|
|
||||||
<NavItem destination="style">style</NavItem>
|
|
||||||
{#if mobile}
|
|
||||||
<NavItem destination="preview">preview</NavItem>
|
|
||||||
{/if}
|
|
||||||
</NavTree>
|
|
||||||
<Editor pane="content" testid="content-pane">
|
|
||||||
<CodeInput bind:code={markdown} />
|
|
||||||
</Editor>
|
|
||||||
<Editor pane="style" testid="style-pane">
|
|
||||||
<CodeInput bind:code={stylesheet} />
|
|
||||||
</Editor>
|
|
||||||
<Preview {mobile} {markdown} {stylesheet} />
|
|
||||||
<footer>
|
|
||||||
<p>
|
|
||||||
made with ❤ by
|
|
||||||
<a href="https://github.com/tweakdeveloper" title="my github profile">@tweakdeveloper</a>
|
|
||||||
</p>
|
|
||||||
<a href="/attributions">attributions</a>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
div {
|
|
||||||
align-content: start;
|
|
||||||
display: grid;
|
|
||||||
grid-template-areas:
|
|
||||||
'headline'
|
|
||||||
'navtree'
|
|
||||||
'editor'
|
|
||||||
'footer';
|
|
||||||
grid-template-rows: min-content min-content 1fr;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
@media screen and (min-width: @sizes[lg]) {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
grid-template-areas:
|
|
||||||
'headline headline'
|
|
||||||
'navtree preview'
|
|
||||||
'editor preview'
|
|
||||||
'footer footer';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
align-items: center;
|
|
||||||
background-color: @color-dark;
|
|
||||||
border-top: 2px solid @color-dark;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: @color-light;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
gap: @padding-md-y;
|
|
||||||
grid-area: footer;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: @padding-lg;
|
|
||||||
|
|
||||||
@media screen and (min-width: @sizes[lg]) {
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
gap: @padding-sm-y;
|
|
||||||
padding: @padding-md;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
footer a {
|
|
||||||
color: @color-light;
|
|
||||||
transition: color 0.2s;
|
|
||||||
|
|
||||||
&:visited {
|
|
||||||
color: darken(@color-light, 30%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: darken(@color-light, 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
color: darken(@color-light, 10%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
footer p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,6 +0,0 @@
|
|||||||
<!--
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
-->
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// colors
|
|
||||||
@color-accent-dark: #a8c3b5;
|
|
||||||
@color-accent-light: #558b6e;
|
|
||||||
@color-danger: #d64933;
|
|
||||||
@color-dark: #2e282a;
|
|
||||||
@color-light: #fbfbfb;
|
|
||||||
|
|
||||||
// fonts
|
|
||||||
@fonts-sans: Rubik, Arial, sans-serif;
|
|
||||||
@font-s-lg: 2rem;
|
|
||||||
@font-s-md: 1.25rem;
|
|
||||||
@font-s-sm: 1rem;
|
|
||||||
@font-w-bold: 700;
|
|
||||||
@font-w-semibold: 600;
|
|
||||||
|
|
||||||
// layout
|
|
||||||
@border-r-xl: 0.75em;
|
|
||||||
@padding-md: @padding-md-y @padding-md-x;
|
|
||||||
@padding-md-x: 1em;
|
|
||||||
@padding-md-y: 0.5em;
|
|
||||||
@padding-lg: @padding-lg-y @padding-lg-x;
|
|
||||||
@padding-lg-x: 1.5em;
|
|
||||||
@padding-lg-y: 1em;
|
|
||||||
@padding-sm: @padding-sm-y @padding-sm-x;
|
|
||||||
@padding-sm-x: 0.875em;
|
|
||||||
@padding-sm-y: 0.375em;
|
|
||||||
@padding-xl: @padding-xl-y @padding-xl-x;
|
|
||||||
@padding-xl-x: 2em;
|
|
||||||
@padding-xl-y: 1.5em;
|
|
||||||
|
|
||||||
// sizes
|
|
||||||
@sizes: {
|
|
||||||
lg: 1280px;
|
|
||||||
};
|
|
||||||
|
|
||||||
// utils
|
|
||||||
.full-without-padding(@padding) {
|
|
||||||
@result: calc(100% - @padding * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// shared sizing mixins
|
|
||||||
.container() {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: @padding-lg;
|
|
||||||
|
|
||||||
@media screen and (min-width: @sizes[lg]) {
|
|
||||||
padding: @padding-xl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-content() {
|
|
||||||
border: 1px solid @color-dark;
|
|
||||||
border-radius: @border-r-xl;
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-size: @font-s-md;
|
|
||||||
height: 100%;
|
|
||||||
padding: @padding-lg;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
@media screen and (min-width: @sizes[lg]) {
|
|
||||||
font-size: unset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// shared interactivity mixins
|
|
||||||
.selectable() {
|
|
||||||
font-weight: @font-w-semibold;
|
|
||||||
margin: 0;
|
|
||||||
padding: @padding-md;
|
|
||||||
text-align: center;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import adapter from '@sveltejs/adapter-node';
|
|
||||||
|
|
||||||
import { sveltePreprocess } from 'svelte-preprocess';
|
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
|
||||||
const config = {
|
|
||||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
|
||||||
// for more information about preprocessors
|
|
||||||
preprocess: sveltePreprocess({
|
|
||||||
less: {
|
|
||||||
prependData: `@import 'src/styles.less';`,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
kit: {
|
|
||||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
|
||||||
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
|
||||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
|
||||||
adapter: adapter(),
|
|
||||||
},
|
|
||||||
|
|
||||||
compilerOptions: {
|
|
||||||
runes: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { expect, test } from '@playwright/test';
|
|
||||||
|
|
||||||
test('desktop page has nav tree', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await expect(page.getByRole('navigation')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('desktop page does not have nav toggle', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await expect(page.getByRole('navigation').getByRole('button')).toBeHidden();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('desktop page has two-column layout', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await expect(page.getByTestId('content-pane')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('preview-pane')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('desktop page has no "preview" nav item', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await expect(page.getByRole('tab').filter({ hasText: 'preview' })).toBeHidden();
|
|
||||||
});
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { expect, test } from '@playwright/test';
|
|
||||||
|
|
||||||
test('page has headline', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await expect(page.getByRole('banner')).toHaveText('resumarkdown');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('nav items work', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
|
|
||||||
for (const tab of await page.getByRole('tab').all()) {
|
|
||||||
await tab.click();
|
|
||||||
await expect(page.getByTestId(`${await tab.textContent()}-pane`)).toBeVisible();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('preview accepts content', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await page.getByText('content').click();
|
|
||||||
await page.getByRole('textbox').fill('# résumé');
|
|
||||||
|
|
||||||
const previewFrame = page.getByTitle('résumé preview').contentFrame();
|
|
||||||
await expect(previewFrame.getByRole('heading')).toHaveText('résumé');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('preview accepts styles', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await page.getByText('content').click();
|
|
||||||
await page.getByRole('textbox').fill('# blah');
|
|
||||||
await page.getByText('style', { exact: true }).click();
|
|
||||||
await page.getByRole('textbox').fill('h1 { color: red; }');
|
|
||||||
|
|
||||||
const previewFrame = page.getByTitle('résumé preview').contentFrame();
|
|
||||||
await expect(previewFrame.getByRole('heading')).toHaveCSS('color', 'rgb(255, 0, 0)');
|
|
||||||
});
|
|
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { expect, test } from '@playwright/test';
|
|
||||||
|
|
||||||
import UserAgent from 'user-agents';
|
|
||||||
|
|
||||||
test.use({
|
|
||||||
viewport: { height: 2556, width: 1179 },
|
|
||||||
userAgent: new UserAgent({ deviceCategory: 'mobile' }).toString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
test('mobile page has nav tree hidden by default', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await expect(page.getByRole('navigation').getByRole('tablist')).toBeHidden();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('mobile page has nav toggle', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await expect(page.getByRole('navigation').getByRole('button')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('nav toggle works', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await page.getByText('show navigation').click();
|
|
||||||
await expect(page.getByRole('navigation').getByRole('tablist')).toBeVisible();
|
|
||||||
await page.getByText('hide navigation').click();
|
|
||||||
await expect(page.getByRole('navigation').getByRole('tablist')).toBeHidden();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('mobile page has single-column layout', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await expect(page.getByTestId('content-pane')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('preview-pane')).toBeHidden();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('mobile page has preview nav item', async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await page.getByText('show navigation').click();
|
|
||||||
await expect(page.getByRole('tab').filter({ hasText: 'preview' })).toBeVisible();
|
|
||||||
});
|
|
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./.svelte-kit/tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"allowJs": true,
|
|
||||||
"checkJs": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"strict": true,
|
|
||||||
"moduleResolution": "bundler"
|
|
||||||
}
|
|
||||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
|
||||||
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
|
|
||||||
//
|
|
||||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
|
||||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
/*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { sveltekit } from '@sveltejs/kit/vite';
|
|
||||||
import { defineConfig } from 'vitest/config';
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [sveltekit()],
|
|
||||||
test: {
|
|
||||||
include: ['src/**/*.{test,spec}.{js,ts}'],
|
|
||||||
},
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user