brandbot 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 ADDED
@@ -0,0 +1,42 @@
1
+ MIT License (applies to all code in this package)
2
+
3
+ Copyright (c) 2026 Aidan Zarski
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.
22
+
23
+ --------------------------------------------------------------------------
24
+
25
+ 3D Model provenance (src/nexbot-model.js)
26
+
27
+ The bundled robot model is "NEXBOT - robot character concept", a Spline
28
+ Community file created by the Spline team, exported to glTF.
29
+
30
+ Source: https://community.spline.design/file/87343305-102d-4670-bb8e-38252064de27
31
+ License: CC0 1.0 Universal (public domain dedication)
32
+ https://creativecommons.org/publicdomain/zero/1.0/
33
+
34
+ Spline's Community Platform documentation
35
+ (https://docs.spline.design/doc/community-platform/docSaqY7mlFz) states:
36
+ "All the community files are licensed under CC0 1.0 Universal Deed."
37
+ (Verified 2026-06-12. Archived evidence:
38
+ https://web.archive.org/web/20260612220813/https://docs.spline.design/basics/community-platform
39
+ https://web.archive.org/web/20260612220949/https://community.spline.design/file/87343305-102d-4670-bb8e-38252064de27 )
40
+
41
+ CC0 places the work in the public domain: it may be used, modified, and
42
+ redistributed for any purpose, including commercially, without attribution.
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # BrandBot ๐Ÿค–
2
+
3
+ **A 3D robot mascot you brand with your own logo and colors.** A drop-in React component that gestures, blinks, and follows the visitor's cursor. It renders with [Three.js](https://threejs.org), is written in TypeScript with full type declarations, and bundles the model โ€” so there's nothing to configure or host. Works in any React app (see the Next.js note below).
4
+
5
+ **[โ–ถ Live demo](https://aidan945.github.io/brandbot/demo/)** ยท **[Configure & copy code](https://aidan945.github.io/brandbot/demo/configure.html)**
6
+
7
+ ![BrandBot](./preview.png)
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install brandbot three react
13
+ ```
14
+
15
+ ## Use
16
+
17
+ ```jsx
18
+ import { BrandBot } from 'brandbot';
19
+
20
+ export function Hero() {
21
+ return (
22
+ <div style={{ height: 600 }}>
23
+ <BrandBot primary="#13294b" eyes="#7fd4ff" logoText="ACME" />
24
+ </div>
25
+ );
26
+ }
27
+ ```
28
+
29
+ That's it. The component fills its parent, renders on a transparent background, and starts moving. **The default needs no extra CSS** โ€” drop it in a sized box and it looks right.
30
+
31
+ ### Full-bleed hero (optional)
32
+
33
+ To have the robot rise from the bottom of a full-height hero with its legs fading into the page (like the [demo](https://aidan945.github.io/brandbot/demo/)), wrap it:
34
+
35
+ ```jsx
36
+ <div style={{ height: '100vh', overflow: 'hidden',
37
+ WebkitMaskImage: 'linear-gradient(180deg,#000 88%,transparent 100%)' }}>
38
+ <div style={{ position: 'absolute', inset: 0,
39
+ transform: 'scale(1.1) translateY(8%)', transformOrigin: 'center top' }}>
40
+ <BrandBot />
41
+ </div>
42
+ </div>
43
+ ```
44
+
45
+ **By default it stays put** โ€” locked in its box, facing forward, following the cursor. It does *not* let visitors drag-rotate it (rarely what you want in a hero or card). Add `orbit` to enable drag-to-rotate for demos.
46
+
47
+ ## Brand it
48
+
49
+ Every color and the chest logo is a prop. Drop in your logo as text, an uploaded image (inlined as base64), or a URL:
50
+
51
+ ```jsx
52
+ <BrandBot
53
+ primary="#0e1a2b"
54
+ accent="#1d3350"
55
+ eyes="#7fd4ff"
56
+ logoImage="/logo.png" // or logoText="ACME"
57
+ />
58
+ ```
59
+
60
+ The **[configurator](https://aidan945.github.io/brandbot/demo/configure.html)** lets you tweak everything visually and copies the exact JSX for your setup.
61
+
62
+ ## Props
63
+
64
+ | Prop | Type | Default | |
65
+ |---|---|---|---|
66
+ | `primary` | color | near-black | body shell + carbon parts |
67
+ | `accent` | color | dark metal | joints, mechanical internals |
68
+ | `visor` | color | chrome | face plate |
69
+ | `hands` | color | chrome | hands |
70
+ | `eyes` | color | white-blue | LED dot-matrix eyes |
71
+ | `logoText` | string | โ€” | text logo on the chest (1โ€“3 words) |
72
+ | `logoColor` | color | white | |
73
+ | `logoImage` | url | โ€” | image logo (transparent PNG/SVG best); overrides text |
74
+ | `legs` | boolean | `true` | `false` shows the upper body only |
75
+ | `orbit` | boolean | `false` | `true` lets visitors drag to rotate (for demos) |
76
+ | `intro` | boolean | `true` | one-time zoom-in when the model loads |
77
+ | `trackPointer` | `'window' \| 'element' \| false` | `'window'` | what the head watches |
78
+ | `shadow` | boolean | `true` | soft ground shadow |
79
+ | `modelUrl` | url | bundled | load a different glTF instead of the bundled model |
80
+ | `className`, `style` | | | passed to the wrapper div |
81
+
82
+ All color/logo/legs props are live โ€” update them in state and the robot changes instantly. (`orbit` and `intro` are set once at mount.)
83
+
84
+ Imperative handle via ref:
85
+
86
+ ```jsx
87
+ const bot = useRef(null);
88
+ <BrandBot ref={bot} />
89
+ <button onClick={() => bot.current.spin()}>Spin</button> {/* one full turn */}
90
+ <button onClick={() => bot.current.replay()}>Replay</button> {/* the zoom-in intro */}
91
+ ```
92
+
93
+ ## Next.js
94
+
95
+ WebGL can't server-render, so load it client-side:
96
+
97
+ ```jsx
98
+ import dynamic from 'next/dynamic';
99
+ const BrandBot = dynamic(() => import('brandbot').then(m => m.BrandBot), { ssr: false });
100
+ ```
101
+
102
+ ## Without React
103
+
104
+ The engine is framework-agnostic:
105
+
106
+ ```js
107
+ import { createBrandbot } from 'brandbot/core';
108
+ import model from 'brandbot/model';
109
+
110
+ const robot = createBrandbot(document.querySelector('#hero'), { gltf: model });
111
+ robot.set({ primary: '#13294b', logoText: 'ACME' });
112
+ ```
113
+
114
+ ## Demo
115
+
116
+ The `demo/` folder is a no-build static site (works on GitHub Pages):
117
+
118
+ - **`demo/index.html`** โ€” showcase: the robot rising from a light-grid hero with the zoom-in intro and cursor tracking.
119
+ - **`demo/configure.html`** โ€” live configurator with tabbed code, "copy code", and "copy prompt for agent".
120
+
121
+ Run `python3 -m http.server` in the package folder and open `demo/index.html`.
122
+
123
+ ## Development
124
+
125
+ ```bash
126
+ npm install # toolchain (typescript, @types/three, @types/react)
127
+ npm run build # compile src/*.ts โ†’ dist/ (+ .d.ts) and copy the model
128
+ npm run typecheck
129
+ ```
130
+
131
+ Source lives in `src/` (TypeScript). The committed `dist/` is what the demo and
132
+ npm package consume; rebuild it after changing `src/`.
133
+
134
+ ## License
135
+
136
+ - **Code:** MIT.
137
+ - **Model:** the bundled robot is the [NEXBOT character](https://community.spline.design/file/87343305-102d-4670-bb8e-38252064de27), a Spline Community file under [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) (public domain). Per [Spline's docs](https://docs.spline.design/doc/community-platform/docSaqY7mlFz): "All the community files are licensed under CC0 1.0 Universal Deed." Free to use, modify, and redistribute โ€” including commercially โ€” with no attribution required. Credited anyway, because it's a great model.
@@ -0,0 +1,40 @@
1
+ import type { CSSProperties } from 'react';
2
+ import type { BrandBotHandle, Color, TrackPointer } from './types.js';
3
+ export interface BrandBotProps {
4
+ primary?: Color;
5
+ accent?: Color;
6
+ visor?: Color;
7
+ hands?: Color;
8
+ eyes?: Color;
9
+ logoText?: string;
10
+ logoColor?: Color;
11
+ /** Image logo: URL, data URI, or object URL. Overrides `logoText`. */
12
+ logoImage?: string | null;
13
+ /** `false` shows the upper body only. */
14
+ legs?: boolean;
15
+ /** Let visitors drag to rotate (for demos). */
16
+ orbit?: boolean;
17
+ /** One-time zoom-in when the model loads. */
18
+ intro?: boolean;
19
+ /** What the head watches. */
20
+ trackPointer?: TrackPointer;
21
+ /** Soft ground shadow. */
22
+ shadow?: boolean;
23
+ /** Load a different glTF instead of the bundled model. */
24
+ modelUrl?: string;
25
+ className?: string;
26
+ style?: CSSProperties;
27
+ }
28
+ /**
29
+ * <BrandBot primary="#13294b" eyes="#7fd4ff" logoText="ACME"
30
+ * style={{ height: 600 }} />
31
+ *
32
+ * A 3D robot mascot you brand with your own logo and colors. It sits locked in
33
+ * its box, faces forward, and follows the cursor. It does NOT let visitors
34
+ * drag-rotate unless you pass `orbit` (handy for demos).
35
+ *
36
+ * All color/logo props are live โ€” change them and the robot updates.
37
+ * Use a ref for the imperative bits: ref.current.spin() / ref.current.replay()
38
+ */
39
+ export declare const BrandBot: import("react").ForwardRefExoticComponent<BrandBotProps & import("react").RefAttributes<BrandBotHandle>>;
40
+ export default BrandBot;
@@ -0,0 +1,53 @@
1
+ import { createElement, forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
2
+ import { createBrandbot } from './robot-core.js';
3
+ import model from './nexbot-model.js';
4
+ /**
5
+ * <BrandBot primary="#13294b" eyes="#7fd4ff" logoText="ACME"
6
+ * style={{ height: 600 }} />
7
+ *
8
+ * A 3D robot mascot you brand with your own logo and colors. It sits locked in
9
+ * its box, faces forward, and follows the cursor. It does NOT let visitors
10
+ * drag-rotate unless you pass `orbit` (handy for demos).
11
+ *
12
+ * All color/logo props are live โ€” change them and the robot updates.
13
+ * Use a ref for the imperative bits: ref.current.spin() / ref.current.replay()
14
+ */
15
+ export const BrandBot = forwardRef(function BrandBot(props, ref) {
16
+ const { primary, accent, visor, hands, eyes, logoText, logoColor, logoImage, legs = true, trackPointer = 'window', shadow = true, modelUrl, orbit = false, intro = true, className, style, } = props;
17
+ const el = useRef(null);
18
+ const bot = useRef(null);
19
+ useEffect(() => {
20
+ if (!el.current)
21
+ return;
22
+ bot.current = createBrandbot(el.current, {
23
+ gltf: modelUrl ? null : model,
24
+ modelUrl,
25
+ trackPointer,
26
+ shadow,
27
+ legs,
28
+ orbit,
29
+ intro,
30
+ ...(primary && { primary }), ...(accent && { accent }),
31
+ ...(visor && { visor }), ...(hands && { hands }), ...(eyes && { eyes }),
32
+ ...(logoText !== undefined && { logoText }),
33
+ ...(logoColor && { logoColor }), ...(logoImage && { logoImage }),
34
+ });
35
+ return () => bot.current?.dispose();
36
+ // structural options rebuild the scene; colors/logo flow through below
37
+ // eslint-disable-next-line react-hooks/exhaustive-deps
38
+ }, [modelUrl, trackPointer, shadow, orbit, intro]);
39
+ useEffect(() => {
40
+ bot.current?.set({ primary, accent, visor, hands, eyes, logoText, logoColor, logoImage, legs });
41
+ }, [primary, accent, visor, hands, eyes, logoText, logoColor, logoImage, legs]);
42
+ useImperativeHandle(ref, () => ({
43
+ spin: () => bot.current?.spin(),
44
+ replay: () => bot.current?.replay(),
45
+ set: (o) => bot.current?.set(o),
46
+ }), []);
47
+ return createElement('div', {
48
+ ref: el,
49
+ className,
50
+ style: { width: '100%', height: '100%', ...style },
51
+ });
52
+ });
53
+ export default BrandBot;
@@ -0,0 +1,4 @@
1
+ export { BrandBot, default } from './BrandBot.js';
2
+ export { createBrandbot } from './robot-core.js';
3
+ export type { BrandbotOptions, BrandbotUpdate, BrandbotInstance, BrandBotHandle, TrackPointer, Color, } from './types.js';
4
+ export type { BrandBotProps } from './BrandBot.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { BrandBot, default } from './BrandBot.js';
2
+ export { createBrandbot } from './robot-core.js';
@@ -0,0 +1,3 @@
1
+ // The bundled NEXBOT model (Spline Community file, CC0) as parsed glTF JSON.
2
+ declare const model: unknown;
3
+ export default model;