[READ-ONLY] Mirror of https://github.com/probablykasper/cpc. Text calculator with support for units and conversion cpc.kasper.space
calculator cli conversion converter library math package units units-converter wasm website
0

Configure Feed

Select the types of activity you want to include in your feed.

Format

Kasper (Jun 20, 2026, 2:38 AM +0200) f802ff52 f765bca8

+146 -123
+1 -1
web/src/app.css
··· 1 - @import 'tailwindcss'; 1 + @import "tailwindcss";
+17 -4
web/src/app.html
··· 4 4 <meta charset="utf-8" /> 5 5 <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 6 7 - <link rel="icon" type="image/png" href="%sveltekit.assets%/favicon-96x96.png" sizes="96x96" /> 8 - <link rel="icon" type="image/svg+xml" href="%sveltekit.assets%/favicon.svg" /> 7 + <link 8 + rel="icon" 9 + type="image/png" 10 + href="%sveltekit.assets%/favicon-96x96.png" 11 + sizes="96x96" 12 + /> 13 + <link 14 + rel="icon" 15 + type="image/svg+xml" 16 + href="%sveltekit.assets%/favicon.svg" 17 + /> 9 18 <link rel="shortcut icon" href="%sveltekit.assets%/favicon.ico" /> 10 - <link rel="apple-touch-icon" sizes="180x180" href="%sveltekit.assets%/apple-touch-icon.png" /> 19 + <link 20 + rel="apple-touch-icon" 21 + sizes="180x180" 22 + href="%sveltekit.assets%/apple-touch-icon.png" 23 + /> 11 24 <meta name="apple-mobile-web-app-title" content="cpc" /> 12 25 <link rel="manifest" href="%sveltekit.assets%/site.webmanifest" /> 13 - 26 + 14 27 %sveltekit.head% 15 28 </head> 16 29 <body data-sveltekit-preload-data="hover">
+25 -15
web/src/lib/helpers.ts
··· 1 1 type ShortcutOptions = { 2 - shift?: boolean 3 - alt?: boolean 4 - cmd_or_ctrl?: boolean 5 - } 6 - const is_mac = navigator.userAgent.indexOf('Mac') !== -1 2 + shift?: boolean; 3 + alt?: boolean; 4 + cmd_or_ctrl?: boolean; 5 + }; 6 + const is_mac = navigator.userAgent.indexOf("Mac") !== -1; 7 7 8 - function check_modifiers(e: KeyboardEvent | MouseEvent, options: ShortcutOptions) { 8 + function check_modifiers( 9 + e: KeyboardEvent | MouseEvent, 10 + options: ShortcutOptions, 11 + ) { 9 12 const target = { 10 13 shift: options.shift || false, 11 14 alt: options.alt || false, 12 15 ctrl: (!is_mac && options.cmd_or_ctrl) || false, 13 16 meta: (is_mac && options.cmd_or_ctrl) || false, 14 - } 17 + }; 15 18 16 19 const pressed = { 17 20 shift: !!e.shiftKey, 18 21 alt: !!e.altKey, 19 22 ctrl: !!e.ctrlKey, 20 23 meta: !!e.metaKey, 21 - } 24 + }; 22 25 23 - const ignore_ctrl = is_mac && e instanceof MouseEvent 26 + const ignore_ctrl = is_mac && e instanceof MouseEvent; 24 27 25 28 return ( 26 29 pressed.shift === target.shift && 27 30 pressed.alt === target.alt && 28 31 (pressed.ctrl === target.ctrl || ignore_ctrl) && 29 32 pressed.meta === target.meta 30 - ) 33 + ); 31 34 } 32 35 33 - export function check_shortcut(e: KeyboardEvent, key: string, options: ShortcutOptions = {}) { 34 - if (e.key.toUpperCase() !== key.toUpperCase()) return false 35 - return check_modifiers(e, options) 36 + export function check_shortcut( 37 + e: KeyboardEvent, 38 + key: string, 39 + options: ShortcutOptions = {}, 40 + ) { 41 + if (e.key.toUpperCase() !== key.toUpperCase()) return false; 42 + return check_modifiers(e, options); 36 43 } 37 - export function check_mouse_shortcut(e: MouseEvent, options: ShortcutOptions = {}) { 38 - return check_modifiers(e, options) 44 + export function check_mouse_shortcut( 45 + e: MouseEvent, 46 + options: ShortcutOptions = {}, 47 + ) { 48 + return check_modifiers(e, options); 39 49 }
+1 -1
web/src/routes/+layout.svelte
··· 1 1 <script lang="ts"> 2 - import "../app.css"; 2 + import "../app.css"; 3 3 </script> 4 4 5 5 <slot />
+97 -97
web/src/routes/+page.svelte
··· 1 1 <script lang="ts"> 2 - import { check_shortcut } from "$lib/helpers"; 3 - import { flip } from "svelte/animate"; 4 - import { fly } from "svelte/transition"; 5 - // Has to be dynamically imported for prerendering to work 6 - // https://github.com/sveltejs/svelte/issues/13155 7 - const cpc_promise = import("cpc"); 8 - let cpc: typeof import("cpc") | undefined; 9 - cpc_promise.then((mod) => { 10 - cpc = mod; 11 - }); 2 + import { check_shortcut } from "$lib/helpers"; 3 + import { flip } from "svelte/animate"; 4 + import { fly } from "svelte/transition"; 5 + // Has to be dynamically imported for prerendering to work 6 + // https://github.com/sveltejs/svelte/issues/13155 7 + const cpc_promise = import("cpc"); 8 + let cpc: typeof import("cpc") | undefined; 9 + cpc_promise.then((mod) => { 10 + cpc = mod; 11 + }); 12 12 13 - function wasm_eval(input: string) { 14 - if (!cpc || input.trim().length === 0) { 15 - return ""; 16 - } 17 - try { 18 - return cpc.wasm_eval(input); 19 - } catch (e) { 20 - return ""; 21 - } 22 - } 13 + function wasm_eval(input: string) { 14 + if (!cpc || input.trim().length === 0) { 15 + return ""; 16 + } 17 + try { 18 + return cpc.wasm_eval(input); 19 + } catch (e) { 20 + return ""; 21 + } 22 + } 23 23 24 - let input = $state(""); 25 - let output = $derived(wasm_eval(input)); 26 - let saved_queries: { id: number; in: string; out: string }[] = $state([]); 24 + let input = $state(""); 25 + let output = $derived(wasm_eval(input)); 26 + let saved_queries: { id: number; in: string; out: string }[] = $state([]); 27 27 </script> 28 28 29 29 <svelte:head> 30 - <title>cpc</title> 31 - <meta 32 - name="description" 33 - content="Text calculator with support for units and conversion" 34 - /> 30 + <title>cpc</title> 31 + <meta 32 + name="description" 33 + content="Text calculator with support for units and conversion" 34 + /> 35 35 </svelte:head> 36 36 37 37 <main class="w-full px-4 lg:px-8 text-base lg:text-lg"> 38 - <nav class="flex items-center justify-between py-4 lg:py-6"> 39 - <h1 class="text-3xl font-bold text-amber-600 dark:text-amber-400">cpc</h1> 40 - <a 41 - href="https://github.com/probablykasper/cpc" 42 - aria-label="GitHub repository" 43 - class="svelte-1ugh5mt" 44 - > 45 - <svg 46 - height="24" 47 - viewBox="-2 -2 28 28" 48 - width="24" 49 - xmlns="http://www.w3.org/2000/svg" 50 - class="svelte-8lfi33 svelte-1ugh5mt" 51 - ><path 52 - d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22" 53 - fill="none" 54 - stroke="currentColor" 55 - stroke-linecap="round" 56 - stroke-linejoin="round" 57 - stroke-width="2" 58 - class="svelte-8lfi33" 59 - ></path></svg 60 - > 61 - </a> 62 - </nav> 63 - <!-- svelte-ignore a11y_autofocus --> 64 - <input 65 - type="text" 66 - class="border border-gray-500/50 w-full rounded-lg px-3 py-2 outline-none" 67 - bind:value={input} 68 - onkeydown={async (e) => { 69 - const input_el = e.currentTarget; 70 - const input = e.currentTarget.value; 71 - if (check_shortcut(e, "Enter")) { 72 - const out = wasm_eval(input) 73 - console.log(out); 74 - saved_queries.unshift({ 75 - id: saved_queries.length, 76 - in: input, 77 - out, 78 - }); 79 - input_el.value = ""; 80 - output = ""; 81 - } 82 - }} 83 - placeholder="10km/h * 1 decade in light seconds" 84 - autofocus 85 - /> 86 - <div class="pt-1 leading-tight"> 87 - <div class="px-3 py-2"> 88 - {output}<span class="invisible select-none">x</span> 89 - </div> 90 - {#each saved_queries as query (query.id)} 91 - <div 92 - class="px-3 py-2" 93 - in:fly={{ y: -10, duration: 150 }} 94 - animate:flip={{ duration: 150 }} 95 - > 96 - <p class="opacity-65 text-base leading-tight">{query.in}</p> 97 - <p>{query.out}</p> 98 - </div> 99 - {/each} 100 - </div> 38 + <nav class="flex items-center justify-between py-4 lg:py-6"> 39 + <h1 class="text-3xl font-bold text-amber-600 dark:text-amber-400">cpc</h1> 40 + <a 41 + href="https://github.com/probablykasper/cpc" 42 + aria-label="GitHub repository" 43 + class="svelte-1ugh5mt" 44 + > 45 + <svg 46 + height="24" 47 + viewBox="-2 -2 28 28" 48 + width="24" 49 + xmlns="http://www.w3.org/2000/svg" 50 + class="svelte-8lfi33 svelte-1ugh5mt" 51 + ><path 52 + d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22" 53 + fill="none" 54 + stroke="currentColor" 55 + stroke-linecap="round" 56 + stroke-linejoin="round" 57 + stroke-width="2" 58 + class="svelte-8lfi33" 59 + ></path></svg 60 + > 61 + </a> 62 + </nav> 63 + <!-- svelte-ignore a11y_autofocus --> 64 + <input 65 + type="text" 66 + class="border border-gray-500/50 w-full rounded-lg px-3 py-2 outline-none" 67 + bind:value={input} 68 + onkeydown={async (e) => { 69 + const input_el = e.currentTarget; 70 + const input = e.currentTarget.value; 71 + if (check_shortcut(e, "Enter")) { 72 + const out = wasm_eval(input); 73 + console.log(out); 74 + saved_queries.unshift({ 75 + id: saved_queries.length, 76 + in: input, 77 + out, 78 + }); 79 + input_el.value = ""; 80 + output = ""; 81 + } 82 + }} 83 + placeholder="10km/h * 1 decade in light seconds" 84 + autofocus 85 + /> 86 + <div class="pt-1 leading-tight"> 87 + <div class="px-3 py-2"> 88 + {output}<span class="invisible select-none">x</span> 89 + </div> 90 + {#each saved_queries as query (query.id)} 91 + <div 92 + class="px-3 py-2" 93 + in:fly={{ y: -10, duration: 150 }} 94 + animate:flip={{ duration: 150 }} 95 + > 96 + <p class="opacity-65 text-base leading-tight">{query.in}</p> 97 + <p>{query.out}</p> 98 + </div> 99 + {/each} 100 + </div> 101 101 </main> 102 102 103 103 <style lang="postcss"> 104 - @reference "../app.css"; 104 + @reference "../app.css"; 105 105 106 - @media (prefers-color-scheme: dark) { 107 - :global(body) { 108 - @apply bg-black text-white; 109 - } 110 - } 106 + @media (prefers-color-scheme: dark) { 107 + :global(body) { 108 + @apply bg-black text-white; 109 + } 110 + } 111 111 </style>
+3 -3
web/svelte.config.js
··· 1 - import adapter from '@sveltejs/adapter-vercel'; 2 - import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 1 + import adapter from "@sveltejs/adapter-vercel"; 2 + import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; 3 3 4 4 const config = { 5 5 preprocess: vitePreprocess(), 6 - kit: { adapter: adapter() } 6 + kit: { adapter: adapter() }, 7 7 }; 8 8 9 9 export default config;
+2 -2
web/tsconfig.json
··· 9 9 "skipLibCheck": true, 10 10 "sourceMap": true, 11 11 "strict": true, 12 - "moduleResolution": "bundler" 13 - } 12 + "moduleResolution": "bundler", 13 + }, 14 14 // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias 15 15 // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files 16 16 //