Skip to content

Commit 6cd3f4f

Browse files
authored
Toolbar and Control Plane (#61)
1 parent 9c09f0a commit 6cd3f4f

12 files changed

Lines changed: 813 additions & 48 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ Vite's `tidewave` accepts the configuration options below:
122122
Defaults to `tmp`, storing files under `tmp/tidewave/screenshots` and
123123
`tmp/tidewave/recordings`
124124
- `team`: enable Tidewave Web for teams
125+
- `toolbar`: controls whether the Tidewave toolbar is injected into your HTML
126+
pages. Defaults to `true`
125127

126128
## Available tools
127129

src/core.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export interface TidewaveConfig {
145145
port?: number;
146146
host?: string;
147147
clientUrl?: string;
148+
toolbar?: boolean;
148149
allowRemoteAccess?: boolean;
149150
allowedOrigins?: string[];
150151
projectName?: string;

src/http/handlers/config.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,29 @@ import { default as tidewavePackage } from '../../../package.json' with { type:
44

55
export type LocalPortGetter = () => number | undefined;
66

7+
export interface TidewaveConfigPayload {
8+
project_name: string;
9+
framework_type: string;
10+
tidewave_version: string;
11+
team: NonNullable<TidewaveConfig['team']> | Record<string, never>;
12+
local_port: number | undefined;
13+
tmp_dir: string;
14+
}
15+
16+
export function tidewaveConfig(
17+
config: TidewaveConfig,
18+
getLocalPort?: LocalPortGetter,
19+
): TidewaveConfigPayload {
20+
return {
21+
project_name: config.projectName || 'app',
22+
framework_type: config.framework || 'unknown',
23+
tidewave_version: tidewavePackage.version,
24+
team: config.team || {},
25+
local_port: getLocalPort?.(),
26+
tmp_dir: config.tmpDir || 'tmp',
27+
};
28+
}
29+
730
export function createHandleConfig(
831
config: TidewaveConfig,
932
getLocalPort?: LocalPortGetter,
@@ -14,19 +37,10 @@ export function createHandleConfig(
1437
next: TidewaveNext,
1538
): Promise<void> {
1639
try {
17-
const tidewaveConfig = {
18-
project_name: config.projectName || 'app',
19-
framework_type: config.framework || 'unknown',
20-
tidewave_version: tidewavePackage.version,
21-
team: config.team || {},
22-
local_port: getLocalPort?.(),
23-
tmp_dir: config.tmpDir || 'tmp',
24-
};
25-
2640
res.statusCode = 200;
2741
res.setHeader('Content-Type', 'application/json');
2842
res.setHeader('Access-Control-Allow-Origin', '*');
29-
res.end(JSON.stringify(tidewaveConfig));
43+
res.end(JSON.stringify(tidewaveConfig(config, getLocalPort)));
3044
} catch (err) {
3145
console.error(`[Tidewave] Failed to serve config: ${err}`);
3246

src/http/handlers/html.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,33 @@ import type { TidewaveConfig } from '../../core';
22
import type { TidewaveHandler, TidewaveNext, TidewaveRequest, TidewaveResponse } from '../types';
33

44
export function createHandleHtml(config: TidewaveConfig): TidewaveHandler {
5+
return createHtmlHandler(config, 'tc.js', {});
6+
}
7+
8+
export function createHandleAppHtml(config: TidewaveConfig): TidewaveHandler {
9+
return createHtmlHandler(config, 'control.js', {
10+
headers: {
11+
'Content-Security-Policy': "base-uri 'self'; frame-ancestors 'self';",
12+
},
13+
});
14+
}
15+
16+
function createHtmlHandler(
17+
config: TidewaveConfig,
18+
script: 'tc.js' | 'control.js',
19+
options: { headers?: Record<string, string> },
20+
): TidewaveHandler {
521
return async function handleHtml(
622
req: TidewaveRequest,
723
res: TidewaveResponse,
824
next: TidewaveNext,
925
): Promise<void> {
10-
// Only handle exact /tidewave path, not sub-paths
26+
// Only handle exact mounted paths, not sub-paths.
1127
const url = req.url || '/';
12-
const [pathname] = url.split('?');
28+
const pathname = url.split('?')[0] || '';
1329

14-
// Different connect-style middleware stacks may strip different URL prefixes.
15-
if (pathname !== '' && pathname !== '/' && pathname !== '/tidewave') {
30+
// Vite strips the prefix passed to server.use, so we can always check /
31+
if (pathname !== '/') {
1632
return next();
1733
}
1834

@@ -21,12 +37,15 @@ export function createHandleHtml(config: TidewaveConfig): TidewaveHandler {
2137

2238
res.statusCode = 200;
2339
res.setHeader('Content-Type', 'text/html');
40+
for (const [header, value] of Object.entries(options.headers || {})) {
41+
res.setHeader(header, value);
42+
}
2443
res.end(`
2544
<html>
2645
<head>
2746
<meta charset="UTF-8" />
2847
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
29-
<script type="module" src="${clientUrl}/tc/tc.js"></script>
48+
<script type="module" src="${clientUrl}/tc/${script}"></script>
3049
</head>
3150
<body></body>
3251
</html>

0 commit comments

Comments
 (0)