@universelle/sentinel-report 0.1.0 → 0.2.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/README.md +31 -0
- package/dist/element.d.ts +11 -0
- package/dist/element.js +21 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/logo.d.ts +8 -0
- package/dist/logo.js +14 -0
- package/dist/mark.d.ts +1 -0
- package/dist/mark.js +3 -1
- package/dist/react.d.ts +36 -0
- package/dist/react.js +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -125,6 +125,37 @@ import { createReporter } from "@universelle/sentinel-report";
|
|
|
125
125
|
await createReporter({ site: "your-site" }).send({ kind: "wish", text: "Export the table" });
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
+
## Branding: SENTINEL's mark
|
|
129
|
+
|
|
130
|
+
For your own button, a footer or a "Protected by SENTINEL" line. The eye and the star take the text colour
|
|
131
|
+
(`currentColor`), so the mark works on light and dark backgrounds; the moon is always the brand's green.
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { SentinelMark, useSentinelReport } from "@universelle/sentinel-report/react";
|
|
135
|
+
|
|
136
|
+
<button onClick={…}>
|
|
137
|
+
<SentinelMark size={20} /> Report a problem
|
|
138
|
+
</button>
|
|
139
|
+
|
|
140
|
+
// title makes it an image with a name, for screen readers:
|
|
141
|
+
<SentinelMark size={32} title="SENTINEL" />
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Without React:
|
|
145
|
+
|
|
146
|
+
```html
|
|
147
|
+
<script type="module">import "@universelle/sentinel-report/element";</script>
|
|
148
|
+
<span style="color: #6b3fc4">Made with <sentinel-mark size="20" label="SENTINEL"></sentinel-mark></span>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
import { markSvg, MARK_SHAPES } from "@universelle/sentinel-report";
|
|
153
|
+
|
|
154
|
+
button.innerHTML = `${markSvg({ size: 20 })} Report`;
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`markSvg` gives you the `<svg>` as a string. `MARK_SHAPES` gives you its inside, for your own `<svg viewBox="0 0 100 100">`.
|
|
158
|
+
|
|
128
159
|
## Notes
|
|
129
160
|
|
|
130
161
|
- **Content Security Policy:** allow `connect-src https://sentinel.cervilla.es`, plus `script-src` if you use the
|
package/dist/element.d.ts
CHANGED
|
@@ -34,8 +34,19 @@ export declare class SentinelReport extends HTMLElement {
|
|
|
34
34
|
private say;
|
|
35
35
|
private submit;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* `<sentinel-mark size="24" label="…">`: SENTINEL's mark on its own, for your branding without React. It takes the
|
|
39
|
+
* text colour (`color` on the element); `label` makes it an image with that name, otherwise it is decorative.
|
|
40
|
+
*/
|
|
41
|
+
export declare class SentinelMark extends HTMLElement {
|
|
42
|
+
static observedAttributes: string[];
|
|
43
|
+
connectedCallback(): void;
|
|
44
|
+
attributeChangedCallback(): void;
|
|
45
|
+
private render;
|
|
46
|
+
}
|
|
37
47
|
declare global {
|
|
38
48
|
interface HTMLElementTagNameMap {
|
|
39
49
|
"sentinel-report": SentinelReport;
|
|
50
|
+
"sentinel-mark": SentinelMark;
|
|
40
51
|
}
|
|
41
52
|
}
|
package/dist/element.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// (auto | light | dark), hide-mark; and the `texts` property for every other word.
|
|
9
9
|
// Events: `sentinel-sent` (detail: { kind }) and `sentinel-error` (detail: { error }). Methods: open(), close().
|
|
10
10
|
import { capture, createReporter } from "./core.js";
|
|
11
|
+
import { markSvg } from "./logo.js";
|
|
11
12
|
import { MARK } from "./mark.js";
|
|
12
13
|
export const DEFAULT_TEXTS = {
|
|
13
14
|
label: "¿Algo falla?",
|
|
@@ -159,5 +160,25 @@ export class SentinelReport extends HTMLElement {
|
|
|
159
160
|
}
|
|
160
161
|
}
|
|
161
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* `<sentinel-mark size="24" label="…">`: SENTINEL's mark on its own, for your branding without React. It takes the
|
|
165
|
+
* text colour (`color` on the element); `label` makes it an image with that name, otherwise it is decorative.
|
|
166
|
+
*/
|
|
167
|
+
export class SentinelMark extends HTMLElement {
|
|
168
|
+
static observedAttributes = ["size", "label"];
|
|
169
|
+
connectedCallback() {
|
|
170
|
+
this.render();
|
|
171
|
+
}
|
|
172
|
+
attributeChangedCallback() {
|
|
173
|
+
if (this.isConnected)
|
|
174
|
+
this.render();
|
|
175
|
+
}
|
|
176
|
+
render() {
|
|
177
|
+
this.style.display ||= "inline-flex";
|
|
178
|
+
this.innerHTML = markSvg({ size: this.getAttribute("size") ?? "1em", title: this.getAttribute("label") ?? undefined });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
162
181
|
if (typeof customElements !== "undefined" && customElements.get("sentinel-report") === undefined)
|
|
163
182
|
customElements.define("sentinel-report", SentinelReport);
|
|
183
|
+
if (typeof customElements !== "undefined" && customElements.get("sentinel-mark") === undefined)
|
|
184
|
+
customElements.define("sentinel-mark", SentinelMark);
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
// @universelle/sentinel-report: the core, with no framework. For the ready-made button, `…/element` (any site) or
|
|
2
2
|
// `…/react`; to build your own form, `createReporter` here or `useSentinelReport` in React.
|
|
3
3
|
export { capture, createReporter, DEFAULT_ENDPOINT } from "./core.js";
|
|
4
|
+
export { MARK_SHAPES, markSvg } from "./logo.js";
|
package/dist/logo.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { MARK_SHAPES } from "./mark.js";
|
|
2
|
+
export { MARK_SHAPES };
|
|
3
|
+
/** The mark as an `<svg>`: decorative by default; with `title`, an image with that name for screen readers. */
|
|
4
|
+
export declare function markSvg(options?: {
|
|
5
|
+
size?: number | string;
|
|
6
|
+
title?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
}): string;
|
package/dist/logo.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// SENTINEL's mark for your own branding (a button of your own, a footer…): as an SVG string. The eye and the star take
|
|
2
|
+
// the text colour (currentColor), so it works on light and dark backgrounds; the moon is always the brand's green.
|
|
3
|
+
import { MARK_SHAPES } from "./mark.js";
|
|
4
|
+
export { MARK_SHAPES };
|
|
5
|
+
function escape(text) {
|
|
6
|
+
return text.replace(/[&<>"']/g, (char) => `&#${char.charCodeAt(0)};`);
|
|
7
|
+
}
|
|
8
|
+
/** The mark as an `<svg>`: decorative by default; with `title`, an image with that name for screen readers. */
|
|
9
|
+
export function markSvg(options = {}) {
|
|
10
|
+
const size = options.size ?? 24;
|
|
11
|
+
const label = options.title ? ` role="img" aria-label="${escape(options.title)}"` : ' aria-hidden="true" focusable="false"';
|
|
12
|
+
const cls = options.className ? ` class="${escape(options.className)}"` : "";
|
|
13
|
+
return `<svg${cls} viewBox="0 0 100 100" width="${escape(String(size))}" height="${escape(String(size))}"${label}>${options.title ? `<title>${escape(options.title)}</title>` : ""}${MARK_SHAPES}</svg>`;
|
|
14
|
+
}
|
package/dist/mark.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
+
export declare const MARK_SHAPES = "<path d=\"M14 50 Q50 14 86 50 Q50 86 14 50Z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"7\" stroke-linejoin=\"round\"/><path d=\"M51.1 40.2 A11 11 0 1 0 59.77 53.22 A7.86 7.86 0 0 1 51.1 40.2Z\" fill=\"#3fbf6f\"/><path d=\"M20.9258 28C20.6615 27.82 20.4223 27.6072 20.2143 27.3671C19.9961 26.9641 19.8024 26.549 19.6343 26.1238C17.3476 21.0763 13.2746 16.9981 8.15841 14.6333C7.94143 14.5501 7.7297 14.4545 7.5243 14.347C6.80512 13.9024 6.82832 13.2921 7.57843 12.84C7.74361 12.7589 7.91411 12.6884 8.08881 12.6291C13.3854 10.5226 17.6112 6.45934 19.8431 1.32691C20.0751 0.814541 20.0906 0.0309247 20.8562 0.000785591C21.6217 -0.0293535 21.7377 0.814541 21.9852 1.35704C23.0822 3.90828 24.6892 6.22141 26.7125 8.16176C28.7358 10.1021 31.135 11.6309 33.7704 12.6592C33.9933 12.7315 34.2104 12.8196 34.42 12.9229C35.1933 13.3524 35.1933 13.925 34.42 14.3621C33.9406 14.6333 33.3992 14.8066 32.8734 15.0628C28.0444 17.4681 24.2329 21.4457 22.1012 26.3047C21.9511 26.6872 21.7755 27.0597 21.5753 27.4198C21.3859 27.6401 21.1675 27.8352 20.9258 28Z\" fill=\"currentColor\" transform=\"translate(57.64 44.71) scale(0.3367) translate(-21 -14)\"/>";
|
|
1
2
|
export declare const MARK = "<svg class=\"mark\" part=\"mark\" viewBox=\"0 0 100 100\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M14 50 Q50 14 86 50 Q50 86 14 50Z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"7\" stroke-linejoin=\"round\"/><path d=\"M51.1 40.2 A11 11 0 1 0 59.77 53.22 A7.86 7.86 0 0 1 51.1 40.2Z\" fill=\"#3fbf6f\"/><path d=\"M20.9258 28C20.6615 27.82 20.4223 27.6072 20.2143 27.3671C19.9961 26.9641 19.8024 26.549 19.6343 26.1238C17.3476 21.0763 13.2746 16.9981 8.15841 14.6333C7.94143 14.5501 7.7297 14.4545 7.5243 14.347C6.80512 13.9024 6.82832 13.2921 7.57843 12.84C7.74361 12.7589 7.91411 12.6884 8.08881 12.6291C13.3854 10.5226 17.6112 6.45934 19.8431 1.32691C20.0751 0.814541 20.0906 0.0309247 20.8562 0.000785591C21.6217 -0.0293535 21.7377 0.814541 21.9852 1.35704C23.0822 3.90828 24.6892 6.22141 26.7125 8.16176C28.7358 10.1021 31.135 11.6309 33.7704 12.6592C33.9933 12.7315 34.2104 12.8196 34.42 12.9229C35.1933 13.3524 35.1933 13.925 34.42 14.3621C33.9406 14.6333 33.3992 14.8066 32.8734 15.0628C28.0444 17.4681 24.2329 21.4457 22.1012 26.3047C21.9511 26.6872 21.7755 27.0597 21.5753 27.4198C21.3859 27.6401 21.1675 27.8352 20.9258 28Z\" fill=\"currentColor\" transform=\"translate(57.64 44.71) scale(0.3367) translate(-21 -14)\"/></svg>";
|
package/dist/mark.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
// SENTINEL's mark (generated from the main repo's src/brand.ts by `pnpm brand`; do not edit by hand).
|
|
1
|
+
// SENTINEL's mark (generated from the main repo's src/brand.ts by `pnpm brand`; do not edit by hand). The eye and
|
|
2
|
+
// the star take the text colour (currentColor); the moon is always the brand's green.
|
|
3
|
+
export const MARK_SHAPES = "<path d=\"M14 50 Q50 14 86 50 Q50 86 14 50Z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"7\" stroke-linejoin=\"round\"/><path d=\"M51.1 40.2 A11 11 0 1 0 59.77 53.22 A7.86 7.86 0 0 1 51.1 40.2Z\" fill=\"#3fbf6f\"/><path d=\"M20.9258 28C20.6615 27.82 20.4223 27.6072 20.2143 27.3671C19.9961 26.9641 19.8024 26.549 19.6343 26.1238C17.3476 21.0763 13.2746 16.9981 8.15841 14.6333C7.94143 14.5501 7.7297 14.4545 7.5243 14.347C6.80512 13.9024 6.82832 13.2921 7.57843 12.84C7.74361 12.7589 7.91411 12.6884 8.08881 12.6291C13.3854 10.5226 17.6112 6.45934 19.8431 1.32691C20.0751 0.814541 20.0906 0.0309247 20.8562 0.000785591C21.6217 -0.0293535 21.7377 0.814541 21.9852 1.35704C23.0822 3.90828 24.6892 6.22141 26.7125 8.16176C28.7358 10.1021 31.135 11.6309 33.7704 12.6592C33.9933 12.7315 34.2104 12.8196 34.42 12.9229C35.1933 13.3524 35.1933 13.925 34.42 14.3621C33.9406 14.6333 33.3992 14.8066 32.8734 15.0628C28.0444 17.4681 24.2329 21.4457 22.1012 26.3047C21.9511 26.6872 21.7755 27.0597 21.5753 27.4198C21.3859 27.6401 21.1675 27.8352 20.9258 28Z\" fill=\"currentColor\" transform=\"translate(57.64 44.71) scale(0.3367) translate(-21 -14)\"/>";
|
|
2
4
|
export const MARK = "<svg class=\"mark\" part=\"mark\" viewBox=\"0 0 100 100\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M14 50 Q50 14 86 50 Q50 86 14 50Z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"7\" stroke-linejoin=\"round\"/><path d=\"M51.1 40.2 A11 11 0 1 0 59.77 53.22 A7.86 7.86 0 0 1 51.1 40.2Z\" fill=\"#3fbf6f\"/><path d=\"M20.9258 28C20.6615 27.82 20.4223 27.6072 20.2143 27.3671C19.9961 26.9641 19.8024 26.549 19.6343 26.1238C17.3476 21.0763 13.2746 16.9981 8.15841 14.6333C7.94143 14.5501 7.7297 14.4545 7.5243 14.347C6.80512 13.9024 6.82832 13.2921 7.57843 12.84C7.74361 12.7589 7.91411 12.6884 8.08881 12.6291C13.3854 10.5226 17.6112 6.45934 19.8431 1.32691C20.0751 0.814541 20.0906 0.0309247 20.8562 0.000785591C21.6217 -0.0293535 21.7377 0.814541 21.9852 1.35704C23.0822 3.90828 24.6892 6.22141 26.7125 8.16176C28.7358 10.1021 31.135 11.6309 33.7704 12.6592C33.9933 12.7315 34.2104 12.8196 34.42 12.9229C35.1933 13.3524 35.1933 13.925 34.42 14.3621C33.9406 14.6333 33.3992 14.8066 32.8734 15.0628C28.0444 17.4681 24.2329 21.4457 22.1012 26.3047C21.9511 26.6872 21.7755 27.0597 21.5753 27.4198C21.3859 27.6401 21.1675 27.8352 20.9258 28Z\" fill=\"currentColor\" transform=\"translate(57.64 44.71) scale(0.3367) translate(-21 -14)\"/></svg>";
|
package/dist/react.d.ts
CHANGED
|
@@ -20,6 +20,42 @@ export type ReportButtonProps = {
|
|
|
20
20
|
children?: ReactNode;
|
|
21
21
|
};
|
|
22
22
|
export declare function ReportButton({ site, endpoint, label, heading, position, theme, hideMark, texts, className, style, onSent, onError, children }: ReportButtonProps): import("react").DOMElement<Record<string, unknown>, Element>;
|
|
23
|
+
export type SentinelMarkProps = {
|
|
24
|
+
/** Width and height (a number is px; default `1em`, the size of the text around it). */
|
|
25
|
+
size?: number | string;
|
|
26
|
+
/** A name for screen readers; without it, the mark is decorative. */
|
|
27
|
+
title?: string;
|
|
28
|
+
className?: string;
|
|
29
|
+
style?: CSSProperties;
|
|
30
|
+
};
|
|
31
|
+
/** SENTINEL's mark, for your own button or footer. It takes the text colour (`color`); the moon stays green. */
|
|
32
|
+
export declare function SentinelMark({ size, title, className, style }: SentinelMarkProps): import("react").DOMElement<{
|
|
33
|
+
dangerouslySetInnerHTML: {
|
|
34
|
+
__html: string;
|
|
35
|
+
};
|
|
36
|
+
role: string;
|
|
37
|
+
"aria-label": string;
|
|
38
|
+
"aria-hidden"?: undefined;
|
|
39
|
+
focusable?: undefined;
|
|
40
|
+
viewBox: string;
|
|
41
|
+
width: string | number;
|
|
42
|
+
height: string | number;
|
|
43
|
+
className: string | undefined;
|
|
44
|
+
style: CSSProperties | undefined;
|
|
45
|
+
} | {
|
|
46
|
+
dangerouslySetInnerHTML: {
|
|
47
|
+
__html: string;
|
|
48
|
+
};
|
|
49
|
+
"aria-hidden": boolean;
|
|
50
|
+
focusable: string;
|
|
51
|
+
role?: undefined;
|
|
52
|
+
"aria-label"?: undefined;
|
|
53
|
+
viewBox: string;
|
|
54
|
+
width: string | number;
|
|
55
|
+
height: string | number;
|
|
56
|
+
className: string | undefined;
|
|
57
|
+
style: CSSProperties | undefined;
|
|
58
|
+
}, Element>;
|
|
23
59
|
export type ReportStatus = "idle" | "sending" | "sent" | "error";
|
|
24
60
|
/** For your own form: records the visit from the first render and sends with its context. */
|
|
25
61
|
export declare function useSentinelReport(options: {
|
package/dist/react.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// own form (`useSentinelReport`, which records the context and sends; the look is all yours).
|
|
3
3
|
import { createElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { capture, createReporter } from "./core.js";
|
|
5
|
+
import { MARK_SHAPES } from "./mark.js";
|
|
5
6
|
import "./element.js";
|
|
6
7
|
export function ReportButton({ site, endpoint, label, heading, position, theme, hideMark, texts, className, style, onSent, onError, children }) {
|
|
7
8
|
const ref = useRef(null);
|
|
@@ -25,6 +26,11 @@ export function ReportButton({ site, endpoint, label, heading, position, theme,
|
|
|
25
26
|
attributes["hide-mark"] = "";
|
|
26
27
|
return createElement("sentinel-report", attributes, children === undefined ? undefined : createElement("span", { slot: "trigger", style: { display: "contents" } }, children));
|
|
27
28
|
}
|
|
29
|
+
/** SENTINEL's mark, for your own button or footer. It takes the text colour (`color`); the moon stays green. */
|
|
30
|
+
export function SentinelMark({ size = "1em", title, className, style }) {
|
|
31
|
+
const label = title ? { role: "img", "aria-label": title } : { "aria-hidden": true, focusable: "false" };
|
|
32
|
+
return createElement("svg", { viewBox: "0 0 100 100", width: size, height: size, className, style, ...label, dangerouslySetInnerHTML: { __html: `${title ? `<title>${title.replace(/[&<>"']/g, (char) => `&#${char.charCodeAt(0)};`)}</title>` : ""}${MARK_SHAPES}` } });
|
|
33
|
+
}
|
|
28
34
|
/** For your own form: records the visit from the first render and sends with its context. */
|
|
29
35
|
export function useSentinelReport(options) {
|
|
30
36
|
const reporter = useMemo(() => (typeof window === "undefined" ? undefined : createReporter(options)), [options.site, options.endpoint]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@universelle/sentinel-report",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "SENTINEL's report button: let real users tell you what fails or what they miss, with the technical context of their visit.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|