hackatime-heatmap 0.3.1

Easy to set up Hackatime coding activity heatmap for your profile!
<script lang="ts">
	import Prism from 'prismjs';

	import 'prismjs/components/prism-markup.js';
	import 'prismjs/themes/prism-tomorrow.css';

	interface Props {
		code: string;
		language?: string;
		readonly?: boolean;
		class?: string;
	}

	let { code, language = 'html', readonly = true, class: className = '' }: Props = $props();
	let codeElement: HTMLElement;
	let isCopied = $state(false);

	let highlightedCode = $derived.by(() => {
		return Prism.highlight(code, Prism.languages[language] || Prism.languages.html, language);
	});

	function handleClick() {
		if (readonly) {
			const selection = window.getSelection();
			const range = document.createRange();
			range.selectNodeContents(codeElement);
			selection?.removeAllRanges();
			selection?.addRange(range);
		}
	}

	function copyToClipboard() {
		if (code) {
			isCopied = true;
			navigator.clipboard
				.writeText(code)
				.then(() => {
					console.log('Code copied to clipboard');
					setTimeout(() => {
						isCopied = false;
					}, 1500);
				})
				.catch((err) => {
					console.error('Failed to copy code: ', err);
					isCopied = false;
				});
		}
	}
</script>

<div class="relative {className}">
	<button
		onclick={copyToClipboard}
		class="absolute top-3 right-3 z-10 cursor-pointer rounded-lg border border-overlay0/50 p-2 shadow-sm backdrop-blur-sm transition-all duration-200 hover:scale-110 active:scale-95 {isCopied
			? 'bg-green/80 hover:bg-green'
			: 'bg-surface1/80 hover:bg-surface2'}"
		style="color: {isCopied
			? 'var(--color-mantle)'
			: 'var(--color-text)'}; transition: background-color 200ms ease-in-out, color 200ms ease-in-out;"
		title="Copy to clipboard"
	>
		{#if isCopied}
			<svg
				xmlns="http://www.w3.org/2000/svg"
				width="16"
				height="16"
				viewBox="0 0 24 24"
				class="transition-all duration-200"
			>
				<!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE -->
				<path
					fill="currentColor"
					d="m9.55 15.15l8.475-8.475q.3-.3.7-.3t.7.3t.3.713t-.3.712l-9.175 9.2q-.3.3-.7.3t-.7-.3L4.55 13q-.3-.3-.288-.712t.313-.713t.713-.3t.712.3z"
				/>
			</svg>
		{:else}
			<svg
				xmlns="http://www.w3.org/2000/svg"
				width="16"
				height="16"
				viewBox="0 0 24 24"
				class="transition-all duration-200"
			>
				<!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE -->
				<path
					fill="currentColor"
					d="M9 18q-.825 0-1.412-.587T7 16V4q0-.825.588-1.412T9 2h9q.825 0 1.413.588T20 4v12q0 .825-.587 1.413T18 18zm-4 4q-.825 0-1.412-.587T3 20V7q0-.425.288-.712T4 6t.713.288T5 7v13h10q.425 0 .713.288T16 21t-.288.713T15 22z"
				/>
			</svg>
		{/if}
	</button>
	<pre
		class="overflow-x-auto rounded-md border border-overlay0 !bg-surface0/60 px-3 py-2 transition-all duration-300 ease-in-out dark:border-overlay0"><code
			bind:this={codeElement}
			class="language-{language} !bg-transparent text-sm text-text dark:text-text"
			onclick={handleClick}
			aria-label="Code block"
			role={readonly ? 'textbox' : undefined}
			style="cursor: {readonly ? 'pointer' : 'default'}">{@html highlightedCode}</code
		></pre>
</div>

<style>
	:global(.token.tag) {
		color: var(--color-blue) !important;
	}

	:global(.token.attr-name) {
		color: var(--color-yellow) !important;
	}

	:global(.token.attr-value) {
		color: var(--color-green) !important;
	}

	:global(.token.string) {
		color: var(--color-green) !important;
	}

	:global(.token.punctuation) {
		color: var(--color-text) !important;
	}

	:global(.token.comment) {
		color: var(--color-overlay2) !important;
		font-style: italic;
	}

	:global(.token.keyword) {
		color: var(--color-mauve) !important;
	}

	:global(.token.operator) {
		color: var(--color-sky) !important;
	}

	:global(.token.number) {
		color: var(--color-peach) !important;
	}

	:global(.token.boolean) {
		color: var(--color-peach) !important;
	}

	:global(.token.function) {
		color: var(--color-blue) !important;
	}

	:global(.token.class-name) {
		color: var(--color-yellow) !important;
	}

	:global(.token.selector) {
		color: var(--color-red) !important;
	}

	:global(.token.property) {
		color: var(--color-blue) !important;
	}

	:global(.token.important) {
		color: var(--color-red) !important;
		font-weight: bold;
	}
</style>