diff --git a/src/lib/components/CodeBlock/CodeBlock.svelte b/src/lib/components/CodeBlock/CodeBlock.svelte
index 18018c19dd1d99dfa8cc607b39456ec8fcd0bc2f..bb93421639b8bc0ba8fdd48b366ab0dc1c30be85 100644
--- a/src/lib/components/CodeBlock/CodeBlock.svelte
+++ b/src/lib/components/CodeBlock/CodeBlock.svelte
@@ -2,31 +2,33 @@
 	import { createHighlighterCoreSync } from 'shiki/core';
 	import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';
 	// Themes
-	// https://shiki.style/themes
 	import themeDarkPlus from 'shiki/themes/dark-plus.mjs';
 	// Languages
-	// https://shiki.style/languages
-	import console from 'shiki/langs/console.mjs';
 	import html from 'shiki/langs/html.mjs';
 	import css from 'shiki/langs/css.mjs';
 	import js from 'shiki/langs/javascript.mjs';
-	// https://shiki.style/guide/sync-usage
+
 	const shiki = createHighlighterCoreSync({
 		engine: createJavaScriptRegexEngine(),
 		themes: [themeDarkPlus],
-		langs: [console, html, css, js]
+		langs: [html, css, js]
 	});
 </script>
 
 <script lang="ts">
-	import type { CodeBlockProps, SelectionInfo } from './types';
-	import { addTokenIds } from './transformers';
+	import type {
+		CodeBlockProps,
+		SelectionInfo,
+		EnhancedSelectionInfo,
+		SourceSelection
+	} from './types';
+	import { sourceMappingTransformer, domSelectionToSourceSelection } from './transformers';
 	import { onMount, onDestroy } from 'svelte';
 	import { browser } from '$app/environment';
 
 	let {
 		code = '',
-		lang = 'console',
+		lang = 'js',
 		theme = 'dark-plus',
 		// Base Style Props
 		base = 'overflow-hidden',
@@ -37,35 +39,45 @@
 		preBase = '',
 		prePadding = '[&>pre]:p-4',
 		preClasses = '',
-		// Selection props
-		onSelectionChange = (selInfo: Selection) => {},
-		// Debug view
+
+		onSelectionChange = (_) => {},
 		showDebug = false
 	}: CodeBlockProps = $props();
 
-	// Shiki convert to HTML
 	const generatedHtml = shiki.codeToHtml(code, {
 		lang,
 		theme,
-		transformers: [addTokenIds]
+		transformers: [sourceMappingTransformer],
+		structure: 'classic'
 	});
 
-	// Selection state
-	let currentSelection: Selection| null  = $state(null);
+	let currentSelection: SelectionInfo | null = $state(null);
+	let sourceSelection: SourceSelection | null = $state(null);
+
 	$inspect(currentSelection);
+	$inspect(sourceSelection);
 
 	// Code block element reference
 	let codeBlockEl: HTMLElement;
 
 	function updateSelectionInfo() {
-		const sel= window.getSelection();
-		if (!sel|| !isSelectionInCodeBlock(sel)) { currentSelection = null; return; }
+		const sel = window.getSelection();
+
+		if (!sel || !isSelectionInCodeBlock(sel)) {
+			// currentSelection = null;
+			// sourceSelection = null;
+			return;
+		}
 
-		currentSelection = sel;
-		onSelectionChange(currentSelection);
+		currentSelection = getSelectionInfo(sel);
+
+		sourceSelection = domSelectionToSourceSelection(sel, codeBlockEl);
+
+		if (sourceSelection) {
+			onSelectionChange({ ...currentSelection, source: sourceSelection });
+		}
 	}
 
-	// Helper to check if selection is within our code block
 	function isSelectionInCodeBlock(selection: Selection): boolean {
 		if (!selection.rangeCount) return false;
 
@@ -73,9 +85,30 @@
 		return codeBlockEl.contains(range.commonAncestorContainer);
 	}
 
-	// Helper to describe a DOM node in a readable way
-	function describeNode(node: Node): string {
-		if (node.nodeType === Node.TEXT_NODE) {
+	function getSelectionInfo(sel: Selection): SelectionInfo {
+		const info: Record<string, any> = {};
+
+		function addPropertiesFrom(obj: object) {
+			Object.getOwnPropertyNames(obj).forEach((prop) => {
+				try {
+					const value = (sel as any)[prop];
+					if (typeof value !== 'function') {
+						info[prop] = value;
+					}
+				} catch (e) {}
+			});
+		}
+
+		addPropertiesFrom(sel);
+		addPropertiesFrom(Object.getPrototypeOf(sel));
+
+		return info as SelectionInfo;
+	}
+
+	function describeNode(node: Node | null): string {
+		if (!node) {
+			return 'NULL';
+		} else if (node.nodeType === Node.TEXT_NODE) {
 			return `TEXT: "${node.textContent?.slice(0, 20)}${node.textContent && node.textContent.length > 20 ? '...' : ''}"`;
 		} else if (node.nodeType === Node.ELEMENT_NODE) {
 			const el = node as Element;
@@ -86,19 +119,12 @@
 	}
 
 	onMount(() => {
-		document.addEventListener("selectionchange", updateSelectionInfo);
-		codeBlockEl.addEventListener('mouseup', updateSelectionInfo);
-		codeBlockEl.addEventListener('keyup', updateSelectionInfo);
+		document.addEventListener('selectionchange', updateSelectionInfo);
 	});
 
 	onDestroy(() => {
 		if (!browser) return;
-
 		document.removeEventListener('selectionchange', updateSelectionInfo);
-		if (codeBlockEl) {
-			codeBlockEl.removeEventListener('mouseup', updateSelectionInfo);
-			codeBlockEl.removeEventListener('keyup', updateSelectionInfo);
-		}
 	});
 </script>
 
@@ -106,23 +132,14 @@
 	bind:this={codeBlockEl}
 	class="{base} {rounded} {shadow} {classes} {preBase} {prePadding} {preClasses} code-block-selectable"
 >
-	<!-- Output Shiki's Generated HTML -->
 	{@html generatedHtml}
 </div>
 
-<!-- Selection Debug View -->
 {#if showDebug}
 	<div class="selection-debug">
 		<h3>Selection Debug</h3>
-		<pre>{currentSelection? currentSelection.toString() : "No selection" }</pre>
 		{#if currentSelection !== null && !currentSelection.isCollapsed}
 			<div class="selection-info">
-				<!--
-				<div class="info-row">
-					<span class="label">Selected Text:</span>
-					<span class="value text-value">{currentSelection.text}</span>
-				</div>
-				-->
 				<div class="info-row">
 					<span class="label">Type:</span>
 					<span class="value">{currentSelection.type}</span>
@@ -141,7 +158,7 @@
 				</div>
 				<div class="info-row">
 					<span class="label">Anchor Node:</span>
-					<span class="value">{currentSelection.anchorNode}</span>
+					<span class="value">{describeNode(currentSelection.anchorNode)}</span>
 				</div>
 				<div class="info-row">
 					<span class="label">Anchor Offset:</span>
@@ -149,22 +166,32 @@
 				</div>
 				<div class="info-row">
 					<span class="label">Focus Node:</span>
-					<span class="value">{currentSelection.focusNode}</span>
+					<span class="value">{describeNode(currentSelection.focusNode)}</span>
 				</div>
 				<div class="info-row">
 					<span class="label">Focus Offset:</span>
 					<span class="value">{currentSelection.focusOffset}</span>
 				</div>
-				<!--
-				<div class="info-row">
-					<span class="label">Position:</span>
-					<span class="value">
-						Start: Line {currentSelection.position.start.line}, Column {currentSelection.position.start
-							.column} | End: Line {currentSelection.position.end.line}, Column {currentSelection.position
-							.end.column}
-					</span>
-				</div>
-				-->
+
+				{#if sourceSelection}
+					<hr />
+					<div class="info-row">
+						<span class="label">Source Start:</span>
+						<span class="value"
+							>Line {sourceSelection.start.line}, Col {sourceSelection.start.column}</span
+						>
+					</div>
+					<div class="info-row">
+						<span class="label">Source End:</span>
+						<span class="value"
+							>Line {sourceSelection.end.line}, Col {sourceSelection.end.column}</span
+						>
+					</div>
+					<div class="info-row">
+						<span class="label">Selected Text:</span>
+						<span class="value text-value">{sourceSelection.text}</span>
+					</div>
+				{/if}
 			</div>
 		{:else}
 			<p>No selection active. Click and drag to select text in the code block.</p>
diff --git a/src/lib/components/CodeBlock/transformers.ts b/src/lib/components/CodeBlock/transformers.ts
index 15c3e8fde184dafc9ea371dd5bff585cd91f04ff..aef87bb5fb62cdb2731d688949fd5c50511e8563 100644
--- a/src/lib/components/CodeBlock/transformers.ts
+++ b/src/lib/components/CodeBlock/transformers.ts
@@ -1,48 +1,123 @@
 import type { ShikiTransformer } from 'shiki';
 import type * as Hast from 'hast';
+import type {
+	EnhancedSelectionInfo,
+	SelectionInfo,
+	SourcePosition,
+	SourceSelection
+} from './types';
 
-/**
- * Adds line numbers to the code display
- */
-export const addLineNumber: ShikiTransformer = {
-	line(node, line) {
-		// Create a span element for the line number
-		const lineNumberSpan: Hast.Element = {
-			type: 'element',
-			tagName: 'span',
-			properties: {
-				className: ['line-number']
-			},
-			children: [{ type: 'text', value: `${line}` }]
-		};
-
-		// Add the line number span at the beginning of the line's children
-		if (node.children) {
-			node.children.unshift(lineNumberSpan);
-		}
+export const sourceMappingTransformer: ShikiTransformer = {
+	name: 'source-mapping-transformer',
+	span(hast, line, col, lineElement, token) {
+		hast.properties['data-line'] = line;
+		hast.properties['data-col'] = col; // the tokens start column on the given line
+		// token.offset is the start offset of the token, relative to the input code. 0-indexed.
+		hast.properties['data-token-offset'] = token.offset;
+		hast.properties['data-token-len'] = token.content.length;
+
+		hast.properties['data-token-id'] = `${line}:${col}:${token.offset}`;
+
+		// if (!hast.position) return;
+		// hast.properties['data-position'] = JSON.stringify(hast.position);
+	},
+	line(hast, line) {
+		hast.properties['data-source-line'] = line;
 	}
 };
 
-/**
- * Adds data attributes to each token span to help with selection mapping
- */
-export const addTokenIds: ShikiTransformer = {
-	code(node) {
-		// Add a class to the code element for easier selection
-		node.properties.className = [...(node.properties.className || []), 'annotatable-code'];
-	},
+export function domSelectionToSourceSelection(
+	selection: Selection,
+	codeEl: HTMLElement
+): SourceSelection | null {
+	if (!selection || selection.rangeCount === 0 || !codeEl) return null;
+	const range = selection.getRangeAt(0);
 
-	line(node, line) {
-		// Add a data attribute for the line number to help with selection mapping
-		node.properties['data-line'] = `${line}`;
-	},
+	if (!codeEl.contains(range.commonAncestorContainer)) return null;
+
+	const startPosition = findSourcePosition(range.startContainer, range.startOffset, codeEl);
+	const endPosition = findSourcePosition(range.endContainer, range.endOffset, codeEl);
 
-	span(node, line, col) {
-		// Add a data attribute to help with selection mapping
-		node.properties['data-token'] = `token:${line}:${col}`;
+	if (!startPosition || !endPosition) return null;
 
-		// Add the line and column as separate attributes for easier access
-		node.properties['data-line'] = `${line}`;
-		node.properties['data-column'] = `${col}`;
+	const selectedText = selection.toString(); // TODO get source text, not DOM text
+
+	return { start: startPosition, end: endPosition, text: selectedText };
+}
+
+function findSourcePosition(
+	node: Node,
+	offset: number,
+	codeEl: HTMLElement
+): SourcePosition | null {
+	// Special case for text nodes - find their parent element
+	let element: Element | null = null;
+	let textOffset = offset;
+
+	if (node.nodeType === Node.TEXT_NODE) {
+		element = node.parentElement;
+		textOffset = offset;
+	} else if (node.nodeType === Node.ELEMENT_NODE) {
+		element = node as Element;
+
+		// If the offset is pointing to a child node, adjust accordingly
+		if (offset > 0 && offset <= element.childNodes.length) {
+			const childNode = element.childNodes[offset - 1];
+			if (childNode.nodeType === Node.TEXT_NODE) {
+				return findSourcePosition(childNode, (childNode as Text).length, codeEl);
+			}
+		}
 	}
-};
+
+	if (!element) {
+		return null;
+	}
+
+	// find the closest ancestor that has position data
+	let current: Element | null = element;
+	while (current && current !== codeEl) {
+		if (current.hasAttribute('data-line') && current.hasAttribute('data-col')) {
+			break;
+		}
+		current = current.parentElement;
+	}
+
+	if (!current || current === codeEl) {
+		return null;
+	}
+
+	const line = parseInt(current.getAttribute('data-line') || '0', 10);
+	const col = parseInt(current.getAttribute('data-col') || '0', 10);
+	// TODO use these rather than line/col, since the latter don't guarantee any relation to the source input
+	const tokenOffset = parseInt(current.getAttribute('data-token-offset') || '0', 10);
+	const tokenLen = parseInt(current.getAttribute('data-token-len') || '0', 10);
+
+	// Calculate the actual character offset within the token
+	let charOffset = 0;
+
+	if (node.nodeType === Node.TEXT_NODE && node.parentElement === current) {
+		// Direct text child of the token span
+		charOffset = textOffset;
+	} else {
+		// need to calculate offset based on preceding text nodes
+		// TODO: more complex DOM structures might need refinement
+		let textContentBeforeOffset = '';
+
+		if (node.nodeType === Node.TEXT_NODE) {
+			// count characters in previous sibling text nodes
+			let prevNode = node.previousSibling;
+			while (prevNode) {
+				if (prevNode.nodeType === Node.TEXT_NODE) {
+					textContentBeforeOffset = prevNode.textContent + textContentBeforeOffset;
+				}
+				prevNode = prevNode.previousSibling;
+			}
+			charOffset = textContentBeforeOffset.length + textOffset;
+		}
+	}
+
+	return {
+		line: line,
+		column: col + charOffset
+	};
+}
diff --git a/src/lib/components/CodeBlock/types.ts b/src/lib/components/CodeBlock/types.ts
index 84fc0f099a98456cd1b3407cdb8e9c78559688f7..3c81f944fd5a117238dd4494b4dca92b0da5e174 100644
--- a/src/lib/components/CodeBlock/types.ts
+++ b/src/lib/components/CodeBlock/types.ts
@@ -15,15 +15,40 @@ export interface CodeBlockProps {
 	prePadding?: string;
 	preClasses?: string;
 	// Selection props
-	onSelectionChange?: (selInfo: Selection) => void;
+	onSelectionChange?: (selInfo: EnhancedSelectionInfo) => void;
 	// Debug view
 	showDebug?: boolean;
 }
 
+// TODO text position for all ranges (startLine, startColumn, endLine, endColumn)
 export type SelectionInfo = {
 	readonly [K in keyof Selection as Selection[K] extends Function ? never : K]: Selection[K];
 };
 
+/**
+ * Enhanced SelectionInfo that includes source positions
+ */
+export interface EnhancedSelectionInfo extends SelectionInfo {
+	source?: SourceSelection;
+}
+
+/**
+ * Source code position (0-based)
+ */
+export interface SourcePosition {
+	line: number; // 0-based line number
+	column: number; // 0-based column number
+}
+
+/**
+ * Selection range in source code
+ */
+export interface SourceSelection {
+	start: SourcePosition;
+	end: SourcePosition;
+	text: string;
+}
+
 /**
  * Information about a DOM Selection
  */
@@ -48,34 +73,3 @@ export type SelectionInfo = {
 // 		};
 // 	};
 // }
-
-/**
- * Represents a position in the text source
- */
-export interface SrcPosition {
-	column: number;
-	line: number;
-}
-
-/**
- * Represents a range in the text source
- * startLineNumber, startColumn <= endLineNumber, endColumn
- */
-export interface SrcRange {
-	start: SrcPosition;
-	end: SrcPosition;
-}
-
-/**
- * Represents an annotation added to the code
- */
-export interface Annotation {
-	id: string;
-	// TODO completely replace selection with position
-	selection: SelectionInfo;
-	// should be translated from/to a DOM Selection
-	appliesTo: SrcRange;
-	content?: string;
-	color?: string;
-	createdAt: Date;
-}
diff --git a/src/routes/(app)/debug/editor/+page.svelte b/src/routes/(app)/debug/editor/+page.svelte
index 2b9935fddbca8ce7d7fe28f3168187289a0d8cc0..109d49f0c1f61f58197ff67f9d951e7ee8d25a83 100644
--- a/src/routes/(app)/debug/editor/+page.svelte
+++ b/src/routes/(app)/debug/editor/+page.svelte
@@ -1,62 +1,40 @@
 <script lang="ts">
-import { browser } from '$app/environment';
-import { onDestroy, onMount } from 'svelte';
+	import CodeBlock from '$lib/components/CodeBlock/CodeBlock.svelte';
+	import type { EnhancedSelectionInfo } from '$lib/components/CodeBlock/types';
 
-const sampleCode = `function calculateTotal(items) {
-return items
-  .map(item => item.price * item.quantity)
-  .reduce((total, itemTotal) => total + itemTotal, 0);
+	const sampleCode = `function calculateTotal(items) {
+  return items
+    .map(item => item.price * item.quantity)
+    .reduce((total, itemTotal) => total + itemTotal, 0);
 }
 
 const cart = [
-{ name: 'Keyboard', price: 49.99, quantity: 1 },
-{ name: 'Mouse', price: 29.99, quantity: 1 },
-{ name: 'Monitor', price: 199.99, quantity: 2 }
+  { name: 'Keyboard', price: 49.99, quantity: 1 },
+  { name: 'Mouse', price: 29.99, quantity: 1 },
+  { name: 'Monitor', price: 199.99, quantity: 2 }
 ];
 
 const total = calculateTotal(cart);
 console.log(\`Total: \$\${total.toFixed(2)}\`);`;
 
-let showDebug = $state(true);
+	let showDebug = $state(true);
+	let selectionInfo: EnhancedSelectionInfo | null = $state(null);
 
-let selection: Selection | null = $state(null);
-
-let codeArea: Element | null = $state(null);
-
-function updateSelectionInfo() {
-  selection = window.getSelection();
-  console.log(selection);
-  console.log(`In codeArea: ${isSelectionInCodeBlock(selection!)}`)
-}
-
-function isSelectionInCodeBlock(selection: Selection): boolean {
-	if (!codeArea) return false;
-	if (!selection.rangeCount) return false;
-
-	const range = selection.getRangeAt(0);
-	return codeArea.contains(range.commonAncestorContainer);
-}
-
-onMount(() => {
-  	document.addEventListener("selectionchange", updateSelectionInfo);
-
-  	//if (!codeArea) return;
-    //codeArea.addEventListener('mouseup', updateSelectionInfo);
-})
+	function handleSelectionChange(info: EnhancedSelectionInfo) {
+		selectionInfo = info;
+	}
 
-onDestroy(() => {
-	if (!browser) return;
-  document.removeEventListener("selectionchange", updateSelectionInfo);
-	//if (!codeArea) return;
-  //codeArea.removeEventListener('mouseup', updateSelectionInfo);
-})
+	function formatPosition(pos: { line: number; column: number } | undefined) {
+		if (!pos) return 'None';
+		return `Line ${pos.line}, Column ${pos.column + 1}`;
+	}
 </script>
 
 <main>
 	<h1>Code Annotation Editor</h1>
 
 	<div class="code-container">
-		<pre bind:this={codeArea}>{sampleCode}</pre>
+		<CodeBlock code={sampleCode} {showDebug} onSelectionChange={handleSelectionChange} />
 	</div>
 
 	<div class="controls">
@@ -65,34 +43,84 @@ onDestroy(() => {
 			Show Selection Debug
 		</label>
 	</div>
+
+	{#if showDebug}
+		<div class="debug-panel">
+			<h3>Enhanced Selection Info</h3>
+
+			{#if selectionInfo?.source}
+				<div class="source-info">
+					<div class="info-row">
+						<span class="label">Selection Start:</span>
+						<span class="value">{formatPosition(selectionInfo.source.start)}</span>
+					</div>
+					<div class="info-row">
+						<span class="label">Selection End:</span>
+						<span class="value">{formatPosition(selectionInfo.source.end)}</span>
+					</div>
+					<div class="info-row">
+						<span class="label">Selected Text:</span>
+						<pre class="selected-text">{selectionInfo.source.text}</pre>
+					</div>
+				</div>
+			{:else}
+				<p>No selection or source mapping available. Select some code in the editor above.</p>
+			{/if}
+
+			<details>
+				<summary>Raw Selection Data</summary>
+				<pre class="selection-json">{JSON.stringify(selectionInfo, null, 2)}</pre>
+			</details>
+		</div>
+	{/if}
 </main>
 
 <style>
-	main {
-		max-width: 800px;
-		margin: 0 auto;
-		padding: 2rem;
+	.code-container {
+		margin: 1rem 0;
+		border: 1px solid #ddd;
+		border-radius: 4px;
 	}
 
-	h1 {
-		margin-bottom: 2rem;
+	.debug-panel {
+		margin-top: 1rem;
+		padding: 1rem;
+		background-color: #f8f9fa;
+		border-radius: 4px;
 	}
 
-	.code-container {
-		border: 1px solid #ddd;
-		border-radius: 4px;
-		margin-bottom: 1rem;
+	.info-row {
+		margin: 0.5rem 0;
+		display: flex;
 	}
 
-	.controls {
+	.label {
+		font-weight: bold;
+		width: 120px;
+	}
+
+	.source-info {
 		margin-bottom: 1rem;
 	}
 
-	.debug-toggle {
-		display: flex;
-		align-items: center;
-		gap: 0.5rem;
-		font-size: 0.9rem;
-		cursor: pointer;
+	.selected-text {
+		background-color: #f0f0f0;
+		padding: 0.5rem;
+		border-radius: 3px;
+		margin-top: 0.5rem;
+		font-family: monospace;
+		white-space: pre-wrap;
+	}
+
+	details {
+		margin-top: 1rem;
+	}
+
+	.selection-json {
+		max-height: 300px;
+		overflow: auto;
+		background-color: #f0f0f0;
+		padding: 0.5rem;
+		font-size: 0.8rem;
 	}
 </style>