Skip to content

feat: add import support to wasm playground editor#16440

Merged
Mossaka merged 5 commits intomainfrom
feat/wasm-imports
Feb 17, 2026
Merged

feat: add import support to wasm playground editor#16440
Mossaka merged 5 commits intomainfrom
feat/wasm-imports

Conversation

@Mossaka
Copy link
Collaborator

@Mossaka Mossaka commented Feb 17, 2026

Summary

  • Introduces a virtual filesystem (virtual_fs.go / virtual_fs_wasm.go) so the wasm compiler can resolve imports: without real filesystem access
  • Replaces direct os.ReadFile calls in import/include processors with an overridable readFileFunc — native builds default to os.ReadFile, wasm builds use the virtual FS
  • Updates compileWorkflow(md, files?) to accept an optional files map for import resolution
  • Adds a file tab system to the playground editor: users can create shared imports (e.g. shared/tools.md), switch between tabs, and the compiler resolves them automatically

Test plan

  • GOOS=js GOARCH=wasm go build ./cmd/gh-aw-wasm succeeds
  • go build ./cmd/gh-aw still works (native build)
  • go test -short ./pkg/parser/ passes
  • go test -short ./pkg/workflow/ passes
  • Manual: open playground, add a shared import tab, reference it via imports:, verify compilation works

🤖 Generated with Claude Code

Enable `imports:` resolution in the browser-based wasm compiler by
introducing a virtual filesystem. The editor now supports multiple
file tabs — the main workflow file plus any shared imports — and
passes them to the Go wasm runtime as a files map.

Changes:
- Add virtual_fs.go / virtual_fs_wasm.go for in-memory file storage
- Replace os.ReadFile with readFileFunc in import/include/yaml parsers
- Update remote_fetch_wasm.go to check virtual FS instead of os.Stat
- Accept optional files object in compileWorkflow(md, files)
- Thread files through compiler-worker.js and compiler-loader.js
- Add file tab UI to the playground editor (add/remove/switch tabs)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 17, 2026 21:00
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds import/include support to the WASM playground compiler by introducing a virtual filesystem for in-browser builds and wiring the editor/worker protocol to pass additional files for import resolution.

Changes:

  • Introduces a parser-level overridable readFileFunc, with a WASM virtual filesystem implementation (SetVirtualFiles / ClearVirtualFiles).
  • Updates include/import/YAML-import code paths to use readFileFunc instead of os.ReadFile.
  • Extends the WASM compiler bridge and playground editor to pass/maintain a files map (via file tabs) so imports: can resolve in the browser.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pkg/parser/yaml_import.go Switch YAML import reads to readFileFunc.
pkg/parser/virtual_fs.go Adds readFileFunc indirection (defaults to os.ReadFile).
pkg/parser/virtual_fs_wasm.go Implements in-memory virtual FS + overrides readFileFunc for WASM.
pkg/parser/remote_fetch_wasm.go Uses virtual FS existence checks for include resolution in WASM.
pkg/parser/include_processor.go Reads included files via readFileFunc.
pkg/parser/include_expander.go Reads included files via readFileFunc.
pkg/parser/import_processor.go Reads imported files via readFileFunc (including topo-sort).
docs/public/wasm/compiler-worker.js Extends worker protocol to forward optional files.
docs/public/wasm/compiler-loader.js Extends compile(markdown, files?) API and message payload.
docs/public/editor/index.html Adds tabbed file UI and passes non-main files as imports to the compiler.
cmd/gh-aw-wasm/main.go Accepts optional filesObject, sets virtual FS, and compiles with import support.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +59 to +72
// jsObjectToFileMap converts a JS object {path: content, ...} to map[string][]byte.
func jsObjectToFileMap(obj js.Value) map[string][]byte {
files := make(map[string][]byte)

// Use Object.keys() to iterate over the JS object
keys := js.Global().Get("Object").Call("keys", obj)
length := keys.Length()
for i := 0; i < length; i++ {
key := keys.Index(i).String()
value := obj.Get(key).String()
files[key] = []byte(value)
}

return files
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsObjectToFileMap assumes the optional second argument is a plain object whose values are strings. If a caller passes a non-object (e.g. a string/array) or non-string values, .String() will coerce silently (e.g. "[object Object]") and lead to confusing compile errors. Consider validating args[1] is an object and that each value is a string (or can be converted to one) and rejecting the Promise with a clear message when the shape is invalid.

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +81
// Set up virtual filesystem for import resolution
if files != nil {
parser.SetVirtualFiles(files)
defer parser.ClearVirtualFiles()
}
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SetVirtualFiles/ClearVirtualFiles mutate package-level globals, but compileWorkflow launches each request in a goroutine. If multiple compileWorkflow() calls are in-flight concurrently (possible for createWorkerCompiler().compile() consumers), the virtual FS can be overwritten/cleared mid-compilation, producing incorrect results. Consider serializing compilation in the WASM entrypoint (e.g., a mutex) or making the virtual FS state per-compilation instead of global.

Copilot uses AI. Check for mistakes.
Comment on lines 1198 to 1221
const tab = document.createElement('div');
tab.className = 'tab' + (file.name === activeTab ? ' active' : '');
tab.dataset.name = file.name;

const label = document.createElement('span');
label.textContent = file.name;
tab.appendChild(label);

// Close button (not for the main workflow file)
if (file.name !== MAIN_FILE) {
const close = document.createElement('button');
close.className = 'tab-close';
close.title = 'Remove file';
close.innerHTML = '<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><path d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.749.749 0 011.275.326.749.749 0 01-.215.734L9.06 8l3.22 3.22a.749.749 0 01-.326 1.275.749.749 0 01-.734-.215L8 9.06l-3.22 3.22a.751.751 0 01-1.042-.018.751.751 0 01-.018-1.042L6.94 8 3.72 4.78a.75.75 0 010-1.06z"/></svg>';
close.addEventListener('click', (e) => {
e.stopPropagation();
removeTab(file.name);
});
tab.appendChild(close);
}

tab.addEventListener('click', () => switchTab(file.name));
tabBar.insertBefore(tab, tabAdd);
}
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tabs are rendered as clickable <div> elements with click handlers, which are not keyboard-focusable by default and lack appropriate ARIA roles. This makes the new tab system hard to use with keyboard-only navigation and screen readers. Consider rendering tabs as <button> elements (or add role="tab", tabindex="0", and keyboard handlers for Enter/Space + focus management).

Copilot uses AI. Check for mistakes.
Mossaka and others added 4 commits February 17, 2026 21:38
# Conflicts:
#	docs/public/editor/index.html
The schema versioning change (e995df1) requires lock files to have
a gh-aw-metadata comment. Update the test lock content to include it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts:
#	pkg/workflow/action_sha_validation_test.go
# Conflicts:
#	docs/public/editor/index.html
@Mossaka Mossaka merged commit bc7b473 into main Feb 17, 2026
54 checks passed
@Mossaka Mossaka deleted the feat/wasm-imports branch February 17, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant