@ossido-labs/ossido-ui 0.1.0
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/LICENSE +21 -0
- package/README.md +19 -0
- package/dist/esm/components/BaseStyles.d.ts +8 -0
- package/dist/esm/components/BaseStyles.js +17 -0
- package/dist/esm/components/BaseStyles.js.map +1 -0
- package/dist/esm/components/DefaultError.d.ts +9 -0
- package/dist/esm/components/DefaultError.js +30 -0
- package/dist/esm/components/DefaultError.js.map +1 -0
- package/dist/esm/components/DefaultLoading.d.ts +6 -0
- package/dist/esm/components/DefaultLoading.js +13 -0
- package/dist/esm/components/DefaultLoading.js.map +1 -0
- package/dist/esm/components/DefaultScreen.d.ts +19 -0
- package/dist/esm/components/DefaultScreen.js +96 -0
- package/dist/esm/components/DefaultScreen.js.map +1 -0
- package/dist/esm/components/DevBuildErrorContent.d.ts +11 -0
- package/dist/esm/components/DevBuildErrorContent.js +56 -0
- package/dist/esm/components/DevBuildErrorContent.js.map +1 -0
- package/dist/esm/components/DevErrorContent.d.ts +4 -0
- package/dist/esm/components/DevErrorContent.js +192 -0
- package/dist/esm/components/DevErrorContent.js.map +1 -0
- package/dist/esm/components/DevErrorOverlay.d.ts +13 -0
- package/dist/esm/components/DevErrorOverlay.js +53 -0
- package/dist/esm/components/DevErrorOverlay.js.map +1 -0
- package/dist/esm/components/DevErrorOverlayHost.d.ts +2 -0
- package/dist/esm/components/DevErrorOverlayHost.js +295 -0
- package/dist/esm/components/DevErrorOverlayHost.js.map +1 -0
- package/dist/esm/components/DevErrorReporter.d.ts +13 -0
- package/dist/esm/components/DevErrorReporter.js +28 -0
- package/dist/esm/components/DevErrorReporter.js.map +1 -0
- package/dist/esm/components/base.js +6 -0
- package/dist/esm/components/base.js.map +1 -0
- package/dist/esm/components/devErrorSource.d.ts +96 -0
- package/dist/esm/components/devErrorSource.js +321 -0
- package/dist/esm/components/devErrorSource.js.map +1 -0
- package/dist/esm/components/devErrorStore.d.ts +105 -0
- package/dist/esm/components/devErrorStore.js +186 -0
- package/dist/esm/components/devErrorStore.js.map +1 -0
- package/dist/esm/components/devErrorStyles.d.ts +6 -0
- package/dist/esm/components/devErrorStyles.js +426 -0
- package/dist/esm/components/devErrorStyles.js.map +1 -0
- package/dist/esm/index.d.ts +11 -0
- package/dist/esm/index.js +11 -0
- package/dist/esm/types.d.ts +26 -0
- package/dist/esm/vite/error-overlay.d.ts +32 -0
- package/dist/esm/vite/error-overlay.js +49 -0
- package/dist/esm/vite/error-overlay.js.map +1 -0
- package/package.json +81 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Chris Schofield
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# ossido-ui
|
|
2
|
+
|
|
3
|
+
Default UI components and design tokens for [Ossido](https://ossido.dev) — the
|
|
4
|
+
React/Rust full-stack framework.
|
|
5
|
+
|
|
6
|
+
It provides the framework's built-in screens and the development error overlay
|
|
7
|
+
that Ossido renders out of the box: `BaseStyles`, `DefaultScreen`,
|
|
8
|
+
`DefaultError`, `DefaultLoading`, and the `DevErrorOverlay` stack.
|
|
9
|
+
|
|
10
|
+
## Entry points
|
|
11
|
+
|
|
12
|
+
- `ossido-ui` — the components and their types
|
|
13
|
+
- `ossido-ui/vite-plugin` — the Vite plugin that wires the design tokens/styles
|
|
14
|
+
into the build
|
|
15
|
+
|
|
16
|
+
These are used internally by scaffolded Ossido apps; you can also import the
|
|
17
|
+
components directly to customise the default screens.
|
|
18
|
+
|
|
19
|
+
Check [ossido](https://github.com/ossido-labs/ossido) for more.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Injects the shared design tokens ({@link file://./base.css}) — colours,
|
|
4
|
+
* typography and the Google Font imports (Noto Sans / JetBrains Mono) — used by
|
|
5
|
+
* the framework's default screens. Rendered once at the top of each default
|
|
6
|
+
* screen so it is self-contained (no dependency on the app's stylesheet).
|
|
7
|
+
*/
|
|
8
|
+
export declare function BaseStyles(): JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import base_default from "./base.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/components/BaseStyles.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Injects the shared design tokens ({@link file://./base.css}) — colours,
|
|
7
|
+
* typography and the Google Font imports (Noto Sans / JetBrains Mono) — used by
|
|
8
|
+
* the framework's default screens. Rendered once at the top of each default
|
|
9
|
+
* screen so it is self-contained (no dependency on the app's stylesheet).
|
|
10
|
+
*/
|
|
11
|
+
function BaseStyles() {
|
|
12
|
+
return /* @__PURE__ */ jsx("style", { children: base_default });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
export { BaseStyles };
|
|
17
|
+
//# sourceMappingURL=BaseStyles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseStyles.js","names":["baseCss"],"sources":["../../../src/components/BaseStyles.tsx"],"sourcesContent":["import type { JSX } from 'react';\n\nimport baseCss from './base.css';\n\n/**\n * Injects the shared design tokens ({@link file://./base.css}) — colours,\n * typography and the Google Font imports (Noto Sans / JetBrains Mono) — used by\n * the framework's default screens. Rendered once at the top of each default\n * screen so it is self-contained (no dependency on the app's stylesheet).\n */\nexport function BaseStyles(): JSX.Element {\n return <style>{baseCss}</style>;\n}\n"],"mappings":";;;;;;;;;;AAUA,SAAgB,aAA0B;CACxC,OAAO,oBAAC,SAAD,YAAQA,aAAe;AAChC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
import type { OssidoErrorProps } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Production error fallback used when a route has no `error.tsx`. Deliberately
|
|
5
|
+
* shows no error message, stack or source — those could leak internals — just a
|
|
6
|
+
* friendly notice and a retry. The rich {@link DevErrorOverlay} is used in
|
|
7
|
+
* development instead.
|
|
8
|
+
*/
|
|
9
|
+
export declare function DefaultError({ reset }: OssidoErrorProps): JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { DefaultScreen } from "./DefaultScreen.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/components/DefaultError.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Production error fallback used when a route has no `error.tsx`. Deliberately
|
|
7
|
+
* shows no error message, stack or source — those could leak internals — just a
|
|
8
|
+
* friendly notice and a retry. The rich {@link DevErrorOverlay} is used in
|
|
9
|
+
* development instead.
|
|
10
|
+
*/
|
|
11
|
+
function DefaultError({ reset }) {
|
|
12
|
+
return /* @__PURE__ */ jsxs(DefaultScreen, {
|
|
13
|
+
role: "alert",
|
|
14
|
+
badge: "Error",
|
|
15
|
+
title: "Something went wrong",
|
|
16
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
17
|
+
className: "ossido-screen-text",
|
|
18
|
+
children: "An unexpected error occurred. Please try again."
|
|
19
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
20
|
+
type: "button",
|
|
21
|
+
className: "ossido-screen-action",
|
|
22
|
+
onClick: reset,
|
|
23
|
+
children: "Try again"
|
|
24
|
+
})]
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { DefaultError };
|
|
30
|
+
//# sourceMappingURL=DefaultError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DefaultError.js","names":[],"sources":["../../../src/components/DefaultError.tsx"],"sourcesContent":["import type { JSX } from 'react';\n\nimport type { OssidoErrorProps } from '../types';\n\nimport { DefaultScreen } from './DefaultScreen';\n\n/**\n * Production error fallback used when a route has no `error.tsx`. Deliberately\n * shows no error message, stack or source — those could leak internals — just a\n * friendly notice and a retry. The rich {@link DevErrorOverlay} is used in\n * development instead.\n */\nexport function DefaultError({ reset }: OssidoErrorProps): JSX.Element {\n return (\n <DefaultScreen role=\"alert\" badge=\"Error\" title=\"Something went wrong\">\n <p className=\"ossido-screen-text\">\n An unexpected error occurred. Please try again.\n </p>\n <button type=\"button\" className=\"ossido-screen-action\" onClick={reset}>\n Try again\n </button>\n </DefaultScreen>\n );\n}\n"],"mappings":";;;;;;;;;;AAYA,SAAgB,aAAa,EAAE,SAAwC;CACrE,OACE,qBAAC,eAAD;EAAe,MAAK;EAAQ,OAAM;EAAQ,OAAM;YAAhD,CACE,oBAAC,KAAD;GAAG,WAAU;aAAqB;EAE/B,IACH,oBAAC,UAAD;GAAQ,MAAK;GAAS,WAAU;GAAuB,SAAS;aAAO;EAE/D,EACK;;AAEnB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/components/DefaultLoading.tsx
|
|
2
|
+
/**
|
|
3
|
+
* Framework default `<Suspense>` fallback used when a route has no nearest
|
|
4
|
+
* `loading.tsx`. Intentionally renders nothing — apps provide their own
|
|
5
|
+
* `loading.tsx` for real loading UI.
|
|
6
|
+
*/
|
|
7
|
+
function DefaultLoading() {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
export { DefaultLoading };
|
|
13
|
+
//# sourceMappingURL=DefaultLoading.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DefaultLoading.js","names":[],"sources":["../../../src/components/DefaultLoading.tsx"],"sourcesContent":["/**\n * Framework default `<Suspense>` fallback used when a route has no nearest\n * `loading.tsx`. Intentionally renders nothing — apps provide their own\n * `loading.tsx` for real loading UI.\n */\nexport function DefaultLoading(): null {\n return null;\n}\n"],"mappings":";;;;;;AAKA,SAAgB,iBAAuB;CACrC,OAAO;AACT"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { JSX, ReactNode } from 'react';
|
|
2
|
+
interface DefaultScreenProps {
|
|
3
|
+
/** ARIA role for the container (e.g. `alert` for the error fallback). */
|
|
4
|
+
role?: string;
|
|
5
|
+
/** Small uppercase pill above the title (e.g. `Error`, `404`). */
|
|
6
|
+
badge: string;
|
|
7
|
+
/** The screen heading. */
|
|
8
|
+
title: string;
|
|
9
|
+
/** Message paragraph(s) and action(s) — use `ossido-screen-*` classes. */
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Shared shell for the framework's full-page default screens — the production
|
|
14
|
+
* error fallback ({@link DefaultError}) and the 404 page
|
|
15
|
+
* ({@link NotFoundDefaultContent}) — styled to match the development
|
|
16
|
+
* {@link DevErrorOverlay}: a dark canvas with an accent badge and heading.
|
|
17
|
+
*/
|
|
18
|
+
export declare function DefaultScreen({ role, badge, title, children, }: DefaultScreenProps): JSX.Element;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { BaseStyles } from "./BaseStyles.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/components/DefaultScreen.tsx
|
|
5
|
+
const STYLES = `
|
|
6
|
+
.ossido-screen {
|
|
7
|
+
position: fixed;
|
|
8
|
+
inset: 0;
|
|
9
|
+
z-index: 2147483647;
|
|
10
|
+
overflow: auto;
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
padding: var(--ossido-space-8);
|
|
15
|
+
background: var(--ossido-color-bg);
|
|
16
|
+
color: var(--ossido-color-text);
|
|
17
|
+
font-family: var(--ossido-font-sans);
|
|
18
|
+
font-size: var(--ossido-font-size);
|
|
19
|
+
line-height: var(--ossido-line-height);
|
|
20
|
+
}
|
|
21
|
+
.ossido-screen * { font-family: var(--ossido-font-sans); }
|
|
22
|
+
.ossido-screen-card {
|
|
23
|
+
max-width: 32rem;
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
align-items: center;
|
|
27
|
+
gap: var(--ossido-space-4);
|
|
28
|
+
text-align: center;
|
|
29
|
+
}
|
|
30
|
+
.ossido-screen-badge {
|
|
31
|
+
display: inline-block;
|
|
32
|
+
padding: 2px 10px;
|
|
33
|
+
border-radius: var(--ossido-radius-pill);
|
|
34
|
+
background: var(--ossido-color-accent);
|
|
35
|
+
color: var(--ossido-color-on-accent);
|
|
36
|
+
font-size: var(--ossido-font-size-xs);
|
|
37
|
+
font-weight: var(--ossido-font-weight-bold);
|
|
38
|
+
letter-spacing: var(--ossido-letter-spacing-wide);
|
|
39
|
+
text-transform: uppercase;
|
|
40
|
+
}
|
|
41
|
+
.ossido-screen-title {
|
|
42
|
+
margin: 0;
|
|
43
|
+
font-size: var(--ossido-font-size-xl);
|
|
44
|
+
font-weight: var(--ossido-font-weight-bold);
|
|
45
|
+
color: var(--ossido-color-accent-text);
|
|
46
|
+
}
|
|
47
|
+
.ossido-screen-text {
|
|
48
|
+
margin: 0;
|
|
49
|
+
color: var(--ossido-color-text-muted);
|
|
50
|
+
}
|
|
51
|
+
.ossido-screen-action {
|
|
52
|
+
padding: 6px 16px;
|
|
53
|
+
border: 1px solid var(--ossido-color-border-strong);
|
|
54
|
+
border-radius: var(--ossido-radius-sm);
|
|
55
|
+
background: var(--ossido-color-surface-raised);
|
|
56
|
+
color: var(--ossido-color-text);
|
|
57
|
+
font: inherit;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
text-decoration: none;
|
|
60
|
+
}
|
|
61
|
+
.ossido-screen-action:hover { background: var(--ossido-color-surface-hover); }
|
|
62
|
+
`;
|
|
63
|
+
/**
|
|
64
|
+
* Shared shell for the framework's full-page default screens — the production
|
|
65
|
+
* error fallback ({@link DefaultError}) and the 404 page
|
|
66
|
+
* ({@link NotFoundDefaultContent}) — styled to match the development
|
|
67
|
+
* {@link DevErrorOverlay}: a dark canvas with an accent badge and heading.
|
|
68
|
+
*/
|
|
69
|
+
function DefaultScreen({ role, badge, title, children }) {
|
|
70
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
71
|
+
className: "ossido-screen",
|
|
72
|
+
role,
|
|
73
|
+
children: [
|
|
74
|
+
/* @__PURE__ */ jsx(BaseStyles, {}),
|
|
75
|
+
/* @__PURE__ */ jsx("style", { children: STYLES }),
|
|
76
|
+
/* @__PURE__ */ jsxs("div", {
|
|
77
|
+
className: "ossido-screen-card",
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ jsx("span", {
|
|
80
|
+
className: "ossido-screen-badge",
|
|
81
|
+
children: badge
|
|
82
|
+
}),
|
|
83
|
+
/* @__PURE__ */ jsx("h1", {
|
|
84
|
+
className: "ossido-screen-title",
|
|
85
|
+
children: title
|
|
86
|
+
}),
|
|
87
|
+
children
|
|
88
|
+
]
|
|
89
|
+
})
|
|
90
|
+
]
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
export { DefaultScreen };
|
|
96
|
+
//# sourceMappingURL=DefaultScreen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DefaultScreen.js","names":[],"sources":["../../../src/components/DefaultScreen.tsx"],"sourcesContent":["import type { JSX, ReactNode } from 'react';\n\nimport { BaseStyles } from './BaseStyles';\n\ninterface DefaultScreenProps {\n /** ARIA role for the container (e.g. `alert` for the error fallback). */\n role?: string;\n /** Small uppercase pill above the title (e.g. `Error`, `404`). */\n badge: string;\n /** The screen heading. */\n title: string;\n /** Message paragraph(s) and action(s) — use `ossido-screen-*` classes. */\n children: ReactNode;\n}\n\n// Matches the DevErrorOverlay's visual language (dark canvas, accent badge and\n// heading) using the shared base.css tokens, so every framework default screen\n// looks consistent. `.ossido-screen *` re-applies the sans token to beat an\n// app-level `* { font-family: ... }` reset.\nconst STYLES = `\n.ossido-screen {\n position: fixed;\n inset: 0;\n z-index: 2147483647;\n overflow: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--ossido-space-8);\n background: var(--ossido-color-bg);\n color: var(--ossido-color-text);\n font-family: var(--ossido-font-sans);\n font-size: var(--ossido-font-size);\n line-height: var(--ossido-line-height);\n}\n.ossido-screen * { font-family: var(--ossido-font-sans); }\n.ossido-screen-card {\n max-width: 32rem;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--ossido-space-4);\n text-align: center;\n}\n.ossido-screen-badge {\n display: inline-block;\n padding: 2px 10px;\n border-radius: var(--ossido-radius-pill);\n background: var(--ossido-color-accent);\n color: var(--ossido-color-on-accent);\n font-size: var(--ossido-font-size-xs);\n font-weight: var(--ossido-font-weight-bold);\n letter-spacing: var(--ossido-letter-spacing-wide);\n text-transform: uppercase;\n}\n.ossido-screen-title {\n margin: 0;\n font-size: var(--ossido-font-size-xl);\n font-weight: var(--ossido-font-weight-bold);\n color: var(--ossido-color-accent-text);\n}\n.ossido-screen-text {\n margin: 0;\n color: var(--ossido-color-text-muted);\n}\n.ossido-screen-action {\n padding: 6px 16px;\n border: 1px solid var(--ossido-color-border-strong);\n border-radius: var(--ossido-radius-sm);\n background: var(--ossido-color-surface-raised);\n color: var(--ossido-color-text);\n font: inherit;\n cursor: pointer;\n text-decoration: none;\n}\n.ossido-screen-action:hover { background: var(--ossido-color-surface-hover); }\n`;\n\n/**\n * Shared shell for the framework's full-page default screens — the production\n * error fallback ({@link DefaultError}) and the 404 page\n * ({@link NotFoundDefaultContent}) — styled to match the development\n * {@link DevErrorOverlay}: a dark canvas with an accent badge and heading.\n */\nexport function DefaultScreen({\n role,\n badge,\n title,\n children,\n}: DefaultScreenProps): JSX.Element {\n return (\n <div className=\"ossido-screen\" role={role}>\n <BaseStyles />\n <style>{STYLES}</style>\n <div className=\"ossido-screen-card\">\n <span className=\"ossido-screen-badge\">{badge}</span>\n <h1 className=\"ossido-screen-title\">{title}</h1>\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AAmBA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiEf,SAAgB,cAAc,EAC5B,MACA,OACA,OACA,YACkC;CAClC,OACE,qBAAC,OAAD;EAAK,WAAU;EAAsB;YAArC;GACE,oBAAC,YAAD,CAAa;GACb,oBAAC,SAAD,YAAQ,OAAc;GACtB,qBAAC,OAAD;IAAK,WAAU;cAAf;KACE,oBAAC,QAAD;MAAM,WAAU;gBAAuB;KAAY;KACnD,oBAAC,MAAD;MAAI,WAAU;gBAAuB;KAAU;KAC9C;IACE;;EACF;;AAET"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
import type { DevBuildError } from './devErrorStore';
|
|
3
|
+
/**
|
|
4
|
+
* Renders a Vite build/compile error (reported into the store by the patched
|
|
5
|
+
* Vite HMR client). Shows the message, the offending file/location, and Vite's
|
|
6
|
+
* code frame — the same information Vite's native overlay would, but inside the
|
|
7
|
+
* unified ossido overlay.
|
|
8
|
+
*/
|
|
9
|
+
export declare function DevBuildErrorContent({ build, }: {
|
|
10
|
+
build: DevBuildError;
|
|
11
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
|
|
3
|
+
//#region src/components/DevBuildErrorContent.tsx
|
|
4
|
+
/**
|
|
5
|
+
* Renders a Vite build/compile error (reported into the store by the patched
|
|
6
|
+
* Vite HMR client). Shows the message, the offending file/location, and Vite's
|
|
7
|
+
* code frame — the same information Vite's native overlay would, but inside the
|
|
8
|
+
* unified ossido overlay.
|
|
9
|
+
*/
|
|
10
|
+
function DevBuildErrorContent({ build }) {
|
|
11
|
+
const file = build.loc?.file ?? build.id;
|
|
12
|
+
const location = build.loc?.line != null ? `${file}:${build.loc.line}:${build.loc.column ?? 0}` : file;
|
|
13
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
14
|
+
/* @__PURE__ */ jsx("span", {
|
|
15
|
+
className: "ossido-err-badge",
|
|
16
|
+
children: "Build error"
|
|
17
|
+
}),
|
|
18
|
+
/* @__PURE__ */ jsxs("h1", {
|
|
19
|
+
className: "ossido-err-name",
|
|
20
|
+
children: [build.plugin ? `[plugin: ${build.plugin}] ` : "", "Failed to compile"]
|
|
21
|
+
}),
|
|
22
|
+
/* @__PURE__ */ jsxs("div", {
|
|
23
|
+
className: "ossido-err-message-card",
|
|
24
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
25
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
26
|
+
viewBox: "0 0 640 640",
|
|
27
|
+
children: /* @__PURE__ */ jsx("path", { d: "M320 576C178.6 576 64 461.4 64 320C64 178.6 178.6 64 320 64C461.4 64 576 178.6 576 320C576 461.4 461.4 576 320 576zM320 112C205.1 112 112 205.1 112 320C112 434.9 205.1 528 320 528C434.9 528 528 434.9 528 320C528 205.1 434.9 112 320 112zM348 444L292 444L292 388L348 388L348 444zM339.2 352L300.8 352L288 192L352 192L339.2 352z" })
|
|
28
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
29
|
+
className: "ossido-err-message",
|
|
30
|
+
children: build.message
|
|
31
|
+
})]
|
|
32
|
+
}),
|
|
33
|
+
location && /* @__PURE__ */ jsxs("div", {
|
|
34
|
+
className: "ossido-err-source",
|
|
35
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
36
|
+
className: "ossido-err-source-file",
|
|
37
|
+
children: location
|
|
38
|
+
}), build.frame && /* @__PURE__ */ jsx("pre", {
|
|
39
|
+
className: "ossido-err-frame-code",
|
|
40
|
+
children: build.frame.trimEnd()
|
|
41
|
+
})]
|
|
42
|
+
}),
|
|
43
|
+
!location && build.frame && /* @__PURE__ */ jsx("pre", {
|
|
44
|
+
className: "ossido-err-frame-code",
|
|
45
|
+
children: build.frame.trimEnd()
|
|
46
|
+
}),
|
|
47
|
+
build.stack && !build.frame && /* @__PURE__ */ jsx("pre", {
|
|
48
|
+
className: "ossido-err-rawstack",
|
|
49
|
+
children: build.stack
|
|
50
|
+
})
|
|
51
|
+
] });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
export { DevBuildErrorContent };
|
|
56
|
+
//# sourceMappingURL=DevBuildErrorContent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevBuildErrorContent.js","names":[],"sources":["../../../src/components/DevBuildErrorContent.tsx"],"sourcesContent":["import type { JSX } from 'react';\n\nimport type { DevBuildError } from './devErrorStore';\n\n/**\n * Renders a Vite build/compile error (reported into the store by the patched\n * Vite HMR client). Shows the message, the offending file/location, and Vite's\n * code frame — the same information Vite's native overlay would, but inside the\n * unified ossido overlay.\n */\nexport function DevBuildErrorContent({\n build,\n}: {\n build: DevBuildError;\n}): JSX.Element {\n const file = build.loc?.file ?? build.id;\n const location =\n build.loc?.line != null\n ? `${file}:${build.loc.line}:${build.loc.column ?? 0}`\n : file;\n\n return (\n <>\n <span className=\"ossido-err-badge\">Build error</span>\n <h1 className=\"ossido-err-name\">\n {build.plugin ? `[plugin: ${build.plugin}] ` : ''}\n Failed to compile\n </h1>\n <div className=\"ossido-err-message-card\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\">\n <path d=\"M320 576C178.6 576 64 461.4 64 320C64 178.6 178.6 64 320 64C461.4 64 576 178.6 576 320C576 461.4 461.4 576 320 576zM320 112C205.1 112 112 205.1 112 320C112 434.9 205.1 528 320 528C434.9 528 528 434.9 528 320C528 205.1 434.9 112 320 112zM348 444L292 444L292 388L348 388L348 444zM339.2 352L300.8 352L288 192L352 192L339.2 352z\" />\n </svg>\n <p className=\"ossido-err-message\">{build.message}</p>\n </div>\n\n {location && (\n <div className=\"ossido-err-source\">\n <div className=\"ossido-err-source-file\">{location}</div>\n {build.frame && (\n <pre className=\"ossido-err-frame-code\">{build.frame.trimEnd()}</pre>\n )}\n </div>\n )}\n\n {!location && build.frame && (\n <pre className=\"ossido-err-frame-code\">{build.frame.trimEnd()}</pre>\n )}\n\n {build.stack && !build.frame && (\n <pre className=\"ossido-err-rawstack\">{build.stack}</pre>\n )}\n </>\n );\n}\n"],"mappings":";;;;;;;;;AAUA,SAAgB,qBAAqB,EACnC,SAGc;CACd,MAAM,OAAO,MAAM,KAAK,QAAQ,MAAM;CACtC,MAAM,WACJ,MAAM,KAAK,QAAQ,OACf,GAAG,KAAK,GAAG,MAAM,IAAI,KAAK,GAAG,MAAM,IAAI,UAAU,MACjD;CAEN,OACE;EACE,oBAAC,QAAD;GAAM,WAAU;aAAmB;EAAiB;EACpD,qBAAC,MAAD;GAAI,WAAU;aAAd,CACG,MAAM,SAAS,YAAY,MAAM,OAAO,MAAM,IAAG,mBAEhD;;EACJ,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,OAAD;IAAK,OAAM;IAA6B,SAAQ;cAC9C,oBAAC,QAAD,EAAM,GAAE,uUAAwU;GAC7U,IACL,oBAAC,KAAD;IAAG,WAAU;cAAsB,MAAM;GAAW,EACjD;;EAEJ,YACC,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,OAAD;IAAK,WAAU;cAA0B;GAAc,IACtD,MAAM,SACL,oBAAC,OAAD;IAAK,WAAU;cAAyB,MAAM,MAAM,QAAQ;GAAO,EAElE;;EAGN,CAAC,YAAY,MAAM,SAClB,oBAAC,OAAD;GAAK,WAAU;aAAyB,MAAM,MAAM,QAAQ;EAAO;EAGpE,MAAM,SAAS,CAAC,MAAM,SACrB,oBAAC,OAAD;GAAK,WAAU;aAAuB,MAAM;EAAW;CAEzD;AAEN"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { errorLabel, extractExcerpt, highlightExcerpt, initialFrames, prettyPath, resolveDevErrorFrames } from "./devErrorSource.js";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/components/DevErrorContent.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Renders the details of a single JS error (a React render error, uncaught
|
|
8
|
+
* `window` error, or unhandled rejection): the error name and message, a source
|
|
9
|
+
* window around the throwing expression (resolved to original source through
|
|
10
|
+
* sourcemaps), and the stack trace mapped to original locations with application
|
|
11
|
+
* frames highlighted.
|
|
12
|
+
*
|
|
13
|
+
* The excerpt renders as **plain text as soon as the source content is
|
|
14
|
+
* available** (immediately for a server-provided Rust panic; after the sourcemap
|
|
15
|
+
* fetch for a JS error), then syntax highlighting swaps in over the exact same
|
|
16
|
+
* layout — so there is no blank-then-appear jump.
|
|
17
|
+
*
|
|
18
|
+
* This is the inner content only — the window chrome, pager and actions live in
|
|
19
|
+
* {@link DevErrorOverlayHost}. It is remounted per error (keyed by entry id) so
|
|
20
|
+
* its resolution state starts fresh.
|
|
21
|
+
*/
|
|
22
|
+
/** Stack frames shown before the "show more" toggle is expanded. */
|
|
23
|
+
const MAX_VISIBLE_FRAMES = 5;
|
|
24
|
+
/** Rows the excerpt spans (SOURCE_CONTEXT * 2 + 1) — the skeleton matches it so
|
|
25
|
+
* the real excerpt drops into already-reserved space (no layout jump). */
|
|
26
|
+
const EXCERPT_ROWS = 11;
|
|
27
|
+
/**
|
|
28
|
+
* A fixed-height placeholder rendered while a JS error's excerpt is still being
|
|
29
|
+
* resolved (the sourcemap has to be fetched first). It reserves exactly the
|
|
30
|
+
* excerpt's height so the resolved code doesn't shove the stack down when it
|
|
31
|
+
* arrives.
|
|
32
|
+
*/
|
|
33
|
+
function ExcerptSkeleton() {
|
|
34
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
35
|
+
className: "ossido-err-source",
|
|
36
|
+
"aria-hidden": true,
|
|
37
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
38
|
+
className: "ossido-err-source-file",
|
|
39
|
+
children: "\xA0"
|
|
40
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
41
|
+
className: "ossido-err-code",
|
|
42
|
+
children: Array.from({ length: EXCERPT_ROWS }, (_, index) => /* @__PURE__ */ jsxs("div", {
|
|
43
|
+
className: "ossido-err-code-line",
|
|
44
|
+
children: [/* @__PURE__ */ jsx("span", { className: "ossido-err-gutter" }), /* @__PURE__ */ jsx("span", {
|
|
45
|
+
className: "ossido-err-code-text",
|
|
46
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
47
|
+
className: "ossido-err-skel-bar",
|
|
48
|
+
style: { width: `${10 + index * 13 % 34}ch` }
|
|
49
|
+
})
|
|
50
|
+
})]
|
|
51
|
+
}, index))
|
|
52
|
+
})]
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Whether a frame points at the app's own source — a non-vendor frame whose
|
|
57
|
+
* (resolved) path sits under a `src/` directory. The `isApp` guard keeps a
|
|
58
|
+
* dependency's own `src/` (e.g. `node_modules/foo/src/…`) from matching.
|
|
59
|
+
*/
|
|
60
|
+
function isAppSourceFrame(frame) {
|
|
61
|
+
return frame.isApp && !!frame.file && /(^|\/)src\//.test(frame.file);
|
|
62
|
+
}
|
|
63
|
+
function DevErrorContent({ error }) {
|
|
64
|
+
const [framesExpanded, setFramesExpanded] = useState(false);
|
|
65
|
+
const serverSource = error.ossidoServerSource;
|
|
66
|
+
const initialPlain = useMemo(() => serverSource ? extractExcerpt({
|
|
67
|
+
content: serverSource.content,
|
|
68
|
+
file: prettyPath(serverSource.file),
|
|
69
|
+
line: serverSource.line,
|
|
70
|
+
column: serverSource.column
|
|
71
|
+
}) : null, [serverSource]);
|
|
72
|
+
const [frames, setFrames] = useState(() => initialFrames(error.stack));
|
|
73
|
+
const [plain, setPlain] = useState(initialPlain);
|
|
74
|
+
const [highlighted, setHighlighted] = useState(null);
|
|
75
|
+
const expectsExcerpt = useMemo(() => !serverSource && initialFrames(error.stack).some((frame) => frame.isApp && !!frame.file && frame.line != null), [error.stack, serverSource]);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
let cancelled = false;
|
|
78
|
+
const resolve = async () => {
|
|
79
|
+
if (serverSource) {
|
|
80
|
+
const source = {
|
|
81
|
+
content: serverSource.content,
|
|
82
|
+
file: prettyPath(serverSource.file),
|
|
83
|
+
line: serverSource.line,
|
|
84
|
+
column: serverSource.column
|
|
85
|
+
};
|
|
86
|
+
const result = await highlightExcerpt(source);
|
|
87
|
+
if (!cancelled && result) setHighlighted(result);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const { frames: resolvedFrames, source } = await resolveDevErrorFrames(error.stack);
|
|
91
|
+
if (cancelled) return;
|
|
92
|
+
setFrames(resolvedFrames);
|
|
93
|
+
if (source) {
|
|
94
|
+
setPlain(extractExcerpt(source));
|
|
95
|
+
const result = await highlightExcerpt(source);
|
|
96
|
+
if (!cancelled && result) setHighlighted(result);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
resolve();
|
|
100
|
+
return () => {
|
|
101
|
+
cancelled = true;
|
|
102
|
+
};
|
|
103
|
+
}, [error, serverSource]);
|
|
104
|
+
const excerpt = highlighted ?? plain;
|
|
105
|
+
const hiddenFrameCount = Math.max(0, frames.length - MAX_VISIBLE_FRAMES);
|
|
106
|
+
const visibleFrames = framesExpanded ? frames : frames.slice(0, MAX_VISIBLE_FRAMES);
|
|
107
|
+
const label = errorLabel(error);
|
|
108
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
109
|
+
/* @__PURE__ */ jsx("span", {
|
|
110
|
+
className: "ossido-err-badge",
|
|
111
|
+
children: "Error"
|
|
112
|
+
}),
|
|
113
|
+
/* @__PURE__ */ jsx("h1", {
|
|
114
|
+
className: "ossido-err-name",
|
|
115
|
+
children: label
|
|
116
|
+
}),
|
|
117
|
+
/* @__PURE__ */ jsxs("div", {
|
|
118
|
+
className: "ossido-err-message-card",
|
|
119
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
120
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
121
|
+
viewBox: "0 0 640 640",
|
|
122
|
+
children: /* @__PURE__ */ jsx("path", { d: "M320 576C178.6 576 64 461.4 64 320C64 178.6 178.6 64 320 64C461.4 64 576 178.6 576 320C576 461.4 461.4 576 320 576zM320 112C205.1 112 112 205.1 112 320C112 434.9 205.1 528 320 528C434.9 528 528 434.9 528 320C528 205.1 434.9 112 320 112zM348 444L292 444L292 388L348 388L348 444zM339.2 352L300.8 352L288 192L352 192L339.2 352z" })
|
|
123
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
124
|
+
className: "ossido-err-message",
|
|
125
|
+
children: error.message
|
|
126
|
+
})]
|
|
127
|
+
}),
|
|
128
|
+
excerpt ? /* @__PURE__ */ jsxs("div", {
|
|
129
|
+
className: "ossido-err-source",
|
|
130
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
131
|
+
className: "ossido-err-source-file",
|
|
132
|
+
children: [
|
|
133
|
+
excerpt.file,
|
|
134
|
+
":",
|
|
135
|
+
excerpt.line,
|
|
136
|
+
":",
|
|
137
|
+
excerpt.column
|
|
138
|
+
]
|
|
139
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
140
|
+
className: "ossido-err-code",
|
|
141
|
+
children: excerpt.lines.map((sourceLine) => /* @__PURE__ */ jsxs("div", {
|
|
142
|
+
className: sourceLine.isErrorLine ? "ossido-err-code-line ossido-err-code-line--error" : "ossido-err-code-line",
|
|
143
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
144
|
+
className: "ossido-err-gutter",
|
|
145
|
+
children: sourceLine.number
|
|
146
|
+
}), "html" in sourceLine ? /* @__PURE__ */ jsx("span", {
|
|
147
|
+
className: "ossido-err-code-text",
|
|
148
|
+
dangerouslySetInnerHTML: { __html: sourceLine.html }
|
|
149
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
150
|
+
className: "ossido-err-code-text",
|
|
151
|
+
children: sourceLine.text
|
|
152
|
+
})]
|
|
153
|
+
}, sourceLine.number))
|
|
154
|
+
})]
|
|
155
|
+
}) : expectsExcerpt ? /* @__PURE__ */ jsx(ExcerptSkeleton, {}) : null,
|
|
156
|
+
frames.length === 0 && error.stack && /* @__PURE__ */ jsx("pre", {
|
|
157
|
+
className: "ossido-err-rawstack",
|
|
158
|
+
children: error.stack
|
|
159
|
+
}),
|
|
160
|
+
frames.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
161
|
+
className: "ossido-err-stack",
|
|
162
|
+
children: [visibleFrames.map((frame, index) => /* @__PURE__ */ jsxs("div", {
|
|
163
|
+
className: frame.isApp ? "ossido-err-frame ossido-err-frame--app" : "ossido-err-frame ossido-err-frame--vendor",
|
|
164
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
165
|
+
className: "ossido-err-frame-head",
|
|
166
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
167
|
+
className: "ossido-err-fn",
|
|
168
|
+
children: frame.fn ?? "<anonymous>"
|
|
169
|
+
}), isAppSourceFrame(frame) && /* @__PURE__ */ jsx("span", {
|
|
170
|
+
className: "ossido-err-frame-badge",
|
|
171
|
+
children: "app"
|
|
172
|
+
})]
|
|
173
|
+
}), frame.file && /* @__PURE__ */ jsxs("div", {
|
|
174
|
+
className: "ossido-err-loc",
|
|
175
|
+
children: [frame.file, frame.line != null && `:${frame.line}:${frame.column}`]
|
|
176
|
+
})]
|
|
177
|
+
}, index)), hiddenFrameCount > 0 && /* @__PURE__ */ jsx("button", {
|
|
178
|
+
type: "button",
|
|
179
|
+
className: "ossido-err-frame-toggle",
|
|
180
|
+
onClick: () => {
|
|
181
|
+
setFramesExpanded((expanded) => !expanded);
|
|
182
|
+
},
|
|
183
|
+
"aria-expanded": framesExpanded,
|
|
184
|
+
children: framesExpanded ? "Show fewer frames" : `Show ${hiddenFrameCount} more frame${hiddenFrameCount === 1 ? "" : "s"}`
|
|
185
|
+
})]
|
|
186
|
+
})
|
|
187
|
+
] });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
//#endregion
|
|
191
|
+
export { DevErrorContent };
|
|
192
|
+
//# sourceMappingURL=DevErrorContent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevErrorContent.js","names":[],"sources":["../../../src/components/DevErrorContent.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport type { JSX } from 'react';\n\nimport type { OssidoErrorWithSource } from '../types';\n\nimport type {\n PlainExcerpt,\n ResolvedFrame,\n SourceExcerpt,\n} from './devErrorSource';\nimport {\n errorLabel,\n extractExcerpt,\n highlightExcerpt,\n initialFrames,\n prettyPath,\n resolveDevErrorFrames,\n} from './devErrorSource';\n\n/**\n * Renders the details of a single JS error (a React render error, uncaught\n * `window` error, or unhandled rejection): the error name and message, a source\n * window around the throwing expression (resolved to original source through\n * sourcemaps), and the stack trace mapped to original locations with application\n * frames highlighted.\n *\n * The excerpt renders as **plain text as soon as the source content is\n * available** (immediately for a server-provided Rust panic; after the sourcemap\n * fetch for a JS error), then syntax highlighting swaps in over the exact same\n * layout — so there is no blank-then-appear jump.\n *\n * This is the inner content only — the window chrome, pager and actions live in\n * {@link DevErrorOverlayHost}. It is remounted per error (keyed by entry id) so\n * its resolution state starts fresh.\n */\n\n/** Stack frames shown before the \"show more\" toggle is expanded. */\nconst MAX_VISIBLE_FRAMES = 5;\n\n/** Rows the excerpt spans (SOURCE_CONTEXT * 2 + 1) — the skeleton matches it so\n * the real excerpt drops into already-reserved space (no layout jump). */\nconst EXCERPT_ROWS = 11;\n\n/**\n * A fixed-height placeholder rendered while a JS error's excerpt is still being\n * resolved (the sourcemap has to be fetched first). It reserves exactly the\n * excerpt's height so the resolved code doesn't shove the stack down when it\n * arrives.\n */\nfunction ExcerptSkeleton(): JSX.Element {\n return (\n <div className=\"ossido-err-source\" aria-hidden>\n <div className=\"ossido-err-source-file\"> </div>\n <pre className=\"ossido-err-code\">\n {Array.from({ length: EXCERPT_ROWS }, (_, index) => (\n <div key={index} className=\"ossido-err-code-line\">\n <span className=\"ossido-err-gutter\" />\n <span className=\"ossido-err-code-text\">\n <span\n className=\"ossido-err-skel-bar\"\n // `ch` (not `%`): the code-text is an inline flex item with no\n // resolvable width, so a percentage would collapse to zero.\n style={{ width: `${10 + ((index * 13) % 34)}ch` }}\n />\n </span>\n </div>\n ))}\n </pre>\n </div>\n );\n}\n\n/**\n * Whether a frame points at the app's own source — a non-vendor frame whose\n * (resolved) path sits under a `src/` directory. The `isApp` guard keeps a\n * dependency's own `src/` (e.g. `node_modules/foo/src/…`) from matching.\n */\nfunction isAppSourceFrame(frame: ResolvedFrame): boolean {\n return frame.isApp && !!frame.file && /(^|\\/)src\\//.test(frame.file);\n}\n\nexport function DevErrorContent({ error }: { error: Error }): JSX.Element {\n // Long stacks are collapsed to the first few frames until expanded.\n const [framesExpanded, setFramesExpanded] = useState(false);\n\n // A Rust panic ships its own source (no sourcemap exists), so its plain\n // excerpt is available synchronously on the very first paint.\n const serverSource = (error as OssidoErrorWithSource).ossidoServerSource;\n const initialPlain = useMemo<PlainExcerpt | null>(\n () =>\n serverSource\n ? extractExcerpt({\n content: serverSource.content,\n file: prettyPath(serverSource.file),\n line: serverSource.line,\n column: serverSource.column,\n })\n : null,\n [serverSource],\n );\n\n const [frames, setFrames] = useState<Array<ResolvedFrame>>(() =>\n initialFrames(error.stack),\n );\n const [plain, setPlain] = useState<PlainExcerpt | null>(initialPlain);\n const [highlighted, setHighlighted] = useState<SourceExcerpt | null>(null);\n\n // Whether an excerpt is likely coming (a JS error with an application frame),\n // so its space can be reserved with a skeleton while the sourcemap resolves.\n const expectsExcerpt = useMemo(\n () =>\n !serverSource &&\n initialFrames(error.stack).some(\n (frame) => frame.isApp && !!frame.file && frame.line != null,\n ),\n [error.stack, serverSource],\n );\n\n useEffect(() => {\n let cancelled = false;\n\n const resolve = async (): Promise<void> => {\n if (serverSource) {\n // The plain excerpt is already shown; just highlight it in place.\n const source = {\n content: serverSource.content,\n file: prettyPath(serverSource.file),\n line: serverSource.line,\n column: serverSource.column,\n };\n const result = await highlightExcerpt(source);\n if (!cancelled && result) setHighlighted(result);\n return;\n }\n\n const { frames: resolvedFrames, source } = await resolveDevErrorFrames(\n error.stack,\n );\n if (cancelled) return;\n setFrames(resolvedFrames);\n if (source) {\n // Plain excerpt first (no layout jump), highlighting swaps in after.\n setPlain(extractExcerpt(source));\n const result = await highlightExcerpt(source);\n if (!cancelled && result) setHighlighted(result);\n }\n };\n void resolve();\n\n return (): void => {\n cancelled = true;\n };\n }, [error, serverSource]);\n\n // Prefer the highlighted excerpt once ready; both share the same line/gutter\n // structure, so swapping in the colours does not shift the layout.\n const excerpt: SourceExcerpt | PlainExcerpt | null = highlighted ?? plain;\n const hiddenFrameCount = Math.max(0, frames.length - MAX_VISIBLE_FRAMES);\n const visibleFrames = framesExpanded\n ? frames\n : frames.slice(0, MAX_VISIBLE_FRAMES);\n // The constructor (class) name, shown on its own line under the badge.\n const label = errorLabel(error);\n\n return (\n <>\n <span className=\"ossido-err-badge\">Error</span>\n <h1 className=\"ossido-err-name\">{label}</h1>\n <div className=\"ossido-err-message-card\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\">\n <path d=\"M320 576C178.6 576 64 461.4 64 320C64 178.6 178.6 64 320 64C461.4 64 576 178.6 576 320C576 461.4 461.4 576 320 576zM320 112C205.1 112 112 205.1 112 320C112 434.9 205.1 528 320 528C434.9 528 528 434.9 528 320C528 205.1 434.9 112 320 112zM348 444L292 444L292 388L348 388L348 444zM339.2 352L300.8 352L288 192L352 192L339.2 352z\" />\n </svg>\n <p className=\"ossido-err-message\">{error.message}</p>\n </div>\n\n {excerpt ? (\n <div className=\"ossido-err-source\">\n <div className=\"ossido-err-source-file\">\n {excerpt.file}:{excerpt.line}:{excerpt.column}\n </div>\n <pre className=\"ossido-err-code\">\n {excerpt.lines.map((sourceLine) => (\n <div\n key={sourceLine.number}\n className={\n sourceLine.isErrorLine\n ? 'ossido-err-code-line ossido-err-code-line--error'\n : 'ossido-err-code-line'\n }\n >\n <span className=\"ossido-err-gutter\">{sourceLine.number}</span>\n {'html' in sourceLine ? (\n <span\n className=\"ossido-err-code-text\"\n // Highlighted markup produced by Shiki (trusted).\n dangerouslySetInnerHTML={{ __html: sourceLine.html }}\n />\n ) : (\n <span className=\"ossido-err-code-text\">\n {sourceLine.text}\n </span>\n )}\n </div>\n ))}\n </pre>\n </div>\n ) : expectsExcerpt ? (\n <ExcerptSkeleton />\n ) : null}\n\n {frames.length === 0 && error.stack && (\n <pre className=\"ossido-err-rawstack\">{error.stack}</pre>\n )}\n\n {frames.length > 0 && (\n <div className=\"ossido-err-stack\">\n {visibleFrames.map((frame, index) => (\n <div\n key={index}\n className={\n frame.isApp\n ? 'ossido-err-frame ossido-err-frame--app'\n : 'ossido-err-frame ossido-err-frame--vendor'\n }\n >\n <div className=\"ossido-err-frame-head\">\n <span className=\"ossido-err-fn\">\n {frame.fn ?? '<anonymous>'}\n </span>\n {isAppSourceFrame(frame) && (\n <span className=\"ossido-err-frame-badge\">app</span>\n )}\n </div>\n {frame.file && (\n <div className=\"ossido-err-loc\">\n {frame.file}\n {frame.line != null && `:${frame.line}:${frame.column}`}\n </div>\n )}\n </div>\n ))}\n {hiddenFrameCount > 0 && (\n <button\n type=\"button\"\n className=\"ossido-err-frame-toggle\"\n onClick={(): void => {\n setFramesExpanded((expanded) => !expanded);\n }}\n aria-expanded={framesExpanded}\n >\n {framesExpanded\n ? 'Show fewer frames'\n : `Show ${hiddenFrameCount} more frame${hiddenFrameCount === 1 ? '' : 's'}`}\n </button>\n )}\n </div>\n )}\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAqCA,MAAM,qBAAqB;;;AAI3B,MAAM,eAAe;;;;;;;AAQrB,SAAS,kBAA+B;CACtC,OACE,qBAAC,OAAD;EAAK,WAAU;EAAoB;YAAnC,CACE,oBAAC,OAAD;GAAK,WAAU;aAAyB;EAAW,IACnD,oBAAC,OAAD;GAAK,WAAU;aACZ,MAAM,KAAK,EAAE,QAAQ,aAAa,IAAI,GAAG,UACxC,qBAAC,OAAD;IAAiB,WAAU;cAA3B,CACE,oBAAC,QAAD,EAAM,WAAU,oBAAqB,IACrC,oBAAC,QAAD;KAAM,WAAU;eACd,oBAAC,QAAD;MACE,WAAU;MAGV,OAAO,EAAE,OAAO,GAAG,KAAO,QAAQ,KAAM,GAAI,IAAI;KACjD;IACG,EACH;MAVK,KAUL,CACN;EACE,EACF;;AAET;;;;;;AAOA,SAAS,iBAAiB,OAA+B;CACvD,OAAO,MAAM,SAAS,CAAC,CAAC,MAAM,QAAQ,cAAc,KAAK,MAAM,IAAI;AACrE;AAEA,SAAgB,gBAAgB,EAAE,SAAwC;CAExE,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,KAAK;CAI1D,MAAM,eAAgB,MAAgC;CACtD,MAAM,eAAe,cAEjB,eACI,eAAe;EACb,SAAS,aAAa;EACtB,MAAM,WAAW,aAAa,IAAI;EAClC,MAAM,aAAa;EACnB,QAAQ,aAAa;CACvB,CAAC,IACD,MACN,CAAC,YAAY,CACf;CAEA,MAAM,CAAC,QAAQ,aAAa,eAC1B,cAAc,MAAM,KAAK,CAC3B;CACA,MAAM,CAAC,OAAO,YAAY,SAA8B,YAAY;CACpE,MAAM,CAAC,aAAa,kBAAkB,SAA+B,IAAI;CAIzE,MAAM,iBAAiB,cAEnB,CAAC,gBACD,cAAc,MAAM,KAAK,CAAC,CAAC,MACxB,UAAU,MAAM,SAAS,CAAC,CAAC,MAAM,QAAQ,MAAM,QAAQ,IAC1D,GACF,CAAC,MAAM,OAAO,YAAY,CAC5B;CAEA,gBAAgB;EACd,IAAI,YAAY;EAEhB,MAAM,UAAU,YAA2B;GACzC,IAAI,cAAc;IAEhB,MAAM,SAAS;KACb,SAAS,aAAa;KACtB,MAAM,WAAW,aAAa,IAAI;KAClC,MAAM,aAAa;KACnB,QAAQ,aAAa;IACvB;IACA,MAAM,SAAS,MAAM,iBAAiB,MAAM;IAC5C,IAAI,CAAC,aAAa,QAAQ,eAAe,MAAM;IAC/C;GACF;GAEA,MAAM,EAAE,QAAQ,gBAAgB,WAAW,MAAM,sBAC/C,MAAM,KACR;GACA,IAAI,WAAW;GACf,UAAU,cAAc;GACxB,IAAI,QAAQ;IAEV,SAAS,eAAe,MAAM,CAAC;IAC/B,MAAM,SAAS,MAAM,iBAAiB,MAAM;IAC5C,IAAI,CAAC,aAAa,QAAQ,eAAe,MAAM;GACjD;EACF;EACA,AAAK,QAAQ;EAEb,aAAmB;GACjB,YAAY;EACd;CACF,GAAG,CAAC,OAAO,YAAY,CAAC;CAIxB,MAAM,UAA+C,eAAe;CACpE,MAAM,mBAAmB,KAAK,IAAI,GAAG,OAAO,SAAS,kBAAkB;CACvE,MAAM,gBAAgB,iBAClB,SACA,OAAO,MAAM,GAAG,kBAAkB;CAEtC,MAAM,QAAQ,WAAW,KAAK;CAE9B,OACE;EACE,oBAAC,QAAD;GAAM,WAAU;aAAmB;EAAW;EAC9C,oBAAC,MAAD;GAAI,WAAU;aAAmB;EAAU;EAC3C,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,OAAD;IAAK,OAAM;IAA6B,SAAQ;cAC9C,oBAAC,QAAD,EAAM,GAAE,uUAAwU;GAC7U,IACL,oBAAC,KAAD;IAAG,WAAU;cAAsB,MAAM;GAAW,EACjD;;EAEJ,UACC,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,qBAAC,OAAD;IAAK,WAAU;cAAf;KACG,QAAQ;KAAK;KAAE,QAAQ;KAAK;KAAE,QAAQ;IACpC;OACL,oBAAC,OAAD;IAAK,WAAU;cACZ,QAAQ,MAAM,KAAK,eAClB,qBAAC,OAAD;KAEE,WACE,WAAW,cACP,qDACA;eALR,CAQE,oBAAC,QAAD;MAAM,WAAU;gBAAqB,WAAW;KAAa,IAC5D,UAAU,aACT,oBAAC,QAAD;MACE,WAAU;MAEV,yBAAyB,EAAE,QAAQ,WAAW,KAAK;KACpD,KAED,oBAAC,QAAD;MAAM,WAAU;gBACb,WAAW;KACR,EAEL;OAnBE,WAAW,MAmBb,CACN;GACE,EACF;OACH,iBACF,oBAAC,iBAAD,CAAkB,KAChB;EAEH,OAAO,WAAW,KAAK,MAAM,SAC5B,oBAAC,OAAD;GAAK,WAAU;aAAuB,MAAM;EAAW;EAGxD,OAAO,SAAS,KACf,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,cAAc,KAAK,OAAO,UACzB,qBAAC,OAAD;IAEE,WACE,MAAM,QACF,2CACA;cALR,CAQE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,QAAD;MAAM,WAAU;gBACb,MAAM,MAAM;KACT,IACL,iBAAiB,KAAK,KACrB,oBAAC,QAAD;MAAM,WAAU;gBAAyB;KAAS,EAEjD;QACJ,MAAM,QACL,qBAAC,OAAD;KAAK,WAAU;eAAf,CACG,MAAM,MACN,MAAM,QAAQ,QAAQ,IAAI,MAAM,KAAK,GAAG,MAAM,QAC5C;MAEJ;MArBE,KAqBF,CACN,GACA,mBAAmB,KAClB,oBAAC,UAAD;IACE,MAAK;IACL,WAAU;IACV,eAAqB;KACnB,mBAAmB,aAAa,CAAC,QAAQ;IAC3C;IACA,iBAAe;cAEd,iBACG,sBACA,QAAQ,iBAAiB,aAAa,qBAAqB,IAAI,KAAK;GAClE,EAEP;;CAEP;AAEN"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
import type { OssidoErrorProps } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Standalone single-error overlay window, presented as a floating window over a
|
|
5
|
+
* dimmed backdrop. The live dev app uses {@link DevErrorOverlayHost} instead —
|
|
6
|
+
* it is store-driven and browses every kind of dev error with a pager — but
|
|
7
|
+
* this renders a single error directly, which is handy for stories and any
|
|
8
|
+
* caller that already has one `Error` in hand.
|
|
9
|
+
*
|
|
10
|
+
* Never used in production: {@link RouteMatch} selects the detail-free
|
|
11
|
+
* production fallback, so source and internals are not leaked to end users.
|
|
12
|
+
*/
|
|
13
|
+
export declare function DevErrorOverlay({ error, reset, }: OssidoErrorProps): JSX.Element;
|