@tensorfeed/status-widget 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/README.md +72 -0
- package/index.d.ts +18 -0
- package/index.js +96 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# @tensorfeed/status-widget
|
|
2
|
+
|
|
3
|
+
Drop the live [TensorFeed](https://tensorfeed.ai) AI status monitor onto
|
|
4
|
+
any site with one line. Real-time operational status and p95 latency for
|
|
5
|
+
every major AI provider and service, in a self-contained sci-fi console.
|
|
6
|
+
Free, no API key, no tracking, zero dependencies.
|
|
7
|
+
|
|
8
|
+
Live preview and docs: https://tensorfeed.ai/embed
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install @tensorfeed/status-widget
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Use it (any framework, or none)
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
import '@tensorfeed/status-widget';
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<tensorfeed-status accent="blue" poll="30" height="600"></tensorfeed-status>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The custom element renders the widget in a shadow root, so host-page CSS
|
|
27
|
+
cannot affect it and it cannot affect your page.
|
|
28
|
+
|
|
29
|
+
### React
|
|
30
|
+
|
|
31
|
+
No custom element needed. Use the URL helper:
|
|
32
|
+
|
|
33
|
+
```jsx
|
|
34
|
+
import { tensorfeedStatusSrc } from '@tensorfeed/status-widget';
|
|
35
|
+
|
|
36
|
+
<iframe
|
|
37
|
+
src={tensorfeedStatusSrc({ accent: 'blue' })}
|
|
38
|
+
title="TensorFeed live AI status"
|
|
39
|
+
loading="lazy"
|
|
40
|
+
style={{ width: '100%', maxWidth: 720, height: 600, border: 0 }}
|
|
41
|
+
/>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Plain HTML, no build
|
|
45
|
+
|
|
46
|
+
```html
|
|
47
|
+
<script type="module">
|
|
48
|
+
import 'https://esm.sh/@tensorfeed/status-widget';
|
|
49
|
+
</script>
|
|
50
|
+
<tensorfeed-status></tensorfeed-status>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Attributes
|
|
54
|
+
|
|
55
|
+
| Attribute | Values | Default | Notes |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| `accent` | `blue` \| `auto` \| `green` | `blue` | `blue` is a light-blue spine with green status indicators. `auto` greens the whole accent when all systems are nominal. `green` forces green. |
|
|
58
|
+
| `poll` | `5` to `600` | `30` | Client poll interval in seconds. Raise it on low-traffic pages. |
|
|
59
|
+
| `height` | px number or any CSS length | `600` | Widget caps at 720px wide and reflows down to ~320px. |
|
|
60
|
+
| `label` | string | `TensorFeed live AI status` | Accessible iframe title. |
|
|
61
|
+
|
|
62
|
+
## What it shows
|
|
63
|
+
|
|
64
|
+
Operational status and probed p95 latency where TensorFeed measures it,
|
|
65
|
+
real 7-day uptime % otherwise. Vendor status is authoritative; a
|
|
66
|
+
provider with no status source is shown as "no data", never a false
|
|
67
|
+
alarm. "Detail" deep-links to the per-provider page on tensorfeed.ai.
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT. The widget data comes from the public TensorFeed endpoints
|
|
72
|
+
`/api/status/summary` and `/api/status/leaderboard`.
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface TensorfeedStatusOptions {
|
|
2
|
+
/** Accent scheme. Default "blue" (light-blue spine, green status).
|
|
3
|
+
* "auto" greens the whole accent when all systems are nominal.
|
|
4
|
+
* "green" forces green. */
|
|
5
|
+
accent?: 'blue' | 'auto' | 'green';
|
|
6
|
+
/** Client poll interval in seconds (5 to 600). Default 30. */
|
|
7
|
+
poll?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Build the widget iframe URL with clean, default-aware query params. */
|
|
11
|
+
export function tensorfeedStatusSrc(opts?: TensorfeedStatusOptions): string;
|
|
12
|
+
|
|
13
|
+
/** Register the <tensorfeed-status> custom element. Idempotent; a no-op
|
|
14
|
+
* in non-DOM (SSR) environments. Called automatically on import. */
|
|
15
|
+
export function defineTensorFeedStatus(tag?: string): void;
|
|
16
|
+
|
|
17
|
+
/** The custom element class, exported for advanced registration. */
|
|
18
|
+
export class TensorFeedStatus extends HTMLElement {}
|
package/index.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tensorfeed/status-widget
|
|
3
|
+
*
|
|
4
|
+
* The live TensorFeed AI status monitor, as a zero-dependency, framework
|
|
5
|
+
* agnostic custom element plus a tiny helper. The widget itself is a
|
|
6
|
+
* fully self-contained page at tensorfeed.ai/widget/status; this package
|
|
7
|
+
* just makes embedding it a one-liner and keeps the query params honest.
|
|
8
|
+
*
|
|
9
|
+
* HTML / any framework:
|
|
10
|
+
* import '@tensorfeed/status-widget';
|
|
11
|
+
* <tensorfeed-status accent="blue" poll="30" height="600"></tensorfeed-status>
|
|
12
|
+
*
|
|
13
|
+
* React (no custom element needed):
|
|
14
|
+
* import { tensorfeedStatusSrc } from '@tensorfeed/status-widget';
|
|
15
|
+
* <iframe src={tensorfeedStatusSrc({ accent: 'blue' })}
|
|
16
|
+
* title="TensorFeed live AI status"
|
|
17
|
+
* style={{ width: '100%', height: 600, border: 0, maxWidth: 720 }} />
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const BASE = 'https://tensorfeed.ai/widget/status';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Build the widget iframe URL. Mirrors the widget's own defaults so the
|
|
24
|
+
* URL stays clean: accent defaults to blue, poll to 30s; only
|
|
25
|
+
* non-defaults are added as query params.
|
|
26
|
+
*/
|
|
27
|
+
export function tensorfeedStatusSrc(opts = {}) {
|
|
28
|
+
const params = new URLSearchParams();
|
|
29
|
+
const accent = String(opts.accent || 'blue').toLowerCase();
|
|
30
|
+
if (accent === 'auto' || accent === 'green') params.set('accent', accent);
|
|
31
|
+
const poll = Number(opts.poll);
|
|
32
|
+
if (Number.isFinite(poll) && poll >= 5 && poll <= 600 && poll !== 30) {
|
|
33
|
+
params.set('poll', String(Math.round(poll)));
|
|
34
|
+
}
|
|
35
|
+
params.set('utm_source', 'npm');
|
|
36
|
+
params.set('utm_medium', 'component');
|
|
37
|
+
return `${BASE}?${params.toString()}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resolveHeight(h) {
|
|
41
|
+
if (h == null || h === '') return '600px';
|
|
42
|
+
return /^\d+$/.test(String(h)) ? `${h}px` : String(h);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Guarded base so `import`-ing this package never throws in Node / SSR
|
|
46
|
+
// (Next.js, etc.) where HTMLElement does not exist. The element only
|
|
47
|
+
// actually does anything once defineTensorFeedStatus runs in a browser.
|
|
48
|
+
const ElementBase = typeof HTMLElement !== 'undefined' ? HTMLElement : class {};
|
|
49
|
+
|
|
50
|
+
class TensorFeedStatus extends ElementBase {
|
|
51
|
+
static get observedAttributes() {
|
|
52
|
+
return ['accent', 'poll', 'height', 'label'];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
connectedCallback() {
|
|
56
|
+
if (!this.shadowRoot) this.attachShadow({ mode: 'open' });
|
|
57
|
+
this._render();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
attributeChangedCallback() {
|
|
61
|
+
if (this.shadowRoot) this._render();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_render() {
|
|
65
|
+
const src = tensorfeedStatusSrc({
|
|
66
|
+
accent: this.getAttribute('accent'),
|
|
67
|
+
poll: this.getAttribute('poll'),
|
|
68
|
+
});
|
|
69
|
+
const height = resolveHeight(this.getAttribute('height'));
|
|
70
|
+
const label = this.getAttribute('label') || 'TensorFeed live AI status';
|
|
71
|
+
// Shadow DOM keeps host page CSS from touching the iframe and vice
|
|
72
|
+
// versa. The widget brings all its own styling.
|
|
73
|
+
this.shadowRoot.innerHTML =
|
|
74
|
+
`<style>:host{display:block;width:100%}` +
|
|
75
|
+
`iframe{display:block;width:100%;max-width:720px;height:${height};border:0;` +
|
|
76
|
+
`border-radius:6px 28px 28px 6px;overflow:hidden}</style>` +
|
|
77
|
+
`<iframe src="${src}" title="${label.replace(/"/g, '"')}" ` +
|
|
78
|
+
`loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Register the <tensorfeed-status> custom element. Safe to call more
|
|
84
|
+
* than once and a no-op in non-DOM environments (SSR).
|
|
85
|
+
*/
|
|
86
|
+
export function defineTensorFeedStatus(tag = 'tensorfeed-status') {
|
|
87
|
+
if (typeof window === 'undefined' || typeof customElements === 'undefined') return;
|
|
88
|
+
if (!customElements.get(tag)) customElements.define(tag, TensorFeedStatus);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Importing the package auto-registers the element (declared as a side
|
|
92
|
+
// effect in package.json). React-only users can ignore this and use
|
|
93
|
+
// tensorfeedStatusSrc directly.
|
|
94
|
+
defineTensorFeedStatus();
|
|
95
|
+
|
|
96
|
+
export { TensorFeedStatus };
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tensorfeed/status-widget",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Drop the live TensorFeed AI status monitor onto any site with one line. Zero dependencies, framework agnostic web component plus a helper for React and plain HTML.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.js",
|
|
7
|
+
"module": "./index.js",
|
|
8
|
+
"types": "./index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./index.d.ts",
|
|
12
|
+
"import": "./index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["index.js", "index.d.ts", "README.md"],
|
|
16
|
+
"sideEffects": ["./index.js"],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"tensorfeed",
|
|
19
|
+
"ai",
|
|
20
|
+
"llm",
|
|
21
|
+
"status",
|
|
22
|
+
"monitor",
|
|
23
|
+
"uptime",
|
|
24
|
+
"widget",
|
|
25
|
+
"web-component",
|
|
26
|
+
"embed",
|
|
27
|
+
"openai",
|
|
28
|
+
"anthropic",
|
|
29
|
+
"claude",
|
|
30
|
+
"gemini"
|
|
31
|
+
],
|
|
32
|
+
"homepage": "https://tensorfeed.ai/embed",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/RipperMercs/tensorfeed.git",
|
|
36
|
+
"directory": "packages/status-widget"
|
|
37
|
+
},
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18"
|
|
44
|
+
}
|
|
45
|
+
}
|