rusty-replay 0.0.4
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/.eslintrc.js +10 -0
- package/.vscode/settings.json +3 -0
- package/README.md +92 -0
- package/apps/web/README.md +11 -0
- package/apps/web/api/auth/keys.ts +3 -0
- package/apps/web/api/auth/types.ts +25 -0
- package/apps/web/api/auth/use-query-profile.ts +19 -0
- package/apps/web/api/auth/use-sign-in.ts +24 -0
- package/apps/web/api/axios.ts +122 -0
- package/apps/web/api/error-code.ts +36 -0
- package/apps/web/api/event/keys.ts +14 -0
- package/apps/web/api/event/types.ts +91 -0
- package/apps/web/api/event/use-mutation-event-assignee.ts +103 -0
- package/apps/web/api/event/use-mutation-event-priority.ts +97 -0
- package/apps/web/api/event/use-mutation-event-status.ts +198 -0
- package/apps/web/api/event/use-query-event-detail.ts +25 -0
- package/apps/web/api/event/use-query-event-list.ts +42 -0
- package/apps/web/api/health-check/index.ts +21 -0
- package/apps/web/api/project/keys.ts +4 -0
- package/apps/web/api/project/types.ts +28 -0
- package/apps/web/api/project/use-create-project.ts +30 -0
- package/apps/web/api/project/use-query-project-list.ts +19 -0
- package/apps/web/api/project/use-query-project-users.ts +23 -0
- package/apps/web/api/types.ts +44 -0
- package/apps/web/app/(auth)/layout.tsx +5 -0
- package/apps/web/app/(auth)/sign-in/page.tsx +20 -0
- package/apps/web/app/(auth)/sign-up/page.tsx +5 -0
- package/apps/web/app/(project)/project/[project_id]/issues/[issue_id]/page.tsx +17 -0
- package/apps/web/app/(project)/project/[project_id]/issues/page.tsx +15 -0
- package/apps/web/app/(project)/project/[project_id]/page.tsx +10 -0
- package/apps/web/app/(project)/project/page.tsx +10 -0
- package/apps/web/app/(report)/error-list/page.tsx +7 -0
- package/apps/web/app/favicon.ico +0 -0
- package/apps/web/app/layout.tsx +35 -0
- package/apps/web/app/page.tsx +3 -0
- package/apps/web/components/.gitkeep +0 -0
- package/apps/web/components/event-list/event-detail.tsx +242 -0
- package/apps/web/components/event-list/event-list.tsx +376 -0
- package/apps/web/components/event-list/preview.tsx +573 -0
- package/apps/web/components/layouts/default-layout.tsx +59 -0
- package/apps/web/components/login-form.tsx +124 -0
- package/apps/web/components/project/create-project.tsx +130 -0
- package/apps/web/components/project/hooks/use-get-event-params.ts +9 -0
- package/apps/web/components/project/hooks/use-get-project-params.ts +10 -0
- package/apps/web/components/project/project-detail.tsx +240 -0
- package/apps/web/components/project/project-list.tsx +137 -0
- package/apps/web/components/providers.tsx +25 -0
- package/apps/web/components/ui/assignee-dropdown.tsx +176 -0
- package/apps/web/components/ui/event-status-dropdown.tsx +104 -0
- package/apps/web/components/ui/priority-dropdown.tsx +123 -0
- package/apps/web/components/widget/app-sidebar.tsx +225 -0
- package/apps/web/components/widget/nav-main.tsx +73 -0
- package/apps/web/components/widget/nav-projects.tsx +84 -0
- package/apps/web/components/widget/nav-user.tsx +113 -0
- package/apps/web/components.json +20 -0
- package/apps/web/constants/routes.ts +12 -0
- package/apps/web/eslint.config.js +4 -0
- package/apps/web/hooks/use-boolean-state.ts +13 -0
- package/apps/web/lib/.gitkeep +0 -0
- package/apps/web/next-env.d.ts +5 -0
- package/apps/web/next.config.mjs +6 -0
- package/apps/web/package.json +60 -0
- package/apps/web/postcss.config.mjs +1 -0
- package/apps/web/providers/flag-provider.tsx +35 -0
- package/apps/web/providers/query-client-provider.tsx +17 -0
- package/apps/web/providers/telemetry-provider.tsx +12 -0
- package/apps/web/tsconfig.json +24 -0
- package/apps/web/utils/avatar.ts +26 -0
- package/apps/web/utils/date.ts +26 -0
- package/apps/web/utils/front-end-tracer.ts +119 -0
- package/apps/web/utils/schema/project.schema.ts +12 -0
- package/apps/web/utils/span-processor.ts +36 -0
- package/package.json +21 -0
- package/packages/eslint-config/README.md +3 -0
- package/packages/eslint-config/base.js +32 -0
- package/packages/eslint-config/next.js +51 -0
- package/packages/eslint-config/package.json +25 -0
- package/packages/eslint-config/react-internal.js +41 -0
- package/packages/rusty-replay/README.md +165 -0
- package/packages/rusty-replay/package.json +67 -0
- package/packages/rusty-replay/src/environment.ts +27 -0
- package/packages/rusty-replay/src/error-batcher.ts +75 -0
- package/packages/rusty-replay/src/front-end-tracer.ts +86 -0
- package/packages/rusty-replay/src/handler.ts +37 -0
- package/packages/rusty-replay/src/index.ts +8 -0
- package/packages/rusty-replay/src/recorder.ts +71 -0
- package/packages/rusty-replay/src/reporter.ts +115 -0
- package/packages/rusty-replay/src/utils.ts +13 -0
- package/packages/rusty-replay/tsconfig.build.json +13 -0
- package/packages/rusty-replay/tsconfig.json +27 -0
- package/packages/rusty-replay/tsup.config.ts +39 -0
- package/packages/typescript-config/README.md +3 -0
- package/packages/typescript-config/base.json +20 -0
- package/packages/typescript-config/nextjs.json +13 -0
- package/packages/typescript-config/package.json +9 -0
- package/packages/typescript-config/react-library.json +8 -0
- package/packages/ui/components.json +20 -0
- package/packages/ui/eslint.config.js +4 -0
- package/packages/ui/package.json +60 -0
- package/packages/ui/postcss.config.mjs +6 -0
- package/packages/ui/src/components/.gitkeep +0 -0
- package/packages/ui/src/components/avatar.tsx +53 -0
- package/packages/ui/src/components/badge.tsx +46 -0
- package/packages/ui/src/components/breadcrumb.tsx +109 -0
- package/packages/ui/src/components/button.tsx +59 -0
- package/packages/ui/src/components/calendar.tsx +75 -0
- package/packages/ui/src/components/calendars/date-picker.tsx +43 -0
- package/packages/ui/src/components/calendars/date-range-picker.tsx +79 -0
- package/packages/ui/src/components/card.tsx +92 -0
- package/packages/ui/src/components/checkbox.tsx +32 -0
- package/packages/ui/src/components/collapsible.tsx +33 -0
- package/packages/ui/src/components/dialog.tsx +135 -0
- package/packages/ui/src/components/dialogs/confirmation-modal.tsx +216 -0
- package/packages/ui/src/components/dropdown-menu.tsx +261 -0
- package/packages/ui/src/components/input.tsx +30 -0
- package/packages/ui/src/components/label.tsx +24 -0
- package/packages/ui/src/components/login-form.tsx +68 -0
- package/packages/ui/src/components/mode-switcher.tsx +34 -0
- package/packages/ui/src/components/popover.tsx +48 -0
- package/packages/ui/src/components/scroll-area.tsx +58 -0
- package/packages/ui/src/components/select.tsx +185 -0
- package/packages/ui/src/components/separator.tsx +28 -0
- package/packages/ui/src/components/sheet.tsx +139 -0
- package/packages/ui/src/components/sidebar.tsx +726 -0
- package/packages/ui/src/components/skeleton.tsx +13 -0
- package/packages/ui/src/components/sonner.tsx +25 -0
- package/packages/ui/src/components/table.tsx +116 -0
- package/packages/ui/src/components/tabs.tsx +66 -0
- package/packages/ui/src/components/team-switcher.tsx +91 -0
- package/packages/ui/src/components/textarea.tsx +18 -0
- package/packages/ui/src/components/tooltip.tsx +61 -0
- package/packages/ui/src/hooks/.gitkeep +0 -0
- package/packages/ui/src/hooks/use-meta-color.ts +28 -0
- package/packages/ui/src/hooks/use-mobile.ts +19 -0
- package/packages/ui/src/lib/utils.ts +6 -0
- package/packages/ui/src/styles/globals.css +138 -0
- package/packages/ui/tsconfig.json +13 -0
- package/packages/ui/tsconfig.lint.json +8 -0
- package/pnpm-workspace.yaml +4 -0
- package/tsconfig.json +4 -0
- package/turbo.json +21 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
import { getBrowserInfo, getEnvironment } from './environment';
|
2
|
+
import { ErrorBatcher } from './error-batcher';
|
3
|
+
import {
|
4
|
+
startRecording,
|
5
|
+
getRecordedEvents,
|
6
|
+
getCurrentEvents,
|
7
|
+
clearEvents,
|
8
|
+
} from './recorder';
|
9
|
+
import { compressToBase64 } from './utils';
|
10
|
+
|
11
|
+
export interface AdditionalInfo {
|
12
|
+
pageUrl: string;
|
13
|
+
request: {
|
14
|
+
url: string;
|
15
|
+
method: string;
|
16
|
+
headers: Record<string, string>;
|
17
|
+
};
|
18
|
+
response: {
|
19
|
+
data: {
|
20
|
+
message: string;
|
21
|
+
errorCode: string;
|
22
|
+
};
|
23
|
+
status: number;
|
24
|
+
statusText: string;
|
25
|
+
};
|
26
|
+
}
|
27
|
+
|
28
|
+
export interface BatchedEvent {
|
29
|
+
id: string;
|
30
|
+
timestamp: string;
|
31
|
+
message: string;
|
32
|
+
stacktrace: string;
|
33
|
+
replay: string | null;
|
34
|
+
environment: string;
|
35
|
+
browser: string;
|
36
|
+
os: string;
|
37
|
+
userAgent: string;
|
38
|
+
userId?: number;
|
39
|
+
additionalInfo?: Partial<AdditionalInfo>;
|
40
|
+
appVersion: string;
|
41
|
+
apiKey: string;
|
42
|
+
}
|
43
|
+
|
44
|
+
export interface BatcherOptions {
|
45
|
+
endpoint: string;
|
46
|
+
apiKey: string;
|
47
|
+
flushIntervalMs?: number;
|
48
|
+
maxBufferSize?: number;
|
49
|
+
}
|
50
|
+
|
51
|
+
export interface InitOptions {
|
52
|
+
endpoint: string;
|
53
|
+
apiKey: string;
|
54
|
+
flushIntervalMs?: number;
|
55
|
+
maxBufferSize?: number;
|
56
|
+
beforeErrorSec?: number;
|
57
|
+
}
|
58
|
+
|
59
|
+
let batcher: ErrorBatcher;
|
60
|
+
let globalOpts: { beforeErrorSec: number } = { beforeErrorSec: 30 };
|
61
|
+
|
62
|
+
export function init(options: InitOptions) {
|
63
|
+
globalOpts.beforeErrorSec = options.beforeErrorSec ?? 10;
|
64
|
+
|
65
|
+
batcher = new ErrorBatcher({
|
66
|
+
endpoint: options.endpoint,
|
67
|
+
apiKey: options.apiKey,
|
68
|
+
flushIntervalMs: options.flushIntervalMs,
|
69
|
+
maxBufferSize: options.maxBufferSize,
|
70
|
+
});
|
71
|
+
|
72
|
+
if (typeof window !== 'undefined') {
|
73
|
+
if (document.readyState === 'complete') {
|
74
|
+
requestAnimationFrame(() => startRecording());
|
75
|
+
} else {
|
76
|
+
window.addEventListener('load', () => {
|
77
|
+
requestAnimationFrame(() => startRecording());
|
78
|
+
});
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
export function captureException(
|
84
|
+
error: Error,
|
85
|
+
additionalInfo?: Partial<AdditionalInfo>,
|
86
|
+
userId?: number
|
87
|
+
): string {
|
88
|
+
const errorTime = Date.now();
|
89
|
+
const eventsSnapshot = getCurrentEvents();
|
90
|
+
const rawReplay = getRecordedEvents(
|
91
|
+
globalOpts.beforeErrorSec,
|
92
|
+
errorTime,
|
93
|
+
eventsSnapshot
|
94
|
+
);
|
95
|
+
|
96
|
+
clearEvents();
|
97
|
+
|
98
|
+
const { browser, os, userAgent } = getBrowserInfo();
|
99
|
+
|
100
|
+
const compressedReplay = compressToBase64(rawReplay);
|
101
|
+
|
102
|
+
return batcher.capture({
|
103
|
+
message: error.message ?? '',
|
104
|
+
stacktrace: error.stack ?? '',
|
105
|
+
replay: compressedReplay as any,
|
106
|
+
environment: getEnvironment(),
|
107
|
+
browser,
|
108
|
+
os,
|
109
|
+
userAgent,
|
110
|
+
userId,
|
111
|
+
additionalInfo,
|
112
|
+
appVersion: '1.0.0',
|
113
|
+
apiKey: batcher.getApiKey(),
|
114
|
+
});
|
115
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { zlibSync, unzlibSync } from 'fflate';
|
2
|
+
|
3
|
+
export function compressToBase64(obj: any): string {
|
4
|
+
const json = JSON.stringify(obj);
|
5
|
+
const compressed = zlibSync(new TextEncoder().encode(json));
|
6
|
+
return btoa(String.fromCharCode(...compressed));
|
7
|
+
}
|
8
|
+
|
9
|
+
export function decompressFromBase64(base64: string): any[] {
|
10
|
+
const binary = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
11
|
+
const json = new TextDecoder().decode(unzlibSync(binary));
|
12
|
+
return JSON.parse(json);
|
13
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
{
|
2
|
+
"extends": "./tsconfig.json",
|
3
|
+
"compilerOptions": {
|
4
|
+
"noEmit": false,
|
5
|
+
"incremental": false,
|
6
|
+
"declaration": true,
|
7
|
+
"declarationMap": true,
|
8
|
+
"emitDeclarationOnly": true,
|
9
|
+
"tsBuildInfoFile": "./dist/.tsbuildinfo"
|
10
|
+
},
|
11
|
+
"include": ["src/**/*"],
|
12
|
+
"exclude": ["node_modules"]
|
13
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"target": "ES2017",
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
5
|
+
"allowJs": true,
|
6
|
+
"skipLibCheck": true,
|
7
|
+
"strict": true,
|
8
|
+
"noEmit": true,
|
9
|
+
"esModuleInterop": true,
|
10
|
+
"module": "esnext",
|
11
|
+
"moduleResolution": "bundler",
|
12
|
+
"resolveJsonModule": true,
|
13
|
+
"isolatedModules": true,
|
14
|
+
"jsx": "preserve",
|
15
|
+
"incremental": true,
|
16
|
+
"plugins": [
|
17
|
+
{
|
18
|
+
"name": "next"
|
19
|
+
}
|
20
|
+
],
|
21
|
+
"paths": {
|
22
|
+
"@/*": ["./src/*"]
|
23
|
+
}
|
24
|
+
},
|
25
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
26
|
+
"exclude": ["node_modules"]
|
27
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import { defineConfig } from 'tsup';
|
2
|
+
import pkg from './package.json';
|
3
|
+
|
4
|
+
export default defineConfig({
|
5
|
+
entry: ['src/index.ts'],
|
6
|
+
tsconfig: 'tsconfig.build.json',
|
7
|
+
outDir: 'dist',
|
8
|
+
format: ['cjs', 'esm'],
|
9
|
+
dts: true,
|
10
|
+
sourcemap: true,
|
11
|
+
platform: 'browser',
|
12
|
+
clean: true,
|
13
|
+
target: 'es2017',
|
14
|
+
splitting: false,
|
15
|
+
shims: true,
|
16
|
+
external: [
|
17
|
+
'react',
|
18
|
+
'rrweb',
|
19
|
+
'rrweb-player',
|
20
|
+
'@rrweb/types',
|
21
|
+
'axios',
|
22
|
+
'require-in-the-middle',
|
23
|
+
'import-in-the-middle',
|
24
|
+
'@opentelemetry/instrumentation',
|
25
|
+
'@opentelemetry/context-zone',
|
26
|
+
'@opentelemetry/core',
|
27
|
+
'@opentelemetry/sdk-trace-web',
|
28
|
+
'@opentelemetry/sdk-trace-base',
|
29
|
+
'@opentelemetry/instrumentation-fetch',
|
30
|
+
'@opentelemetry/instrumentation-xml-http-request',
|
31
|
+
'@opentelemetry/exporter-trace-otlp-proto',
|
32
|
+
'@opentelemetry/resources',
|
33
|
+
'@opentelemetry/semantic-conventions',
|
34
|
+
],
|
35
|
+
// external: [
|
36
|
+
// ...Object.keys(pkg.peerDependencies ?? {}),
|
37
|
+
// 'axios',
|
38
|
+
// ],
|
39
|
+
});
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
3
|
+
"display": "Default",
|
4
|
+
"compilerOptions": {
|
5
|
+
"declaration": true,
|
6
|
+
"declarationMap": true,
|
7
|
+
"esModuleInterop": true,
|
8
|
+
"incremental": false,
|
9
|
+
"isolatedModules": true,
|
10
|
+
"lib": ["es2022", "DOM", "DOM.Iterable"],
|
11
|
+
"module": "NodeNext",
|
12
|
+
"moduleDetection": "force",
|
13
|
+
"moduleResolution": "NodeNext",
|
14
|
+
"noUncheckedIndexedAccess": true,
|
15
|
+
"resolveJsonModule": true,
|
16
|
+
"skipLibCheck": true,
|
17
|
+
"strict": true,
|
18
|
+
"target": "ES2022"
|
19
|
+
}
|
20
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
3
|
+
"display": "Next.js",
|
4
|
+
"extends": "./base.json",
|
5
|
+
"compilerOptions": {
|
6
|
+
"plugins": [{ "name": "next" }],
|
7
|
+
"module": "ESNext",
|
8
|
+
"moduleResolution": "Bundler",
|
9
|
+
"allowJs": true,
|
10
|
+
"jsx": "preserve",
|
11
|
+
"noEmit": true
|
12
|
+
}
|
13
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
3
|
+
"style": "new-york",
|
4
|
+
"rsc": true,
|
5
|
+
"tsx": true,
|
6
|
+
"tailwind": {
|
7
|
+
"config": "",
|
8
|
+
"css": "src/styles/globals.css",
|
9
|
+
"baseColor": "neutral",
|
10
|
+
"cssVariables": true
|
11
|
+
},
|
12
|
+
"iconLibrary": "lucide",
|
13
|
+
"aliases": {
|
14
|
+
"components": "@workspace/ui/components",
|
15
|
+
"utils": "@workspace/ui/lib/utils",
|
16
|
+
"hooks": "@workspace/ui/hooks",
|
17
|
+
"lib": "@workspace/ui/lib",
|
18
|
+
"ui": "@workspace/ui/components"
|
19
|
+
}
|
20
|
+
}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
{
|
2
|
+
"name": "@workspace/ui",
|
3
|
+
"version": "0.0.0",
|
4
|
+
"type": "module",
|
5
|
+
"private": true,
|
6
|
+
"scripts": {
|
7
|
+
"lint": "eslint . --max-warnings 0",
|
8
|
+
"typecheck": "tsc --noEmit"
|
9
|
+
},
|
10
|
+
"dependencies": {
|
11
|
+
"@radix-ui/react-avatar": "^1.1.6",
|
12
|
+
"@radix-ui/react-checkbox": "^1.3.0",
|
13
|
+
"@radix-ui/react-collapsible": "^1.1.7",
|
14
|
+
"@radix-ui/react-dialog": "^1.1.10",
|
15
|
+
"@radix-ui/react-dropdown-menu": "^2.1.10",
|
16
|
+
"@radix-ui/react-label": "^2.1.4",
|
17
|
+
"@radix-ui/react-popover": "^1.1.11",
|
18
|
+
"@radix-ui/react-scroll-area": "^1.2.6",
|
19
|
+
"@radix-ui/react-select": "^2.2.2",
|
20
|
+
"@radix-ui/react-separator": "^1.1.4",
|
21
|
+
"@radix-ui/react-slot": "^1.1.2",
|
22
|
+
"@radix-ui/react-tabs": "^1.1.8",
|
23
|
+
"@radix-ui/react-tooltip": "^1.2.3",
|
24
|
+
"add": "^2.0.6",
|
25
|
+
"class-variance-authority": "^0.7.1",
|
26
|
+
"clsx": "^2.1.1",
|
27
|
+
"date-fns": "^4.1.0",
|
28
|
+
"dlx": "^0.2.1",
|
29
|
+
"lucide-react": "^0.475.0",
|
30
|
+
"next-themes": "^0.4.4",
|
31
|
+
"pagination": "^0.4.6",
|
32
|
+
"pnpm": "^10.10.0",
|
33
|
+
"react": "^19.0.0",
|
34
|
+
"react-day-picker": "8.10.1",
|
35
|
+
"react-dom": "^19.0.0",
|
36
|
+
"shadcn": "2.6.0-canary.2",
|
37
|
+
"sonner": "^2.0.3",
|
38
|
+
"tailwind-merge": "^3.0.1",
|
39
|
+
"tw-animate-css": "^1.2.4",
|
40
|
+
"zod": "^3.24.2"
|
41
|
+
},
|
42
|
+
"devDependencies": {
|
43
|
+
"@tailwindcss/postcss": "^4.0.8",
|
44
|
+
"@turbo/gen": "^2.4.2",
|
45
|
+
"@types/node": "^20",
|
46
|
+
"@types/react": "^19",
|
47
|
+
"@types/react-dom": "^19",
|
48
|
+
"@workspace/eslint-config": "workspace:*",
|
49
|
+
"@workspace/typescript-config": "workspace:*",
|
50
|
+
"tailwindcss": "^4.0.8",
|
51
|
+
"typescript": "^5.7.3"
|
52
|
+
},
|
53
|
+
"exports": {
|
54
|
+
"./globals.css": "./src/styles/globals.css",
|
55
|
+
"./postcss.config": "./postcss.config.mjs",
|
56
|
+
"./lib/*": "./src/lib/*.ts",
|
57
|
+
"./components/*": "./src/components/*.tsx",
|
58
|
+
"./hooks/*": "./src/hooks/*.ts"
|
59
|
+
}
|
60
|
+
}
|
File without changes
|
@@ -0,0 +1,53 @@
|
|
1
|
+
"use client"
|
2
|
+
|
3
|
+
import * as React from "react"
|
4
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
5
|
+
|
6
|
+
import { cn } from "@workspace/ui/lib/utils"
|
7
|
+
|
8
|
+
function Avatar({
|
9
|
+
className,
|
10
|
+
...props
|
11
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
12
|
+
return (
|
13
|
+
<AvatarPrimitive.Root
|
14
|
+
data-slot="avatar"
|
15
|
+
className={cn(
|
16
|
+
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
17
|
+
className
|
18
|
+
)}
|
19
|
+
{...props}
|
20
|
+
/>
|
21
|
+
)
|
22
|
+
}
|
23
|
+
|
24
|
+
function AvatarImage({
|
25
|
+
className,
|
26
|
+
...props
|
27
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
28
|
+
return (
|
29
|
+
<AvatarPrimitive.Image
|
30
|
+
data-slot="avatar-image"
|
31
|
+
className={cn("aspect-square size-full", className)}
|
32
|
+
{...props}
|
33
|
+
/>
|
34
|
+
)
|
35
|
+
}
|
36
|
+
|
37
|
+
function AvatarFallback({
|
38
|
+
className,
|
39
|
+
...props
|
40
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
41
|
+
return (
|
42
|
+
<AvatarPrimitive.Fallback
|
43
|
+
data-slot="avatar-fallback"
|
44
|
+
className={cn(
|
45
|
+
"bg-muted flex size-full items-center justify-center rounded-full",
|
46
|
+
className
|
47
|
+
)}
|
48
|
+
{...props}
|
49
|
+
/>
|
50
|
+
)
|
51
|
+
}
|
52
|
+
|
53
|
+
export { Avatar, AvatarImage, AvatarFallback }
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import * as React from "react"
|
2
|
+
import { Slot } from "@radix-ui/react-slot"
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
4
|
+
|
5
|
+
import { cn } from "@workspace/ui/lib/utils"
|
6
|
+
|
7
|
+
const badgeVariants = cva(
|
8
|
+
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
9
|
+
{
|
10
|
+
variants: {
|
11
|
+
variant: {
|
12
|
+
default:
|
13
|
+
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
14
|
+
secondary:
|
15
|
+
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
16
|
+
destructive:
|
17
|
+
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
18
|
+
outline:
|
19
|
+
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
20
|
+
},
|
21
|
+
},
|
22
|
+
defaultVariants: {
|
23
|
+
variant: "default",
|
24
|
+
},
|
25
|
+
}
|
26
|
+
)
|
27
|
+
|
28
|
+
function Badge({
|
29
|
+
className,
|
30
|
+
variant,
|
31
|
+
asChild = false,
|
32
|
+
...props
|
33
|
+
}: React.ComponentProps<"span"> &
|
34
|
+
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
35
|
+
const Comp = asChild ? Slot : "span"
|
36
|
+
|
37
|
+
return (
|
38
|
+
<Comp
|
39
|
+
data-slot="badge"
|
40
|
+
className={cn(badgeVariants({ variant }), className)}
|
41
|
+
{...props}
|
42
|
+
/>
|
43
|
+
)
|
44
|
+
}
|
45
|
+
|
46
|
+
export { Badge, badgeVariants }
|
@@ -0,0 +1,109 @@
|
|
1
|
+
import * as React from "react"
|
2
|
+
import { Slot } from "@radix-ui/react-slot"
|
3
|
+
import { ChevronRight, MoreHorizontal } from "lucide-react"
|
4
|
+
|
5
|
+
import { cn } from "@workspace/ui/lib/utils"
|
6
|
+
|
7
|
+
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
|
8
|
+
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />
|
9
|
+
}
|
10
|
+
|
11
|
+
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
12
|
+
return (
|
13
|
+
<ol
|
14
|
+
data-slot="breadcrumb-list"
|
15
|
+
className={cn(
|
16
|
+
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
|
17
|
+
className
|
18
|
+
)}
|
19
|
+
{...props}
|
20
|
+
/>
|
21
|
+
)
|
22
|
+
}
|
23
|
+
|
24
|
+
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
25
|
+
return (
|
26
|
+
<li
|
27
|
+
data-slot="breadcrumb-item"
|
28
|
+
className={cn("inline-flex items-center gap-1.5", className)}
|
29
|
+
{...props}
|
30
|
+
/>
|
31
|
+
)
|
32
|
+
}
|
33
|
+
|
34
|
+
function BreadcrumbLink({
|
35
|
+
asChild,
|
36
|
+
className,
|
37
|
+
...props
|
38
|
+
}: React.ComponentProps<"a"> & {
|
39
|
+
asChild?: boolean
|
40
|
+
}) {
|
41
|
+
const Comp = asChild ? Slot : "a"
|
42
|
+
|
43
|
+
return (
|
44
|
+
<Comp
|
45
|
+
data-slot="breadcrumb-link"
|
46
|
+
className={cn("hover:text-foreground transition-colors", className)}
|
47
|
+
{...props}
|
48
|
+
/>
|
49
|
+
)
|
50
|
+
}
|
51
|
+
|
52
|
+
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
53
|
+
return (
|
54
|
+
<span
|
55
|
+
data-slot="breadcrumb-page"
|
56
|
+
role="link"
|
57
|
+
aria-disabled="true"
|
58
|
+
aria-current="page"
|
59
|
+
className={cn("text-foreground font-normal", className)}
|
60
|
+
{...props}
|
61
|
+
/>
|
62
|
+
)
|
63
|
+
}
|
64
|
+
|
65
|
+
function BreadcrumbSeparator({
|
66
|
+
children,
|
67
|
+
className,
|
68
|
+
...props
|
69
|
+
}: React.ComponentProps<"li">) {
|
70
|
+
return (
|
71
|
+
<li
|
72
|
+
data-slot="breadcrumb-separator"
|
73
|
+
role="presentation"
|
74
|
+
aria-hidden="true"
|
75
|
+
className={cn("[&>svg]:size-3.5", className)}
|
76
|
+
{...props}
|
77
|
+
>
|
78
|
+
{children ?? <ChevronRight />}
|
79
|
+
</li>
|
80
|
+
)
|
81
|
+
}
|
82
|
+
|
83
|
+
function BreadcrumbEllipsis({
|
84
|
+
className,
|
85
|
+
...props
|
86
|
+
}: React.ComponentProps<"span">) {
|
87
|
+
return (
|
88
|
+
<span
|
89
|
+
data-slot="breadcrumb-ellipsis"
|
90
|
+
role="presentation"
|
91
|
+
aria-hidden="true"
|
92
|
+
className={cn("flex size-9 items-center justify-center", className)}
|
93
|
+
{...props}
|
94
|
+
>
|
95
|
+
<MoreHorizontal className="size-4" />
|
96
|
+
<span className="sr-only">More</span>
|
97
|
+
</span>
|
98
|
+
)
|
99
|
+
}
|
100
|
+
|
101
|
+
export {
|
102
|
+
Breadcrumb,
|
103
|
+
BreadcrumbList,
|
104
|
+
BreadcrumbItem,
|
105
|
+
BreadcrumbLink,
|
106
|
+
BreadcrumbPage,
|
107
|
+
BreadcrumbSeparator,
|
108
|
+
BreadcrumbEllipsis,
|
109
|
+
}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import * as React from "react"
|
2
|
+
import { Slot } from "@radix-ui/react-slot"
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
4
|
+
|
5
|
+
import { cn } from "@workspace/ui/lib/utils"
|
6
|
+
|
7
|
+
const buttonVariants = cva(
|
8
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
9
|
+
{
|
10
|
+
variants: {
|
11
|
+
variant: {
|
12
|
+
default:
|
13
|
+
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
14
|
+
destructive:
|
15
|
+
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
16
|
+
outline:
|
17
|
+
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
18
|
+
secondary:
|
19
|
+
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
20
|
+
ghost:
|
21
|
+
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
22
|
+
link: "text-primary underline-offset-4 hover:underline",
|
23
|
+
},
|
24
|
+
size: {
|
25
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
26
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
27
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
28
|
+
icon: "size-9",
|
29
|
+
},
|
30
|
+
},
|
31
|
+
defaultVariants: {
|
32
|
+
variant: "default",
|
33
|
+
size: "default",
|
34
|
+
},
|
35
|
+
}
|
36
|
+
)
|
37
|
+
|
38
|
+
function Button({
|
39
|
+
className,
|
40
|
+
variant,
|
41
|
+
size,
|
42
|
+
asChild = false,
|
43
|
+
...props
|
44
|
+
}: React.ComponentProps<"button"> &
|
45
|
+
VariantProps<typeof buttonVariants> & {
|
46
|
+
asChild?: boolean
|
47
|
+
}) {
|
48
|
+
const Comp = asChild ? Slot : "button"
|
49
|
+
|
50
|
+
return (
|
51
|
+
<Comp
|
52
|
+
data-slot="button"
|
53
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
54
|
+
{...props}
|
55
|
+
/>
|
56
|
+
)
|
57
|
+
}
|
58
|
+
|
59
|
+
export { Button, buttonVariants }
|