Skip to content

Commit 0b15130

Browse files
antonsyndclaude
andcommitted
feat(playground): add local playground dev server command and skill
Add `build_sharpy playground` command (with --release and --no-watch flags) and `/playground` Claude Code skill to run the Blazor WASM playground locally with hot reload. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c9c5076 commit 0b15130

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

.claude/skills/playground/SKILL.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
name: playground
3+
description: Run the Sharpy playground (Blazor WASM) locally with hot reload
4+
---
5+
6+
Run the Sharpy playground locally as a Blazor WebAssembly dev server with hot reload.
7+
8+
**Usage:** `/playground`
9+
10+
**Behavior:**
11+
- Builds and serves `src/Sharpy.Playground` with `dotnet watch run`
12+
- Opens at `http://localhost:5000` or `https://localhost:5001`
13+
- Hot reloads on file changes (Playground, Compiler, Core)
14+
- Ctrl+C to stop
15+
16+
**Log location:** `.claude/tmp/last-playground.log`
17+
18+
## Steps
19+
20+
1. Run `mkdir -p .claude/tmp` to ensure log directory exists
21+
2. Clear the old log with `rm -f .claude/tmp/last-playground.log`
22+
3. Tell the user: "Starting playground at http://localhost:5000 (https://localhost:5001). Press Ctrl+C to stop."
23+
4. Run: `dotnet watch run --project src/Sharpy.Playground > .claude/tmp/last-playground.log 2>&1`
24+
- **Important:** This is a long-running process. Run it in the background with `run_in_background: true` so the user can continue working.
25+
5. After launching, tell the user the playground is running and they can open `http://localhost:5000` in their browser.
26+
6. If the process fails immediately (exit code non-zero within a few seconds): Print "=== PLAYGROUND FAILED ===" then `tail -50 .claude/tmp/last-playground.log`, then echo "=== Full log: .claude/tmp/last-playground.log ==="

build_tools/bin/build_sharpy

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ CORE_DIR: Path = SRC_DIR / "Sharpy.Core"
2323
CORE_TEST_DIR: Path = SRC_DIR / "Sharpy.Core.Tests"
2424
CLI_DIR: Path = SRC_DIR / "Sharpy.Cli"
2525

26+
PLAYGROUND_DIR: Path = SRC_DIR / "Sharpy.Playground"
27+
2628
EDITORS_DIR: Path = ROOT_DIR / "editors"
2729
VSCODE_EXT_DIR: Path = EDITORS_DIR / "vscode"
2830

@@ -84,6 +86,8 @@ def main() -> None:
8486
install(args=args, rest=rest)
8587
elif command == "test":
8688
test(args=args, rest=rest, release=False)
89+
elif command == "playground":
90+
playground(args=args, rest=rest)
8791
elif command == "help":
8892
parser.print_help()
8993
else:
@@ -156,6 +160,16 @@ def create_parser() -> argparse.ArgumentParser:
156160
choices=TARGET_CONSTS,
157161
default=None,
158162
)
163+
playground_parser = subparsers.add_parser(
164+
"playground", help="Runs the Blazor WASM playground locally."
165+
)
166+
playground_parser.add_argument("--release", action="store_true")
167+
playground_parser.add_argument(
168+
"--no-watch",
169+
action="store_true",
170+
help="Run without hot reload (dotnet run instead of dotnet watch run)",
171+
)
172+
159173
_ = subparsers.add_parser("help", help="Prints this help message.")
160174

161175
return parser
@@ -273,6 +287,32 @@ def build_tests(release: bool) -> None:
273287
sys.exit(1)
274288

275289

290+
def playground(args: argparse.Namespace, rest: Sequence[str]) -> None:
291+
build_type: str = "Release" if args.release else "Debug"
292+
project: str = str(PLAYGROUND_DIR)
293+
294+
get_logger().info(f"Starting playground ({build_type.lower()})...")
295+
get_logger().info("Open http://localhost:5000 or https://localhost:5001 in your browser")
296+
get_logger().info("Press Ctrl+C to stop")
297+
298+
try:
299+
if args.no_watch:
300+
subprocess.run(
301+
["dotnet", "run", "--project", project, "-c", build_type, *rest],
302+
check=True,
303+
)
304+
else:
305+
subprocess.run(
306+
["dotnet", "watch", "run", "--project", project, "-c", build_type, *rest],
307+
check=True,
308+
)
309+
except subprocess.CalledProcessError as e:
310+
get_logger().error(f"Playground failed: {e}")
311+
sys.exit(1)
312+
except KeyboardInterrupt:
313+
get_logger().info("Playground stopped.")
314+
315+
276316
def setup(args: argparse.Namespace, rest: Sequence[str]) -> None:
277317
get_logger().info("Setting up...")
278318
get_logger().info("Restoring project...")

0 commit comments

Comments
 (0)