support bare image paths in css url() properties

Hunter
Tuesday, December 23rd, 2025 at 12:28 AM

README.md+4-4
main.js+14-3
vendor/build.mjs+22-22
vendor/package.json+17-17

README.md
 6  
 7  ## Usage
 8  
   -1. Open `index.html` in your web browser (or <a href="https://hunterirving.github.io/web_workshop/">click here</a>)
 9 +1. Open `index.html` in your web browser (<a href="https://hunterirving.github.io/web_workshop/">click here</a>)
10  2. Start typing HTML in the editor pane
11      - The preview pane will rerender when you're done typing
   -    - Your work is automatically saved and restored when you reload the page
   -3. Press `⌘ + S` to export your work as an HTML file (`⌘ + O` to reopen exported files)
12 +    - Your work is automatically saved, and restored when you reload the page
13 +3. Press `⌘ + S` to export your work as an HTML file (`⌘ + O` to open an existing file)
14  
15  ### Options
16  - to toggle <b>line numbers</b>, press <kbd>F1</kbd> (disabled by default)
17  - to toggle <b>line wrapping</b>, press <kbd>F2</kbd> (enabled by default)
18  
19  ### Stock Images
   -A library of retro images is included. To browse them, type the magic tag `<images>` anywhere in the editor. You can use them in your pages like so: `<img src="coffee.gif">`.
20 +A library of stock images is included for your convenience. To browse them, type `<img src="?">` anywhere in the editor. You can use them in your pages like so: `<img src="coffee.gif">`.
21  
22  <br>
23  <img src="images/hint.gif" width=60px>
main.js
18  
19  // Rewrite bare image filenames to use images/ prefix (case-insensitive, uses correct case)
20  function rewriteBareImageSrcs(html) {
   -	return html.replace(/(<img\s[^>]*\bsrc\s*=\s*["'])([^"'/:]+\.(gif|png|jpg|jpeg|svg|webp))(["'])/gi,
21 +	// Rewrite <img src="filename.ext">
22 +	html = html.replace(/(<img\s[^>]*\bsrc\s*=\s*["'])([^"'/:]+\.(gif|png|jpg|jpeg|svg|webp))(["'])/gi,
23  		(match, before, filename, ext, after) => {
24  			const original = stockImages.get(filename.toLowerCase());
25  			if (original) {

27  			}
28  			return match;
29  		});
30 +	// Rewrite url(filename.ext) in CSS
31 +	html = html.replace(/(url\(\s*["']?)([^"')/:]+\.(gif|png|jpg|jpeg|svg|webp))(["']?\s*\))/gi,
32 +		(match, before, filename, ext, after) => {
33 +			const original = stockImages.get(filename.toLowerCase());
34 +			if (original) {
35 +				return before + 'images/' + original + after;
36 +			}
37 +			return match;
38 +		});
39 +	return html;
40  }
41  
   -// Expand <images> tags into gallery table
42 +// Expand <img src="?"> tags into gallery table
43  function expandImagesTag(html) {
   -	return html.replace(/<images\s*\/?>/gi, () => {
44 +	return html.replace(/<img\s+src\s*=\s*["']?\?["']?\s*\/?>/gi, () => {
45  		const images = Array.from(stockImages.values()).sort();
46  		if (images.length === 0) return '<p>No images available</p>';
47  		const rows = images.map(filename =>
vendor/build.mjs
 2  // Build script to bundle CodeMirror for offline use
 3  //
 4  // Usage:
   -//   cd vendor
   -//   npm install
   -//   npm run build
 5 +//	cd vendor
 6 +//	npm install
 7 +//	npm run build
 8  //
 9  // This will regenerate ../codemirror-bundle.js
10  

15  const __dirname = dirname(fileURLToPath(import.meta.url));
16  
17  await esbuild.build({
   -  stdin: {
   -    contents: `
   -      export {EditorView, keymap, placeholder, lineNumbers, Decoration} from "@codemirror/view";
   -      export {EditorState, Compartment} from "@codemirror/state";
   -      export {defaultKeymap, indentWithTab, undo, redo, undoDepth, redoDepth, history, historyKeymap} from "@codemirror/commands";
   -      export {closeBrackets, closeBracketsKeymap} from "@codemirror/autocomplete";
   -      export {html} from "@codemirror/lang-html";
   -      export {githubDark} from "@fsegurai/codemirror-theme-github-dark";
   -      export {indentUnit} from "@codemirror/language";
   -      export {search, searchKeymap, closeSearchPanel, openSearchPanel} from "@codemirror/search";
   -    `,
   -    resolveDir: __dirname,
   -    loader: 'js',
   -  },
   -  bundle: true,
   -  format: 'esm',
   -  outfile: join(__dirname, '..', 'codemirror-bundle.js'),
   -  minify: true,
   -  target: ['es2020'],
18 +	stdin: {
19 +		contents: `
20 +			export {EditorView, keymap, placeholder, lineNumbers, Decoration} from "@codemirror/view";
21 +			export {EditorState, Compartment} from "@codemirror/state";
22 +			export {defaultKeymap, indentWithTab, undo, redo, undoDepth, redoDepth, history, historyKeymap} from "@codemirror/commands";
23 +			export {closeBrackets, closeBracketsKeymap} from "@codemirror/autocomplete";
24 +			export {html} from "@codemirror/lang-html";
25 +			export {githubDark} from "@fsegurai/codemirror-theme-github-dark";
26 +			export {indentUnit} from "@codemirror/language";
27 +			export {search, searchKeymap, closeSearchPanel, openSearchPanel} from "@codemirror/search";
28 +		`,
29 +		resolveDir: __dirname,
30 +		loader: 'js',
31 +	},
32 +	bundle: true,
33 +	format: 'esm',
34 +	outfile: join(__dirname, '..', 'codemirror-bundle.js'),
35 +	minify: true,
36 +	target: ['es2020'],
37  });
38  
39  console.log('✓ CodeMirror bundle created: ../codemirror-bundle.js');
vendor/package.json
 1  {
   -  "name": "codemirror-bundle",
   -  "private": true,
   -  "type": "module",
   -  "scripts": {
   -    "build": "node build.mjs"
   -  },
   -  "dependencies": {
   -    "@codemirror/autocomplete": "^6",
   -    "@codemirror/commands": "^6",
   -    "@codemirror/lang-html": "^6",
   -    "@codemirror/language": "^6",
   -    "@codemirror/search": "^6",
   -    "@codemirror/state": "^6",
   -    "@codemirror/view": "^6",
   -    "@fsegurai/codemirror-theme-github-dark": "^6",
   -    "esbuild": "^0.24"
   -  }
 2 +	"name": "codemirror-bundle",
 3 +	"private": true,
 4 +	"type": "module",
 5 +	"scripts": {
 6 +		"build": "node build.mjs"
 7 +	},
 8 +	"dependencies": {
 9 +		"@codemirror/autocomplete": "^6",
10 +		"@codemirror/commands": "^6",
11 +		"@codemirror/lang-html": "^6",
12 +		"@codemirror/language": "^6",
13 +		"@codemirror/search": "^6",
14 +		"@codemirror/state": "^6",
15 +		"@codemirror/view": "^6",
16 +		"@fsegurai/codemirror-theme-github-dark": "^6",
17 +		"esbuild": "^0.24"
18 +	}
19  }