@xfey/tutti 0.1.46 → 0.1.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/collaboration-state/messages.js +1 -5
- package/dist/server-shell/cli/git-bootstrap.d.ts +3 -1
- package/dist/server-shell/cli/git-bootstrap.js +27 -13
- package/dist/server-shell/cli/launch.js +4 -1
- package/dist/server-shell/local-console/project-service.d.ts +2 -1
- package/dist/server-shell/local-console/project-service.js +60 -6
- package/dist/workspace-ops/run-workspaces.js +7 -3
- package/node_modules/@tutti/shared/dist/utils/path-policy/index.d.ts +11 -2
- package/node_modules/@tutti/shared/dist/utils/path-policy/index.js +20 -8
- package/package.json +1 -1
- package/web/assets/avatar-avery-CkLXb185.svg +1 -0
- package/web/assets/homepage-motion-scene-D0MQyNSs.js +1 -0
- package/web/assets/{index-RBTs-6vt.css → index-BHvAiXUS.css} +1 -1
- package/web/assets/{index-BqiHeYIv.js → index-Br4CGoxh.js} +16 -16
- package/web/assets/ode-to-joy-SNBUPr3-.svg +1 -0
- package/web/index.html +2 -2
package/README.md
CHANGED
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
- Reference 和 Skills 上传的最终写入由 Host Project API 完成;Host 只通过 Relay host-control resolve 获取短期 R2 URL 下载 staged object,不接收浏览器 base64 文件正文,也不持久化 presigned URL。
|
|
50
50
|
- Host Project API command 的 browser session context 只信任 Relay tunnel metadata 中的 `relay_session_context`;route handler 不读取浏览器身份 header,也不接受 payload 内身份字段。
|
|
51
51
|
- Fastify request log、debug log、SSE payload、activity event 和 run result 都必须经过 redaction,不得泄露 provider secret、host registration secret、host connection token、join token、cookie、host 绝对路径或 run workspace path。
|
|
52
|
-
- Repo Viewer path policy
|
|
52
|
+
- Repo path metadata policy 与更严格的 Viewer content path policy 已拆分为可测试 helper:Phase 6 Viewer / Reference Files API 继续拒绝 secret-like 内容读取,Run / merge changed paths 可以完整报告结构性安全的 repo-relative 文件名;Phase 7 checks executor 已以 safe npm script discovery、结构化 `spawn`、timeout、stdout / stderr redaction 与 no-shell 执行为首批命令安全边界。
|
|
53
53
|
- 当前源码 package root 仍是 `apps/server`;对外 npm 名由 staging artifact 使用 `@xfey/tutti`,internal workspace dependency bundling、对应外部 runtime dependency bundling 和 packed CLI install smoke 已接通。
|
|
@@ -365,11 +365,7 @@ export function readMainChatMessageWindow(db, options = {}) {
|
|
|
365
365
|
.all(target.created_at, target.created_at, target.id, afterLimit + 1);
|
|
366
366
|
hasOlder = olderRows.length > beforeLimit;
|
|
367
367
|
hasNewer = newerRows.length > afterLimit;
|
|
368
|
-
rows = [
|
|
369
|
-
...olderRows.slice(0, beforeLimit).reverse(),
|
|
370
|
-
target,
|
|
371
|
-
...newerRows.slice(0, afterLimit),
|
|
372
|
-
];
|
|
368
|
+
rows = [...olderRows.slice(0, beforeLimit).reverse(), target, ...newerRows.slice(0, afterLimit)];
|
|
373
369
|
}
|
|
374
370
|
}
|
|
375
371
|
else if (before !== null) {
|
|
@@ -9,14 +9,16 @@ export type LaunchConfirmationHandler = (request: LaunchConfirmationRequest) =>
|
|
|
9
9
|
export type GitBootstrapOptions = {
|
|
10
10
|
workspaceRoot: string;
|
|
11
11
|
projectId: ProjectId;
|
|
12
|
+
projectSetup: "create" | "resume";
|
|
12
13
|
assumeYes?: boolean;
|
|
13
14
|
confirm?: LaunchConfirmationHandler;
|
|
14
15
|
prepareBootstrapFiles?: () => void;
|
|
15
16
|
};
|
|
16
17
|
export type GitBootstrapResult = {
|
|
17
18
|
branch: typeof TUTTI_MAINLINE_BRANCH;
|
|
19
|
+
mode: "bootstrap_baseline" | "adopt_existing" | "resume_existing";
|
|
18
20
|
initialized_git: boolean;
|
|
19
|
-
|
|
21
|
+
baseline_commit_created: boolean;
|
|
20
22
|
};
|
|
21
23
|
export declare function assertSafeProjectRoot(workspaceRoot: string): void;
|
|
22
24
|
export declare function listSensitiveBootstrapCandidates(workspaceRoot: string): string[];
|
|
@@ -180,11 +180,12 @@ export function listSensitiveBootstrapCandidates(workspaceRoot) {
|
|
|
180
180
|
.filter((file) => !isGitIgnored(workspaceRoot, file))
|
|
181
181
|
.sort();
|
|
182
182
|
}
|
|
183
|
-
function
|
|
183
|
+
function commitBaselineIfNeeded(workspaceRoot, projectId, kind) {
|
|
184
184
|
if (!hasUncommittedChanges(workspaceRoot)) {
|
|
185
185
|
return false;
|
|
186
186
|
}
|
|
187
187
|
runGit(["add", "--all"], workspaceRoot);
|
|
188
|
+
const title = kind === "adoption" ? "Tutti: adopt existing workspace" : "Tutti: initialize project";
|
|
188
189
|
runGit([
|
|
189
190
|
"-c",
|
|
190
191
|
"user.name=Tutti",
|
|
@@ -192,7 +193,7 @@ function commitBootstrapIfNeeded(workspaceRoot, projectId) {
|
|
|
192
193
|
"user.email=tutti@example.invalid",
|
|
193
194
|
"commit",
|
|
194
195
|
"-m",
|
|
195
|
-
|
|
196
|
+
`${title}\n\nProject: ${projectId}\nBranch: ${TUTTI_MAINLINE_BRANCH}`,
|
|
196
197
|
], workspaceRoot);
|
|
197
198
|
return true;
|
|
198
199
|
}
|
|
@@ -208,7 +209,7 @@ async function ensureGitRepository(options) {
|
|
|
208
209
|
}, options);
|
|
209
210
|
}
|
|
210
211
|
runGit(["init", "-b", TUTTI_MAINLINE_BRANCH], workspaceRoot);
|
|
211
|
-
return true;
|
|
212
|
+
return { initializedGit: true, hadHead: false };
|
|
212
213
|
}
|
|
213
214
|
if (!rootHasGitDirectory && gitTopLevel !== workspaceRoot) {
|
|
214
215
|
await requireConfirmation({
|
|
@@ -216,31 +217,44 @@ async function ensureGitRepository(options) {
|
|
|
216
217
|
message: "This directory appears to be inside another Git repository. Tutti will initialize a nested Git repository here.",
|
|
217
218
|
}, options);
|
|
218
219
|
runGit(["init", "-b", TUTTI_MAINLINE_BRANCH], workspaceRoot);
|
|
219
|
-
return true;
|
|
220
|
+
return { initializedGit: true, hadHead: false };
|
|
220
221
|
}
|
|
221
|
-
|
|
222
|
+
const repositoryHadHead = hasHead(workspaceRoot);
|
|
223
|
+
if (options.projectSetup === "create" && hasUncommittedChanges(workspaceRoot)) {
|
|
222
224
|
await requireConfirmation({
|
|
223
225
|
kind: "uncommitted_changes",
|
|
224
226
|
message: "Current repository has uncommitted changes. Tutti can commit the current working tree as the initial Tutti baseline.",
|
|
225
227
|
}, options);
|
|
226
228
|
}
|
|
227
229
|
switchToTuttiMainline(workspaceRoot);
|
|
228
|
-
return false;
|
|
230
|
+
return { initializedGit: false, hadHead: repositoryHadHead };
|
|
229
231
|
}
|
|
230
232
|
export async function ensureGitBootstrap(options) {
|
|
231
233
|
const workspaceRoot = resolve(options.workspaceRoot);
|
|
232
234
|
assertSafeProjectRoot(workspaceRoot);
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
235
|
+
const repository = await ensureGitRepository({ ...options, workspaceRoot });
|
|
236
|
+
const createsBootstrapBaseline = repository.initializedGit || !repository.hadHead;
|
|
237
|
+
const adoptsExistingRepository = repository.hadHead && options.projectSetup === "create";
|
|
238
|
+
const mode = createsBootstrapBaseline
|
|
239
|
+
? "bootstrap_baseline"
|
|
240
|
+
: adoptsExistingRepository
|
|
241
|
+
? "adopt_existing"
|
|
242
|
+
: "resume_existing";
|
|
243
|
+
if (createsBootstrapBaseline) {
|
|
244
|
+
ensureMinimalGitignore(workspaceRoot);
|
|
245
|
+
const sensitiveCandidates = listSensitiveBootstrapCandidates(workspaceRoot);
|
|
246
|
+
if (sensitiveCandidates.length > 0) {
|
|
247
|
+
throw new LaunchError("sensitive_file_detected", `Sensitive-like file would enter bootstrap commit: ${sensitiveCandidates[0]}`, "Move or ignore sensitive files before launching. `--yes` cannot bypass this check.");
|
|
248
|
+
}
|
|
238
249
|
}
|
|
239
250
|
options.prepareBootstrapFiles?.();
|
|
240
251
|
return {
|
|
241
252
|
branch: TUTTI_MAINLINE_BRANCH,
|
|
242
|
-
|
|
243
|
-
|
|
253
|
+
mode,
|
|
254
|
+
initialized_git: repository.initializedGit,
|
|
255
|
+
baseline_commit_created: createsBootstrapBaseline || adoptsExistingRepository
|
|
256
|
+
? commitBaselineIfNeeded(workspaceRoot, options.projectId, adoptsExistingRepository ? "adoption" : "bootstrap")
|
|
257
|
+
: false,
|
|
244
258
|
};
|
|
245
259
|
}
|
|
246
260
|
//# sourceMappingURL=git-bootstrap.js.map
|
|
@@ -55,7 +55,10 @@ export async function prepareLaunchProject(options) {
|
|
|
55
55
|
const gitOptions = {
|
|
56
56
|
workspaceRoot,
|
|
57
57
|
projectId,
|
|
58
|
-
|
|
58
|
+
projectSetup: projectIdentityCreated ? "create" : "resume",
|
|
59
|
+
...(projectIdentityCreated
|
|
60
|
+
? { prepareBootstrapFiles: () => writeProjectIdentity(workspaceRoot, projectId) }
|
|
61
|
+
: {}),
|
|
59
62
|
};
|
|
60
63
|
if (options.yes !== undefined) {
|
|
61
64
|
gitOptions.assumeYes = options.yes;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ProjectId, type RelayProjectRef } from "@tutti/shared/ids";
|
|
2
|
-
import { type ConfigureProjectOpenAiProviderResult } from "../../providers/openai/index.js";
|
|
2
|
+
import { type ConfigureProjectOpenAiProviderResult, type OpenAiProviderCredentialValidator } from "../../providers/openai/index.js";
|
|
3
3
|
import { type FetchLike } from "../cli/host-runtime-endpoint.js";
|
|
4
4
|
import { type RuntimeProjectRow } from "../cli/runtime-commands.js";
|
|
5
5
|
import { type LocalConsoleInvocationContext } from "./invocation-context.js";
|
|
@@ -54,6 +54,7 @@ export declare class LocalConsoleProjectService {
|
|
|
54
54
|
tuttiHome: string;
|
|
55
55
|
serviceEnvironment?: NodeJS.ProcessEnv;
|
|
56
56
|
fetchImpl?: FetchLike;
|
|
57
|
+
validateProviderCredential?: OpenAiProviderCredentialValidator;
|
|
57
58
|
});
|
|
58
59
|
listProjects(context: LocalConsoleInvocationContext): Promise<LocalConsoleProject[]>;
|
|
59
60
|
discoverModels(input: {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { existsSync, statSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { relayProjectRouteSegment } from "@tutti/shared/ids";
|
|
4
|
-
import { configureProjectOpenAiProvider, discoverOpenAiModels, readOpenAiProviderConfigProjection, } from "../../providers/openai/index.js";
|
|
4
|
+
import { configureProjectOpenAiProvider, discoverOpenAiModels, isOpenAiApiKey, readOpenAiProviderConfigProjection, validateOpenAiCredential, } from "../../providers/openai/index.js";
|
|
5
5
|
import { formatCliErrorReason, LaunchError } from "../cli/errors.js";
|
|
6
6
|
import { createRuntimeEndpointProbe, waitForHostShutdown, } from "../cli/host-runtime-endpoint.js";
|
|
7
|
-
import { prepareLaunchProject, resolveRelayUrl } from "../cli/launch.js";
|
|
7
|
+
import { prepareLaunchProject, resolveLaunchLocalContext, resolveRelayUrl } from "../cli/launch.js";
|
|
8
8
|
import { requestHostLocalIdleShutdown, rotateHostLocalInvite, } from "../cli/local-control-client.js";
|
|
9
9
|
import { readHostRegistrationSecret, readMachineProjectBinding, readMachineRuntimeEndpoint, } from "../cli/machine-local.js";
|
|
10
10
|
import { spawnDetachedHost, waitForManagedHostReady } from "../cli/managed-host.js";
|
|
@@ -152,12 +152,15 @@ export class LocalConsoleProjectService {
|
|
|
152
152
|
#tuttiHome;
|
|
153
153
|
#serviceEnvironment;
|
|
154
154
|
#fetchImpl;
|
|
155
|
+
#validateProviderCredential;
|
|
155
156
|
#launches = new Map();
|
|
156
157
|
#mutationTail = Promise.resolve();
|
|
157
158
|
constructor(options) {
|
|
158
159
|
this.#tuttiHome = resolve(options.tuttiHome);
|
|
159
160
|
this.#serviceEnvironment = options.serviceEnvironment ?? process.env;
|
|
160
161
|
this.#fetchImpl = options.fetchImpl ?? fetch;
|
|
162
|
+
this.#validateProviderCredential =
|
|
163
|
+
options.validateProviderCredential ?? validateOpenAiCredential;
|
|
161
164
|
}
|
|
162
165
|
#operationOptions(context) {
|
|
163
166
|
return {
|
|
@@ -243,6 +246,47 @@ export class LocalConsoleProjectService {
|
|
|
243
246
|
apiKey: provider.api_key,
|
|
244
247
|
});
|
|
245
248
|
}
|
|
249
|
+
async #validateLaunchProvider(input) {
|
|
250
|
+
let validation;
|
|
251
|
+
try {
|
|
252
|
+
validation = isOpenAiApiKey(input.api_key)
|
|
253
|
+
? await this.#validateProviderCredential({
|
|
254
|
+
apiKey: input.api_key,
|
|
255
|
+
apiBaseUrl: input.base_url,
|
|
256
|
+
defaultModel: input.model,
|
|
257
|
+
})
|
|
258
|
+
: {
|
|
259
|
+
kind: "failed",
|
|
260
|
+
reason: "provider_auth_invalid",
|
|
261
|
+
retryable: false,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
throw this.#normalizeError(error);
|
|
266
|
+
}
|
|
267
|
+
if (validation.kind === "failed") {
|
|
268
|
+
const failure = {
|
|
269
|
+
kind: "validation_failed",
|
|
270
|
+
reason: validation.reason,
|
|
271
|
+
retryable: validation.retryable,
|
|
272
|
+
...(validation.diagnostic === undefined ? {} : { diagnostic: validation.diagnostic }),
|
|
273
|
+
};
|
|
274
|
+
throw new LocalConsoleProjectError(failure.reason, localConsoleProviderValidationMessage(failure));
|
|
275
|
+
}
|
|
276
|
+
return { input, validation };
|
|
277
|
+
}
|
|
278
|
+
#requireStoredLaunchProvider(workspacePath, operation) {
|
|
279
|
+
const context = resolveLaunchLocalContext({ workspacePath, ...operation });
|
|
280
|
+
const provider = context.existing_identity.kind === "present"
|
|
281
|
+
? readOpenAiProviderConfigProjection({
|
|
282
|
+
tuttiHome: context.tutti_home,
|
|
283
|
+
projectId: context.existing_identity.project_id,
|
|
284
|
+
})
|
|
285
|
+
: { status: "not_configured" };
|
|
286
|
+
if (provider.status !== "configured") {
|
|
287
|
+
throw new LocalConsoleProjectError("provider_configuration_required", "This project does not have a valid saved Provider. Configure it in the launch form.");
|
|
288
|
+
}
|
|
289
|
+
}
|
|
246
290
|
async launchProject(input, context) {
|
|
247
291
|
const workspacePath = validateWorkspacePath(input.workspacePath);
|
|
248
292
|
const active = this.#launches.get(workspacePath);
|
|
@@ -263,6 +307,15 @@ export class LocalConsoleProjectService {
|
|
|
263
307
|
}
|
|
264
308
|
async #launchProject(input, context) {
|
|
265
309
|
const operation = this.#operationOptions(context);
|
|
310
|
+
if (input.provider === undefined) {
|
|
311
|
+
try {
|
|
312
|
+
this.#requireStoredLaunchProvider(input.workspacePath, operation);
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
throw this.#normalizeError(error);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const provider = input.provider === undefined ? undefined : await this.#validateLaunchProvider(input.provider);
|
|
266
319
|
let preparation;
|
|
267
320
|
try {
|
|
268
321
|
preparation = await prepareLaunchProject({
|
|
@@ -282,13 +335,14 @@ export class LocalConsoleProjectService {
|
|
|
282
335
|
catch (error) {
|
|
283
336
|
throw this.#normalizeError(error);
|
|
284
337
|
}
|
|
285
|
-
if (
|
|
338
|
+
if (provider !== undefined) {
|
|
286
339
|
const configured = await configureProjectOpenAiProvider({
|
|
287
340
|
tuttiHome: preparation.tutti_home,
|
|
288
341
|
projectId: preparation.project_id,
|
|
289
|
-
apiBaseUrl: input.
|
|
290
|
-
apiKey: input.
|
|
291
|
-
defaultModel: input.
|
|
342
|
+
apiBaseUrl: provider.input.base_url,
|
|
343
|
+
apiKey: provider.input.api_key,
|
|
344
|
+
defaultModel: provider.input.model,
|
|
345
|
+
validateCredential: () => Promise.resolve(provider.validation),
|
|
292
346
|
});
|
|
293
347
|
if (configured.kind === "validation_failed") {
|
|
294
348
|
throw new LocalConsoleProjectError(configured.reason, localConsoleProviderValidationMessage(configured));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, rmSync } from "node:fs";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { classifyRepositoryPathMetadata } from "@tutti/shared/utils";
|
|
5
5
|
import { WorkspaceOpsError } from "./errors.js";
|
|
6
6
|
import { runGit, runGitRaw, runGitText, stagedChangesExist, summarizeGitFailure, tryGitText, } from "./git.js";
|
|
7
7
|
import { requireMainlineWriteReady } from "./mainline.js";
|
|
@@ -54,8 +54,12 @@ function parsePorcelainChangedPaths(output) {
|
|
|
54
54
|
for (const entry of entries) {
|
|
55
55
|
const pathText = entry.slice(3);
|
|
56
56
|
const path = pathText.includes(" -> ") ? pathText.split(" -> ").at(-1) : pathText;
|
|
57
|
-
if (path
|
|
58
|
-
|
|
57
|
+
if (path === undefined) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const classification = classifyRepositoryPathMetadata(path);
|
|
61
|
+
if (classification.kind === "allowed" && classification.path_kind === "project_relative") {
|
|
62
|
+
paths.add(classification.normalized_path);
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
return [...paths].sort((left, right) => left.localeCompare(right, "en"));
|
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
export type
|
|
2
|
-
export type
|
|
1
|
+
export type RepositoryPathMetadataRejectReason = "absolute_path" | "parent_traversal" | "private_tutti_path" | "git_path" | "control_character";
|
|
2
|
+
export type RepositoryPathMetadataClassification = {
|
|
3
3
|
kind: "allowed";
|
|
4
4
|
normalized_path: string;
|
|
5
5
|
path_kind: "root" | "project_relative";
|
|
6
6
|
} | {
|
|
7
|
+
kind: "rejected";
|
|
8
|
+
reason_code: RepositoryPathMetadataRejectReason;
|
|
9
|
+
normalized_path?: string;
|
|
10
|
+
};
|
|
11
|
+
export type ViewerPathRejectReason = RepositoryPathMetadataRejectReason | "secret_like_path";
|
|
12
|
+
export type ViewerPathClassification = Extract<RepositoryPathMetadataClassification, {
|
|
13
|
+
kind: "allowed";
|
|
14
|
+
}> | {
|
|
7
15
|
kind: "rejected";
|
|
8
16
|
reason_code: ViewerPathRejectReason;
|
|
9
17
|
normalized_path?: string;
|
|
10
18
|
};
|
|
19
|
+
export declare function classifyRepositoryPathMetadata(path: string): RepositoryPathMetadataClassification;
|
|
11
20
|
export declare function classifyViewerPath(path: string): ViewerPathClassification;
|
|
12
21
|
export declare function isViewerPathAllowed(path: string): boolean;
|
|
13
22
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -36,19 +36,18 @@ function hasControlCharacter(value) {
|
|
|
36
36
|
}
|
|
37
37
|
return false;
|
|
38
38
|
}
|
|
39
|
-
export function
|
|
39
|
+
export function classifyRepositoryPathMetadata(path) {
|
|
40
40
|
if (hasControlCharacter(path)) {
|
|
41
41
|
return { kind: "rejected", reason_code: "control_character" };
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
if (trimmed === "" || trimmed === ".") {
|
|
43
|
+
if (path === "" || path === ".") {
|
|
45
44
|
return { kind: "allowed", normalized_path: "", path_kind: "root" };
|
|
46
45
|
}
|
|
47
|
-
if (isAbsolutePath(
|
|
46
|
+
if (isAbsolutePath(path)) {
|
|
48
47
|
return { kind: "rejected", reason_code: "absolute_path" };
|
|
49
48
|
}
|
|
50
49
|
const segments = [];
|
|
51
|
-
for (const segment of
|
|
50
|
+
for (const segment of path.replaceAll("\\", "/").split("/")) {
|
|
52
51
|
if (segment === "" || segment === ".") {
|
|
53
52
|
continue;
|
|
54
53
|
}
|
|
@@ -62,9 +61,6 @@ export function classifyViewerPath(path) {
|
|
|
62
61
|
if (lower === ".tutti") {
|
|
63
62
|
return { kind: "rejected", reason_code: "private_tutti_path" };
|
|
64
63
|
}
|
|
65
|
-
if (isSecretLikeSegment(segment)) {
|
|
66
|
-
return { kind: "rejected", reason_code: "secret_like_path" };
|
|
67
|
-
}
|
|
68
64
|
segments.push(segment);
|
|
69
65
|
}
|
|
70
66
|
const normalizedPath = segments.join("/");
|
|
@@ -74,6 +70,22 @@ export function classifyViewerPath(path) {
|
|
|
74
70
|
path_kind: normalizedPath === "" ? "root" : "project_relative",
|
|
75
71
|
};
|
|
76
72
|
}
|
|
73
|
+
export function classifyViewerPath(path) {
|
|
74
|
+
if (hasControlCharacter(path)) {
|
|
75
|
+
return { kind: "rejected", reason_code: "control_character" };
|
|
76
|
+
}
|
|
77
|
+
const classification = classifyRepositoryPathMetadata(path.trim());
|
|
78
|
+
if (classification.kind === "rejected") {
|
|
79
|
+
return classification;
|
|
80
|
+
}
|
|
81
|
+
if (classification.normalized_path.split("/").some(isSecretLikeSegment)) {
|
|
82
|
+
return {
|
|
83
|
+
kind: "rejected",
|
|
84
|
+
reason_code: "secret_like_path",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return classification;
|
|
88
|
+
}
|
|
77
89
|
export function isViewerPathAllowed(path) {
|
|
78
90
|
return classifyViewerPath(path).kind === "allowed";
|
|
79
91
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" fill="none" shape-rendering="auto" aria-hidden="true"><!-- Generated by DiceBear (https://dicebear.com) --><metadata xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/"><rdf:RDF><rdf:Description><dc:title>Dylan! The Avatar Generator</dc:title><dc:creator>Natalia Spivak</dc:creator><dc:source xsi:type="dcterms:URI">https://www.figma.com/community/file/1356575240759683500</dc:source><dcterms:license xsi:type="dcterms:URI">https://creativecommons.org/licenses/by/4.0/</dcterms:license><dc:rights>Remix of “Dylan! The Avatar Generator” (https://www.figma.com/community/file/1356575240759683500) by “Natalia Spivak”, licensed under “CC BY 4.0” (https://creativecommons.org/licenses/by/4.0/)</dc:rights></rdf:Description></rdf:RDF></metadata><defs><g id="mood-hopeful-181c8200"><path d="M7.88 15.65c-1.18 1.26-2.17 2.74-2.36 4.49a4.4 4.4 0 0 0 1.78 4.03c1.26.9 2.88.8 4.07-.13a5.5 5.5 0 0 0 1.87-3.88c.1-.97-1.46-1.38-1.86-.5-.75 1.63.16 3.26 1.34 4.42a3.9 3.9 0 0 0 4.13.98 4.6 4.6 0 0 0 2.93-3.17 6.6 6.6 0 0 0-.82-4.87c-.64-1.1-2.37-.1-1.73 1.01.92 1.6 1.18 4-.71 5-.68.37-1.48.38-2.1-.1-.5-.38-1.64-1.54-1.31-2.25l-1.86-.5c-.14 1.34-1.44 3.63-2.97 2.23-1.74-1.59-.22-4 1.03-5.33.9-.94-.5-2.36-1.4-1.4z" fill="black"/></g><g id="hair-longCurls-181c8200"><path d="M21.73 17.15c-.23.95-.21 2.72 0 3.67s1.46 1.17 2.44 1.15c1 0 1.75-.93 2.22-1.83.58-1.1.97-2.31 1.16-3.54-.09 1.82.42 4.03 2.15 4.59 1.2.38 2.53-.24 3.34-1.2a10 10 0 0 0 1.6-3.4 8 8 0 0 0 .16 3.34c.35 1.07 1.27 2.05 2.4 2.1.92.04 1.76-.52 2.4-1.2a7 7 0 0 0 1.8-3.92c-.54 2 1.15 4.27 3.2 4.34s3.9-2.1 3.49-4.13c.3 1.57 1.44 3.04 2.99 3.43s3.42-.59 3.7-2.17a16 16 0 0 0-.8 8.7c.17.9.5 1.9 1.32 2.31s2.1-.36 1.76-1.2a16.8 16.8 0 0 0-3.86 10.6c0 1.3.2 2.71 1.12 3.6s2.78.9 3.29-.3c-1.68 1.32-2.76 6.96-2.81 9.1-.04 1.95 0 4 .94 5.7s2.98 2.98 4.83 2.37c2.1-.69 3.73-6.6 3.35-8.79-.32.2-.21.75.1.95.33.2.75.14 1.12.03a5.1 5.1 0 0 0 3.5-3.88 5.1 5.1 0 0 0-1.87-4.88c.06.62 1.02.5 1.51.1a7.2 7.2 0 0 0 2.32-7.77c-.33-.95-1.13-1.97-2.12-1.79 1.32-.17 1.98-1.72 2-3.05.06-3.82-2.17-8.5-6-8.3.96-2.2.86-5.09-.71-6.92-2.54-2.96-4.1-2.55-6.48-2.2a6.5 6.5 0 0 0-2.01-6.52 6.5 6.5 0 0 0-6.73-1c-1.06-2.2-3.53-3.43-5.95-3.8a6 6 0 0 0-3.17.2c-1 .4-1.83 1.32-1.88 2.4a8.56 8.56 0 0 0-11.2-.17c-.55.5-.97 1.51-.3 1.84-.01 0-6.66-1.62-9.5 2.62-2.82 4.24-1.12 5.8-1.12 5.8s-6.92.27-6.78 3.94 1.27 4.24 1.27 4.24-6.01-1.72-7.3 1.8c-1.03 2.81 1.8 4.41 1.8 4.41S-.96 30.81.2 36.16c.76 3.48 3.4 4.1 3.4 4.1s-2.4 6.63-.14 9.03 5.22.42 5.22.42-1.41 7.2 1.84 8.33 5.36-2.12 5.36-2.12-2.12 6.07 2.54 5.5c4.66-.55 7.46-9.6 3.39-13.68 0 0 2.12-3.95 1.4-7.34C22.55 37 20 34.61 20 34.61s1.98-1.13 1.7-3.95c-.3-2.82-3.26-4.38-3.26-4.38s3.4-3.1 3.53-9.18" fill="#ff543d"/></g><clipPath id="clip-181c8200"><rect width="80" height="80" rx="0" ry="0"/></clipPath></defs><g clip-path="url(#clip-181c8200)"><rect width="80" height="80" fill="#e6eff8"/><path d="M19.07 30.47s1.57-20.23 21.59-20.23S62.3 30.55 62.3 30.55s9.43-.8 9.43 7.6c0 8.42-9.28 7.13-9.28 7.13S60.9 67.15 42.03 67.15c-21.11 0-23.4-20.8-23.4-20.8s-9 .72-9.93-6.25c-1.08-8.2 10.37-9.64 10.37-9.64" fill="#ffd6c0"/><path d="m64.3 39.49.46-.41.1-.09c.12-.1-.13.1-.02.02l.24-.17q.5-.35 1.06-.62l.26-.12.05-.02.05-.02.58-.21q.6-.18 1.2-.28c.52-.08.85-.76.7-1.23-.18-.56-.67-.8-1.23-.7a9.3 9.3 0 0 0-4.87 2.43c-.38.36-.4 1.06 0 1.4.4.36 1 .4 1.4 0zm-51.8-1.16.14.01c-.27-.02-.11-.01-.04 0l.3.05.52.14.28.09.12.05c.02 0 .22.09.06.02-.14-.1 0-.04.03-.03l.15.06.26.13.47.3.27.22q.47.38.83.83c.33.4 1.07.37 1.41 0 .4-.43.36-.98 0-1.4a7.3 7.3 0 0 0-4.84-2.53c-.52-.06-1.02.5-1 1 .03.59.44.94 1 1m18.3-1.9v4.54c0 .52.46 1.02 1 1s1-.44 1-1V36.4c0-.52-.46-1.02-1-1s-1 .44-1 1M49.2 36l-.15 4.81a1 1 0 0 0 1 1c.56-.02.98-.44 1-1l.15-4.8a1 1 0 0 0-1-1 1 1 0 0 0-1 1" fill="black"/><use transform="translate(27.82 26.75)" href="#mood-hopeful-181c8200"/><use transform="translate(3.78 2.95)" href="#hair-longCurls-181c8200"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{C as e,S as t,_ as n,a as r,b as i,c as a,d as o,f as s,g as c,h as l,i as u,l as d,m as f,n as p,o as m,p as h,r as g,s as _,t as v,u as y,v as b,w as x,x as S,y as C}from"./index-Br4CGoxh.js";var w=e(`mouse-pointer-2`,[[`path`,{d:`M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z`,key:`edeuup`}]]),T=e(`send`,[[`path`,{d:`M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z`,key:`1ffxy3`}],[`path`,{d:`m21.854 2.147-10.94 10.939`,key:`12cjpa`}]]),E=x();function D(e){return{"--reveal":e}}function O(e,t,n){let r=(e,t)=>Number.parseInt(e.slice(t,t+2),16),i=i=>Math.round(r(e,i)+(r(t,i)-r(e,i))*n).toString(16).padStart(2,`0`);return`#${i(1)}${i(3)}${i(5)}`}function k(e){let t=u((e-v.runEnd)/(v.executionComplete-v.runEnd));return t+t*t-t*t*t}function A({progress:e,children:t,className:n=``}){return(0,E.jsx)(`div`,{className:`scene-reveal ${n}`,style:D(e),children:t})}function j({progress:e,author:t,avatar:n,tone:r,timestamp:i,online:a=!1,assets:o,children:s}){return(0,E.jsxs)(`article`,{className:`scene-message`,style:D(e),children:[(0,E.jsx)(`span`,{className:`scene-avatar is-${r} ${n===`tutti`?`is-tutti`:``} ${a?`is-online`:``}`,"aria-hidden":`true`,children:n===`tutti`?(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:o.tuttiAvatarSrc,alt:``}):(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:o.humanAvatars[n],alt:``})}),(0,E.jsxs)(`div`,{className:`scene-message-copy`,children:[(0,E.jsxs)(`header`,{children:[(0,E.jsx)(`strong`,{children:t}),(0,E.jsx)(`span`,{children:i})]}),(0,E.jsx)(`p`,{children:s})]})]})}function M({time:e,assets:r}){let i=e>=v.artifactLive,u=e>=v.artifactClick,d=e>=v.runEnd&&e<v.executionComplete,f=a(e,v.runEnd,19.2),p=a(e,v.artifactLive,30.92);return(0,E.jsxs)(`aside`,{className:`scene-sidebar`,children:[(0,E.jsx)(`button`,{className:`scene-brand`,type:`button`,"aria-label":`Tutti`,tabIndex:-1,children:(0,E.jsx)(`img`,{src:r.logoSrc,alt:``})}),(0,E.jsxs)(`div`,{className:`scene-nav-stack`,children:[(0,E.jsxs)(`nav`,{className:`scene-nav`,"aria-label":`Workspace pages`,children:[(0,E.jsx)(`button`,{className:u?``:`is-active`,type:`button`,"aria-label":`Chat`,tabIndex:-1,children:(0,E.jsx)(c,{"aria-hidden":`true`})}),(0,E.jsx)(`button`,{type:`button`,"aria-label":`Worklist`,tabIndex:-1,children:(0,E.jsx)(b,{"aria-hidden":`true`})}),(0,E.jsxs)(`button`,{className:u?`is-active`:``,type:`button`,"aria-label":`Artifacts`,tabIndex:-1,children:[(0,E.jsx)(l,{"aria-hidden":`true`}),i?(0,E.jsx)(`span`,{className:`scene-live-pill`,style:D(p),children:`Live`}):null]}),(0,E.jsx)(`button`,{type:`button`,"aria-label":`References`,tabIndex:-1,children:(0,E.jsx)(C,{"aria-hidden":`true`})}),(0,E.jsx)(`button`,{type:`button`,"aria-label":`Skills`,tabIndex:-1,children:(0,E.jsx)(o,{"aria-hidden":`true`})}),(0,E.jsx)(`button`,{type:`button`,"aria-label":`Timeline`,tabIndex:-1,children:(0,E.jsx)(t,{"aria-hidden":`true`})})]}),d?(0,E.jsx)(`button`,{className:`scene-status-entry is-running`,style:D(f),type:`button`,"aria-label":`Task running`,tabIndex:-1,children:(0,E.jsx)(n,{"aria-hidden":`true`})}):null]}),(0,E.jsx)(`button`,{className:`scene-settings`,type:`button`,"aria-label":`Settings`,tabIndex:-1,children:(0,E.jsx)(s,{"aria-hidden":`true`})})]})}function N({time:e,assets:t}){let n=a(e,30.35,30.92);return(0,E.jsxs)(`section`,{className:`scene-panel scene-chat-panel`,children:[(0,E.jsxs)(`header`,{className:`scene-panel-header`,children:[(0,E.jsx)(`span`,{className:`scene-icon-tile`,children:(0,E.jsx)(c,{"aria-hidden":`true`})}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`h2`,{children:`Morrow Studio`}),(0,E.jsx)(`p`,{children:`Ceramics storefront`})]}),(0,E.jsxs)(`span`,{className:`scene-members`,"aria-hidden":`true`,children:[(0,E.jsx)(`i`,{className:`is-green`,children:(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:t.humanAvatars.fey,alt:``})}),(0,E.jsx)(`i`,{className:`is-blue`,children:(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:t.humanAvatars.avery,alt:``})}),(0,E.jsx)(`i`,{className:`is-yellow`,children:(0,E.jsx)(`img`,{className:`scene-avatar-image`,src:t.humanAvatars.jun,alt:``})})]})]}),(0,E.jsxs)(`div`,{className:`scene-chat-stream`,children:[(0,E.jsx)(j,{progress:a(e,1.35,2.05),author:`Fey`,avatar:`fey`,tone:`green`,timestamp:`10:12`,online:!0,assets:t,children:`🏺 Let's build an online shop for our ceramics studio.`}),(0,E.jsx)(j,{progress:a(e,2.65,3.35),author:`Avery`,avatar:`avery`,tone:`blue`,timestamp:`10:13`,online:!0,assets:t,children:`✨ Keep it warm, minimal, and editorial.`}),(0,E.jsx)(j,{progress:a(e,3.95,4.65),author:`Jun`,avatar:`jun`,tone:`yellow`,timestamp:`10:14`,online:!0,assets:t,children:`🎨 Let people preview every piece in different glazes.`}),(0,E.jsx)(j,{progress:a(e,5.25,5.95),author:`Tutti`,avatar:`tutti`,tone:`green`,timestamp:`10:15`,assets:t,children:`Got it — I'll put it together. ✨`}),(0,E.jsxs)(`article`,{className:`scene-task-result`,style:D(n),children:[(0,E.jsx)(`span`,{className:`scene-task-result-rail`,"aria-hidden":`true`}),(0,E.jsx)(`span`,{className:`scene-task-result-icon`,"aria-hidden":`true`,children:(0,E.jsx)(S,{})}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`span`,{children:`Task completed`}),(0,E.jsx)(`strong`,{children:`Build ceramics storefront`}),(0,E.jsx)(`small`,{children:`Storefront`}),(0,E.jsx)(`p`,{children:`Warm editorial shopping and glaze previews are ready in Artifacts.`})]}),(0,E.jsx)(i,{className:`scene-task-result-chevron`,"aria-hidden":`true`})]})]}),(0,E.jsxs)(`div`,{className:`scene-composer`,"aria-hidden":`true`,children:[(0,E.jsx)(`span`,{children:`Write a message`}),(0,E.jsx)(T,{})]})]})}function P({progress:e,icon:t,children:n}){return(0,E.jsxs)(`div`,{className:`scene-scratchpad-row`,style:D(e),children:[(0,E.jsx)(`span`,{"aria-hidden":`true`,children:t}),(0,E.jsx)(`p`,{children:n})]})}function F({time:e}){return(0,E.jsxs)(`section`,{className:`scene-panel scene-scratchpad-panel`,style:{"--scratchpad-collapse":a(e,v.runEnd,19.18)},children:[(0,E.jsxs)(`header`,{className:`scene-panel-header`,children:[(0,E.jsx)(`span`,{className:`scene-icon-tile`,children:(0,E.jsx)(h,{"aria-hidden":`true`})}),(0,E.jsx)(`h2`,{children:`Scratchpad`})]}),(0,E.jsxs)(`div`,{className:`scene-scratchpad-body`,children:[(0,E.jsxs)(A,{progress:a(e,8.95,10),className:`scene-scratchpad-intro`,children:[(0,E.jsx)(`h3`,{children:`Ceramics storefront`}),(0,E.jsx)(`p`,{children:`A warm, editorial storefront for a small-batch studio, centered on tactile product discovery.`})]}),(0,E.jsx)(A,{progress:a(e,9.85,10.65),className:`scene-scratchpad-label`,children:`Confirmed`}),(0,E.jsx)(P,{progress:a(e,10.4,11.25),icon:(0,E.jsx)(S,{}),children:`Product-first editorial layout with generous space`}),(0,E.jsx)(P,{progress:a(e,11.2,12.05),icon:(0,E.jsx)(S,{}),children:`Warm neutrals with quiet serif headlines`}),(0,E.jsx)(P,{progress:a(e,12,12.85),icon:(0,E.jsx)(S,{}),children:`Keep the collection small, curated, and story-led`}),(0,E.jsx)(A,{progress:a(e,12.75,13.55),className:`scene-scratchpad-label`,children:`Requested feature`}),(0,E.jsx)(P,{progress:a(e,13.3,14.2),icon:(0,E.jsx)(y,{}),children:`Preview every piece in clay, sage, and ink glazes`})]}),(0,E.jsxs)(`footer`,{className:`scene-scratchpad-footer`,style:D(a(e,15.7,16.8)),children:[(0,E.jsxs)(`span`,{className:`scene-writing-mark`,children:[(0,E.jsx)(`strong`,{children:`Updated`}),(0,E.jsx)(`span`,{children:`just now`})]}),(0,E.jsxs)(`button`,{className:`scene-run-button`,type:`button`,tabIndex:-1,children:[(0,E.jsx)(f,{"aria-hidden":`true`}),(0,E.jsx)(`span`,{children:`Run`})]})]})]})}function I(e,t){let n=Math.max(0,Math.floor((e-t)*50/5)*5);return`${Math.floor(n/60)}m${(n%60).toString().padStart(2,`0`)}s`}function L({time:e,scoreSrc:t}){let r=a(e,18.92,19.2),i=m(e),o=i.id===`complete`,s={prepare:v.runEnd,implement:v.executionPrepareEnd,validate:v.executionImplementEnd,update:v.executionValidateEnd,complete:v.executionComplete}[i.id],c=a(e,s,s+.32),l=k(e);return(0,E.jsx)(`div`,{className:`scene-execution`,style:{"--reveal":r,"--stage-reveal":c},children:(0,E.jsxs)(`article`,{className:`homepage-motion-panel homepage-motion-execution-panel homepage-motion-execution-card is-score-${o?`complete`:`running`}`,children:[(0,E.jsx)(`header`,{className:`homepage-motion-panel-header`,children:(0,E.jsxs)(`div`,{className:`homepage-motion-panel-title`,children:[(0,E.jsx)(`span`,{className:`scene-icon-tile`,"aria-hidden":`true`,children:(0,E.jsx)(d,{})}),(0,E.jsx)(`h3`,{children:`Execution progress`})]})}),(0,E.jsxs)(`div`,{className:`homepage-motion-execution-body`,children:[(0,E.jsxs)(`div`,{className:`homepage-motion-execution-step homepage-motion-execution-stage is-${o?`complete`:`running`} has-marker`,children:[(0,E.jsx)(`span`,{className:`homepage-motion-execution-marker ${o?`is-done`:`is-running`}`,"aria-hidden":`true`,children:o?(0,E.jsx)(S,{}):(0,E.jsx)(n,{})}),(0,E.jsx)(`span`,{className:`homepage-motion-execution-step-content homepage-motion-execution-stage-copy`,children:(0,E.jsxs)(`span`,{className:`homepage-motion-execution-step-title`,children:[(0,E.jsx)(`span`,{className:`homepage-motion-execution-step-label`,children:i.label}),o?null:(0,E.jsx)(`span`,{className:`homepage-motion-execution-step-elapsed`,children:` (${I(e,s)})`})]})},i.id)]}),(0,E.jsx)(`div`,{className:`homepage-motion-running-score ${o?`is-complete`:``}`,role:`img`,"aria-label":`Ode to Joy score phrase`,children:(0,E.jsx)(`div`,{className:`homepage-motion-score-passage`,style:{"--score-translate-x":`${-395.3*l}px`},"aria-hidden":`true`,children:(0,E.jsx)(`img`,{src:t,alt:``,draggable:!1})})})]})]})})}function R({color:e,variant:t=`vase`,className:n=``}){let r={"--ceramic-color":e};return t===`cup`?(0,E.jsxs)(`svg`,{className:`ceramic-object is-cup ${n}`,viewBox:`0 0 260 260`,style:r,"aria-hidden":`true`,children:[(0,E.jsx)(`path`,{className:`ceramic-main`,d:`M54 64h132l-9 126c-2 26-20 42-46 42h-22c-26 0-44-16-46-42Z`}),(0,E.jsx)(`path`,{className:`ceramic-outline`,d:`M186 92h18c34 0 34 72 1 76h-27`}),(0,E.jsx)(`ellipse`,{className:`ceramic-rim`,cx:`120`,cy:`64`,rx:`66`,ry:`13`}),(0,E.jsx)(`path`,{className:`ceramic-highlight`,d:`M78 90c5 62 4 91 20 114`})]}):t===`bowl`?(0,E.jsxs)(`svg`,{className:`ceramic-object is-bowl ${n}`,viewBox:`0 0 300 220`,style:r,"aria-hidden":`true`,children:[(0,E.jsx)(`ellipse`,{className:`ceramic-rim`,cx:`150`,cy:`55`,rx:`116`,ry:`24`}),(0,E.jsx)(`path`,{className:`ceramic-main`,d:`M34 55c8 82 45 132 116 132S258 137 266 55c-42 25-190 25-232 0Z`}),(0,E.jsx)(`path`,{className:`ceramic-highlight`,d:`M72 82c18 45 40 70 70 82`}),(0,E.jsx)(`path`,{className:`ceramic-base`,d:`M112 185h76`})]}):(0,E.jsxs)(`svg`,{className:`ceramic-object is-vase ${n}`,viewBox:`0 0 320 420`,style:r,"aria-hidden":`true`,children:[(0,E.jsx)(`path`,{className:`ceramic-main`,d:`M116 56c7 38-17 61-36 96-31 56-28 157 5 199 33 42 117 42 150 0 33-42 36-143 5-199-19-35-43-58-36-96Z`}),(0,E.jsx)(`ellipse`,{className:`ceramic-rim`,cx:`160`,cy:`56`,rx:`44`,ry:`12`}),(0,E.jsx)(`ellipse`,{className:`ceramic-base`,cx:`160`,cy:`365`,rx:`66`,ry:`13`}),(0,E.jsx)(`path`,{className:`ceramic-highlight`,d:`M108 153c-25 64-23 145 1 185`})]})}function z({time:e}){let t=a(e,v.artifactClick,33.25),n=a(e,34.25,34.52),r=a(e,34.88,35.15),i=a(e,35.55,35.82),o=p(e),s=O(`#d9cbb4`,`#c56f4f`,n);e>=34.88&&(s=O(`#c56f4f`,`#6f9275`,r)),e>=35.55&&(s=O(`#6f9275`,`#243a46`,i));let c=e>=35.55?`ink`:e>=34.88?`sage`:e>=34.25?`clay`:null,u={"--artifact-scroll":o};return(0,E.jsxs)(`section`,{className:`scene-panel scene-artifact-panel`,style:D(t),children:[(0,E.jsxs)(`header`,{className:`scene-panel-header`,children:[(0,E.jsx)(`span`,{className:`scene-icon-tile`,children:(0,E.jsx)(l,{"aria-hidden":`true`})}),(0,E.jsx)(`h2`,{children:`Artifacts`}),(0,E.jsx)(`span`,{className:`scene-ready-tag`,children:`Ready`})]}),(0,E.jsx)(`div`,{className:`scene-artifact-canvas`,children:(0,E.jsxs)(`div`,{className:`artifact-page-frame`,style:u,children:[(0,E.jsxs)(`div`,{className:`artifact-page-nav`,children:[(0,E.jsx)(`strong`,{children:`Morrow`}),(0,E.jsx)(`span`,{children:`Objects · Journal · Studio`})]}),(0,E.jsxs)(`section`,{className:`artifact-hero`,children:[(0,E.jsxs)(`div`,{className:`artifact-hero-copy`,children:[(0,E.jsx)(`span`,{className:`artifact-eyebrow`,children:`Hand-finished in small batches`}),(0,E.jsx)(`h3`,{children:`Objects for slower days.`}),(0,E.jsx)(`p`,{children:`Quiet forms, warm glazes, and useful pieces made to live with.`}),(0,E.jsx)(`button`,{type:`button`,tabIndex:-1,children:`Explore the collection`})]}),(0,E.jsxs)(`div`,{className:`artifact-hero-object`,children:[(0,E.jsx)(R,{color:s}),(0,E.jsxs)(`div`,{className:`artifact-glaze-picker`,"aria-label":`Glaze preview`,children:[(0,E.jsx)(`span`,{children:`Glaze`}),(0,E.jsx)(`i`,{className:c===`clay`?`is-active is-clay`:`is-clay`}),(0,E.jsx)(`i`,{className:c===`sage`?`is-active is-sage`:`is-sage`}),(0,E.jsx)(`i`,{className:c===`ink`?`is-active is-ink`:`is-ink`})]})]})]}),(0,E.jsxs)(`section`,{className:`artifact-collection`,children:[(0,E.jsxs)(`header`,{children:[(0,E.jsx)(`span`,{children:`Selected pieces`}),(0,E.jsx)(`p`,{children:`Everyday forms shaped for the rituals around them.`})]}),(0,E.jsxs)(`div`,{className:`artifact-product-grid`,children:[(0,E.jsxs)(`article`,{children:[(0,E.jsx)(R,{color:`#b9785f`,variant:`cup`}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`strong`,{children:`Low cup`}),(0,E.jsx)(`span`,{children:`Rust glaze`})]})]}),(0,E.jsxs)(`article`,{children:[(0,E.jsx)(R,{color:`#819283`,variant:`vase`}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`strong`,{children:`Field vase`}),(0,E.jsx)(`span`,{children:`Sage glaze`})]})]}),(0,E.jsxs)(`article`,{children:[(0,E.jsx)(R,{color:`#d5c7af`,variant:`bowl`}),(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`strong`,{children:`Gather bowl`}),(0,E.jsx)(`span`,{children:`Flax glaze`})]})]})]})]}),(0,E.jsxs)(`section`,{className:`artifact-studio`,children:[(0,E.jsx)(`span`,{children:`Made by hand · Meant for every day`}),(0,E.jsx)(`h3`,{children:`Useful things can still feel special.`}),(0,E.jsx)(`p`,{children:`We make a small number of considered objects, slowly and close to home.`}),(0,E.jsx)(`button`,{type:`button`,tabIndex:-1,children:`Visit the studio`})]}),(0,E.jsxs)(`footer`,{className:`artifact-page-footer`,children:[(0,E.jsx)(`strong`,{children:`Morrow Ceramics`}),(0,E.jsx)(`span`,{className:`artifact-built-with`,children:`Built with Tutti.`}),(0,E.jsx)(`span`,{children:`Small batch · Est. 2026`})]})]})})]})}function B(e,t,n){return e+(t-e)*n}function V(e,t,n,i,o){let s=a(e,t,n,r);return{x:B(i.x,o.x,s),y:B(i.y,o.y,s)}}function H(e,t,n){return e<t||e>n?0:Math.sin(Math.PI*((e-t)/(n-t)))}function U(e){if(e>=v.runCursorStart&&e<18.92){let t=V(e,v.runCursorStart,v.runCursorArrive,{x:86,y:76},{x:94.25,y:94.2});return{visible:!0,x:t.x,y:t.y,clickPulse:H(e,v.runClickStart,v.runEnd)}}if(e>=31&&e<33.45){let t=V(e,31,32.22,{x:50,y:58},{x:4,y:22.85});return{visible:!0,x:t.x,y:t.y,clickPulse:H(e,32.28,32.6)}}if(e>=33.45&&e<35.95){let t={x:87.27,y:66.35},n={x:88.95,y:66.35},r={x:90.64,y:66.35},i;return i=e<34.15?V(e,33.45,34.15,{x:4,y:22.85},t):e<34.78?V(e,34.5,34.78,t,n):e<35.45?V(e,35.15,35.45,n,r):r,{visible:!0,x:i.x,y:i.y,clickPulse:Math.max(H(e,34.15,34.4),H(e,34.78,35.03),H(e,35.45,35.7))}}return{visible:!1,x:50,y:50,clickPulse:0}}function W({time:e,assets:t}){let n=g(e),i=U(e),o=_(e),s=a(e,.08,1.05,r);return(0,E.jsx)(`div`,{className:`motion-scene`,role:`img`,"aria-label":`Tutti workflow animation from team discussion to a generated ceramics storefront`,style:{opacity:o,visibility:o<=.001?`hidden`:`visible`},children:(0,E.jsxs)(`div`,{className:`motion-stage`,style:{opacity:s,transform:`translate(50%, 50%) scale(${n.scale}) translate(${-n.centerX*100}%, ${-n.centerY*100}%)`},children:[(0,E.jsx)(M,{time:e,assets:t}),(0,E.jsxs)(`div`,{className:`scene-workspace-layer`,children:[(0,E.jsx)(N,{time:e,assets:t}),(0,E.jsx)(F,{time:e}),(0,E.jsx)(L,{time:e,scoreSrc:t.scoreSrc})]}),(0,E.jsx)(z,{time:e}),(0,E.jsxs)(`span`,{className:`scene-cursor ${i.visible?`is-visible`:``}`,style:{left:`${i.x}%`,top:`${i.y}%`,"--click-pulse":i.clickPulse},"aria-hidden":`true`,children:[(0,E.jsx)(w,{}),(0,E.jsx)(`i`,{})]})]})})}export{W as HomepageMotionScene};
|