lightnode-sdk 0.8.2 → 0.8.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/dist/add.d.ts +1 -1
- package/dist/add.js +242 -166
- package/package.json +1 -1
package/dist/add.d.ts
CHANGED
|
@@ -119,7 +119,7 @@ export interface LayoutPatch {
|
|
|
119
119
|
* Returns what happened so the CLI can report it; never throws.
|
|
120
120
|
*/
|
|
121
121
|
export declare function patchLayoutWithProviders(cwd?: string): LayoutPatch;
|
|
122
|
-
export declare const SCAFFOLD_GLOBALS_CSS = "@import \"tailwindcss\";\n\n/* dark mode via .dark class (we default the app to dark) */\n@custom-variant dark (&:is(.dark, .dark *));\n\n/* design tokens (light) - ported from lcai-chat-v2 */\n:root {\n font-family: var(--font-inter), ui-sans-serif, system-ui, sans-serif;\n\n --background: #ffffff;\n --primary: #6767e9;\n --primary-600: #5a4fd8;\n --foreground: #09090b;\n --card: #ffffff;\n --card-foreground: #09090b;\n --popover: #ffffff;\n --popover-foreground: hsl(240 10% 3.9%);\n --primary-foreground: #fafafa;\n --secondary: hsl(240 4.8% 95.9%);\n --secondary-foreground: hsl(240 5.9% 10%);\n --muted: hsl(240 4.8% 95.9%);\n --muted-foreground: hsl(240 3.8% 46.1%);\n --accent: hsl(240 4.8% 95.9%);\n --accent-foreground: hsl(240 5.9% 10%);\n --destructive: #ef4d6a;\n --destructive-foreground: hsl(0 0% 98%);\n --success: #15bd77;\n --warning: #eaa53d;\n --border: hsl(240 5.9% 90%);\n --input: hsl(240 5.9% 90%);\n --ring: hsl(240 10% 3.9%);\n --radius: 0.625rem;\n\n --surface-base-subtle: rgba(34, 35, 42, 0.02);\n --surface-base-faint: rgba(14, 18, 27, 0.04);\n --surface-base-light: rgba(204, 206, 239, 0.16);\n --surface-elevation-light: #ffffff;\n\n --content-primary: #0f0f14;\n --content-default: #373842;\n --content-soft: #656678;\n --content-extraLight: #9798b6;\n\n --border-soft: rgba(14, 18, 27, 0.08);\n --border-light: rgba(14, 18, 27, 0.06);\n}\n\n/* design tokens (dark) */\n.dark {\n --background: #070710;\n --foreground: hsl(0 0% 98%);\n --card: #0f0f14;\n --card-foreground: hsl(0 0% 98%);\n --popover: #0f0f14;\n --popover-foreground: hsl(0 0% 98%);\n --primary: #7064e9;\n --primary-600: #8c71f6;\n --primary-foreground: hsl(0 0% 98%);\n --secondary: hsl(240 3.7% 15.9%);\n --secondary-foreground: hsl(0 0% 98%);\n --muted: hsl(240 3.7% 15.9%);\n --muted-foreground: hsl(240 5% 64.9%);\n --accent: hsl(240 3.7% 15.9%);\n --accent-foreground: hsl(0 0% 98%);\n --destructive: #fb5a76;\n --destructive-foreground: hsl(0 0% 98%);\n --success: #22d68a;\n --warning: #f5be5c;\n --border: hsl(240 3.7% 15.9%);\n --input: hsl(240 3.7% 15.9%);\n --ring: hsl(240 4.9% 83.9%);\n\n --surface-base-subtle: rgba(204, 206, 239, 0.02);\n --surface-base-faint: rgba(204, 206, 239, 0.04);\n --surface-base-light: rgba(204, 206, 239, 0.08);\n --surface-elevation-light: #0f0f14;\n\n --content-primary: #cccef0;\n --content-default: #9798b6;\n --content-soft: rgba(154, 156, 207, 0.8);\n --content-extraLight: #9798b6;\n\n --border-soft: rgba(204, 206, 239, 0.12);\n --border-light: rgba(204, 206, 239, 0.08);\n}\n\n/* theme mapping (Tailwind v4 @theme) */\n@theme inline {\n --radius-md: calc(var(--radius) - 2px);\n --radius-sm: calc(var(--radius) - 4px);\n --radius-lg: var(--radius);\n\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-600: var(--primary-600);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-destructive-foreground: var(--destructive-foreground);\n --color-success: var(--success);\n --color-warning: var(--warning);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n\n --color-surface-base-subtle: var(--surface-base-subtle);\n --color-surface-base-faint: var(--surface-base-faint);\n --color-surface-base-light: var(--surface-base-light);\n --color-surface-elevation-light: var(--surface-elevation-light);\n --color-surface-base-brand-default: #693ee0;\n --color-surface-base-brand-strong: #8c71f6;\n\n --color-content-primary: var(--content-primary);\n --color-content-default: var(--content-default);\n --color-content-soft: var(--content-soft);\n --color-content-extraLight: var(--content-extraLight);\n\n --color-bdr-soft: var(--border-soft);\n --color-bdr-light: var(--border-light);\n\n --color-gradient-primary: linear-gradient(270deg, #7064e9 0%, #dd00ac 100%);\n}\n\n@layer base {\n * {\n border-color: var(--border);\n }\n body {\n background-color: var(--background);\n color: var(--foreground);\n overflow-x: hidden;\n }\n html {\n overflow-x: hidden;\n }\n button {\n cursor: pointer;\n }\n button:disabled {\n cursor: not-allowed;\n }\n /* visible keyboard focus across interactive elements */\n a:focus-visible,\n button:focus-visible,\n input:focus-visible,\n select:focus-visible,\n textarea:focus-visible {\n outline: 2px solid var(--primary);\n outline-offset: 2px;\n border-radius: 6px;\n }\n}\n\n/* respect reduced-motion: kill non-essential animation */\n@media (prefers-reduced-motion: reduce) {\n *,\n ::before,\n ::after {\n animation-duration: 0.001ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.001ms !important;\n scroll-behavior: auto !important;\n }\n}\n\n/* ambient app background (gradient mesh behind everything) */\nbody::before {\n content: \"\";\n position: fixed;\n inset: 0;\n z-index: -1;\n pointer-events: none;\n background:\n radial-gradient(60% 50% at 50% -6%, rgba(221, 0, 172, 0.10), transparent 60%),\n radial-gradient(55% 45% at 12% -8%, rgba(112, 100, 233, 0.14), transparent 60%),\n radial-gradient(50% 40% at 88% -2%, rgba(112, 100, 233, 0.12), transparent 60%),\n radial-gradient(45% 45% at 50% 115%, rgba(112, 100, 233, 0.07), transparent 60%);\n}\n.dark body::before {\n background:\n radial-gradient(60% 50% at 50% -6%, rgba(221, 0, 172, 0.12), transparent 60%),\n radial-gradient(55% 45% at 12% -8%, rgba(112, 100, 233, 0.18), transparent 60%),\n radial-gradient(50% 40% at 88% -2%, rgba(112, 100, 233, 0.14), transparent 60%),\n radial-gradient(45% 45% at 50% 118%, rgba(112, 100, 233, 0.10), transparent 60%);\n}\n\n/* signature lcai gradient (primary buttons / accents) */\n.bg-gradient-primary {\n background-image: var(--color-gradient-primary);\n}\n.text-gradient {\n background: linear-gradient(94deg, #dd00ac 10%, #7130c3 53%, #7064e9 96%);\n -webkit-background-clip: text;\n background-clip: text;\n color: transparent;\n}\n\n/* minimal scrollbar */\n::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n}\n::-webkit-scrollbar-track {\n background: transparent;\n}\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 3px;\n}\n* {\n scrollbar-width: thin;\n scrollbar-color: var(--border) transparent;\n}\n";
|
|
122
|
+
export declare const SCAFFOLD_GLOBALS_CSS = "@import \"tailwindcss\";\n\n/* let Tailwind v4 see streamdown's classes so markdown answers are styled\n (harmless when streamdown isn't installed - the path just matches nothing) */\n@source \"../node_modules/streamdown/dist/index.js\";\n\n/* dark mode via .dark class (we default the app to dark) */\n@custom-variant dark (&:is(.dark, .dark *));\n\n/* design tokens (light) - ported from lcai-chat-v2 */\n:root {\n font-family: var(--font-inter), ui-sans-serif, system-ui, sans-serif;\n\n --background: #ffffff;\n --primary: #6767e9;\n --primary-600: #5a4fd8;\n --foreground: #09090b;\n --card: #ffffff;\n --card-foreground: #09090b;\n --popover: #ffffff;\n --popover-foreground: hsl(240 10% 3.9%);\n --primary-foreground: #fafafa;\n --secondary: hsl(240 4.8% 95.9%);\n --secondary-foreground: hsl(240 5.9% 10%);\n --muted: hsl(240 4.8% 95.9%);\n --muted-foreground: hsl(240 3.8% 46.1%);\n --accent: hsl(240 4.8% 95.9%);\n --accent-foreground: hsl(240 5.9% 10%);\n --destructive: #ef4d6a;\n --destructive-foreground: hsl(0 0% 98%);\n --success: #15bd77;\n --warning: #eaa53d;\n --border: hsl(240 5.9% 90%);\n --input: hsl(240 5.9% 90%);\n --ring: hsl(240 10% 3.9%);\n --radius: 0.625rem;\n\n --surface-base-subtle: rgba(34, 35, 42, 0.02);\n --surface-base-faint: rgba(14, 18, 27, 0.04);\n --surface-base-light: rgba(204, 206, 239, 0.16);\n --surface-elevation-light: #ffffff;\n\n --content-primary: #0f0f14;\n --content-default: #373842;\n --content-soft: #656678;\n --content-extraLight: #9798b6;\n\n --border-soft: rgba(14, 18, 27, 0.08);\n --border-light: rgba(14, 18, 27, 0.06);\n}\n\n/* design tokens (dark) */\n.dark {\n --background: #070710;\n --foreground: hsl(0 0% 98%);\n --card: #0f0f14;\n --card-foreground: hsl(0 0% 98%);\n --popover: #0f0f14;\n --popover-foreground: hsl(0 0% 98%);\n --primary: #7064e9;\n --primary-600: #8c71f6;\n --primary-foreground: hsl(0 0% 98%);\n --secondary: hsl(240 3.7% 15.9%);\n --secondary-foreground: hsl(0 0% 98%);\n --muted: hsl(240 3.7% 15.9%);\n --muted-foreground: hsl(240 5% 64.9%);\n --accent: hsl(240 3.7% 15.9%);\n --accent-foreground: hsl(0 0% 98%);\n --destructive: #fb5a76;\n --destructive-foreground: hsl(0 0% 98%);\n --success: #22d68a;\n --warning: #f5be5c;\n --border: hsl(240 3.7% 15.9%);\n --input: hsl(240 3.7% 15.9%);\n --ring: hsl(240 4.9% 83.9%);\n\n --surface-base-subtle: rgba(204, 206, 239, 0.02);\n --surface-base-faint: rgba(204, 206, 239, 0.04);\n --surface-base-light: rgba(204, 206, 239, 0.08);\n --surface-elevation-light: #0f0f14;\n\n --content-primary: #cccef0;\n --content-default: #9798b6;\n --content-soft: rgba(154, 156, 207, 0.8);\n --content-extraLight: #9798b6;\n\n --border-soft: rgba(204, 206, 239, 0.12);\n --border-light: rgba(204, 206, 239, 0.08);\n}\n\n/* theme mapping (Tailwind v4 @theme) */\n@theme inline {\n --radius-md: calc(var(--radius) - 2px);\n --radius-sm: calc(var(--radius) - 4px);\n --radius-lg: var(--radius);\n\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-600: var(--primary-600);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-destructive-foreground: var(--destructive-foreground);\n --color-success: var(--success);\n --color-warning: var(--warning);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n\n --color-surface-base-subtle: var(--surface-base-subtle);\n --color-surface-base-faint: var(--surface-base-faint);\n --color-surface-base-light: var(--surface-base-light);\n --color-surface-elevation-light: var(--surface-elevation-light);\n --color-surface-base-brand-default: #693ee0;\n --color-surface-base-brand-strong: #8c71f6;\n\n --color-content-primary: var(--content-primary);\n --color-content-default: var(--content-default);\n --color-content-soft: var(--content-soft);\n --color-content-extraLight: var(--content-extraLight);\n\n --color-bdr-soft: var(--border-soft);\n --color-bdr-light: var(--border-light);\n\n --color-gradient-primary: linear-gradient(270deg, #7064e9 0%, #dd00ac 100%);\n}\n\n@layer base {\n * {\n border-color: var(--border);\n }\n body {\n background-color: var(--background);\n color: var(--foreground);\n overflow-x: hidden;\n }\n html {\n overflow-x: hidden;\n }\n button {\n cursor: pointer;\n }\n button:disabled {\n cursor: not-allowed;\n }\n /* visible keyboard focus across interactive elements */\n a:focus-visible,\n button:focus-visible,\n input:focus-visible,\n select:focus-visible,\n textarea:focus-visible {\n outline: 2px solid var(--primary);\n outline-offset: 2px;\n border-radius: 6px;\n }\n}\n\n/* respect reduced-motion: kill non-essential animation */\n@media (prefers-reduced-motion: reduce) {\n *,\n ::before,\n ::after {\n animation-duration: 0.001ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.001ms !important;\n scroll-behavior: auto !important;\n }\n}\n\n/* ambient app background (gradient mesh behind everything) */\nbody::before {\n content: \"\";\n position: fixed;\n inset: 0;\n z-index: -1;\n pointer-events: none;\n background:\n radial-gradient(60% 50% at 50% -6%, rgba(221, 0, 172, 0.10), transparent 60%),\n radial-gradient(55% 45% at 12% -8%, rgba(112, 100, 233, 0.14), transparent 60%),\n radial-gradient(50% 40% at 88% -2%, rgba(112, 100, 233, 0.12), transparent 60%),\n radial-gradient(45% 45% at 50% 115%, rgba(112, 100, 233, 0.07), transparent 60%);\n}\n.dark body::before {\n background:\n radial-gradient(60% 50% at 50% -6%, rgba(221, 0, 172, 0.12), transparent 60%),\n radial-gradient(55% 45% at 12% -8%, rgba(112, 100, 233, 0.18), transparent 60%),\n radial-gradient(50% 40% at 88% -2%, rgba(112, 100, 233, 0.14), transparent 60%),\n radial-gradient(45% 45% at 50% 118%, rgba(112, 100, 233, 0.10), transparent 60%);\n}\n\n/* signature lcai gradient (primary buttons / accents) */\n.bg-gradient-primary {\n background-image: var(--color-gradient-primary);\n}\n/* the lcai-chat connect-button gradient (pink -> purple) */\n.bg-gradient-btn {\n background-image: linear-gradient(94deg, #dd00ac 10.66%, #7130c3 53.03%, #410093 96.34%);\n background-size: 200% auto;\n}\n@keyframes pulse-dot {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n}\n.animate-pulse-dot {\n animation: pulse-dot 1.6s ease-in-out infinite;\n}\n.text-gradient {\n background: linear-gradient(94deg, #dd00ac 10%, #7130c3 53%, #7064e9 96%);\n -webkit-background-clip: text;\n background-clip: text;\n color: transparent;\n}\n\n/* minimal scrollbar */\n::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n}\n::-webkit-scrollbar-track {\n background: transparent;\n}\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 3px;\n}\n* {\n scrollbar-width: thin;\n scrollbar-color: var(--border) transparent;\n}\n";
|
|
123
123
|
export interface ScaffoldWiring {
|
|
124
124
|
written: WrittenFile[];
|
|
125
125
|
/** The route now also served at `/` (e.g. "/chat-web3"), or null if nothing was wired. */
|
package/dist/add.js
CHANGED
|
@@ -961,9 +961,10 @@ const NEXTJS_CHAT_WEB3_PAGE = `// app/chat-web3/page.tsx
|
|
|
961
961
|
// turn plus a small gas amount.
|
|
962
962
|
"use client";
|
|
963
963
|
|
|
964
|
-
import { useEffect, useState } from "react";
|
|
964
|
+
import { useEffect, useRef, useState } from "react";
|
|
965
965
|
import { useAccount, useWalletClient, usePublicClient } from "wagmi";
|
|
966
966
|
import { siweSignIn, GatewayClient, runInference, estimateJobFee, NETWORKS } from "lightnode-sdk";
|
|
967
|
+
import { Streamdown } from "streamdown";
|
|
967
968
|
import { ConnectButton } from "@/components/connect-button";
|
|
968
969
|
|
|
969
970
|
type Turn = {
|
|
@@ -990,6 +991,7 @@ export default function ChatWeb3() {
|
|
|
990
991
|
const [busyStage, setBusyStage] = useState("");
|
|
991
992
|
const [err, setErr] = useState<string | null>(null);
|
|
992
993
|
const [feeLcai, setFeeLcai] = useState<number | null>(null);
|
|
994
|
+
const endRef = useRef<HTMLDivElement>(null);
|
|
993
995
|
|
|
994
996
|
// Read the on-chain fee for the connected network so we can show the
|
|
995
997
|
// visitor the real cost per turn before they click Send.
|
|
@@ -1003,6 +1005,11 @@ export default function ChatWeb3() {
|
|
|
1003
1005
|
return () => { cancelled = true; };
|
|
1004
1006
|
}, [network]);
|
|
1005
1007
|
|
|
1008
|
+
// Keep the latest turn (and the "writing on chain" indicator) in view.
|
|
1009
|
+
useEffect(() => {
|
|
1010
|
+
endRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
1011
|
+
}, [turns, busy]);
|
|
1012
|
+
|
|
1006
1013
|
/** Build a single prompt from history + new user input. */
|
|
1007
1014
|
function composePrompt(history: Turn[], next: string, system: string): string {
|
|
1008
1015
|
const lines: string[] = [];
|
|
@@ -1074,10 +1081,10 @@ export default function ChatWeb3() {
|
|
|
1074
1081
|
}
|
|
1075
1082
|
|
|
1076
1083
|
return (
|
|
1077
|
-
<main className="mx-auto flex min-h-screen w-full max-w-
|
|
1078
|
-
<header className="flex items-center justify-between gap-3
|
|
1084
|
+
<main className="mx-auto flex min-h-screen w-full max-w-3xl flex-col px-4 py-6">
|
|
1085
|
+
<header className="flex items-center justify-between gap-3 pb-6">
|
|
1079
1086
|
<div className="min-w-0">
|
|
1080
|
-
<h1 className="
|
|
1087
|
+
<h1 className="font-semibold text-foreground">Chat</h1>
|
|
1081
1088
|
<p className="truncate text-xs text-muted-foreground">
|
|
1082
1089
|
{network ? (
|
|
1083
1090
|
<>Signed from your wallet on {network} · {feeLcai != null ? feeLcai + " LCAI" : "..."}/turn + gas</>
|
|
@@ -1089,10 +1096,12 @@ export default function ChatWeb3() {
|
|
|
1089
1096
|
<ConnectButton />
|
|
1090
1097
|
</header>
|
|
1091
1098
|
|
|
1092
|
-
<div className="flex flex-1 flex-col gap-
|
|
1093
|
-
{turns.length === 0 ? (
|
|
1099
|
+
<div className="flex flex-1 flex-col gap-6 overflow-y-auto pb-6">
|
|
1100
|
+
{turns.length === 0 && !busy ? (
|
|
1094
1101
|
<div className="flex flex-1 flex-col items-center justify-center gap-3 text-center">
|
|
1095
|
-
<h2 className="text-
|
|
1102
|
+
<h2 className="text-3xl font-medium tracking-tight text-foreground">
|
|
1103
|
+
Start talking with <span className="text-gradient">AI Chat</span>
|
|
1104
|
+
</h2>
|
|
1096
1105
|
<p className="max-w-sm text-sm text-muted-foreground">
|
|
1097
1106
|
Connect your wallet and send a message. Each turn is signed and paid from your
|
|
1098
1107
|
own wallet, no backend required.
|
|
@@ -1104,61 +1113,88 @@ export default function ChatWeb3() {
|
|
|
1104
1113
|
)}
|
|
1105
1114
|
</div>
|
|
1106
1115
|
) : (
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
: "self-start rounded-bl-md border border-border bg-card text-foreground")
|
|
1115
|
-
}
|
|
1116
|
-
>
|
|
1117
|
-
<div>{t.text}</div>
|
|
1118
|
-
{t.role === "assistant" && t.submitTx ? (
|
|
1119
|
-
<div className="mt-2 flex flex-wrap gap-3 text-[11px] text-muted-foreground">
|
|
1120
|
-
{t.worker && (
|
|
1121
|
-
<a className="hover:text-foreground hover:underline" href={\`https://\${network}.lightscan.app/address/\${t.worker}\`} target="_blank" rel="noopener noreferrer">
|
|
1122
|
-
worker
|
|
1123
|
-
</a>
|
|
1124
|
-
)}
|
|
1125
|
-
{t.jobId && <span>job #{t.jobId}</span>}
|
|
1126
|
-
{t.submitTx && (
|
|
1127
|
-
<a className="hover:text-foreground hover:underline" href={\`https://\${network}.lightscan.app/tx/\${t.submitTx}\`} target="_blank" rel="noopener noreferrer">
|
|
1128
|
-
submitJob
|
|
1129
|
-
</a>
|
|
1130
|
-
)}
|
|
1131
|
-
{t.jobCompletedTx && (
|
|
1132
|
-
<a className="hover:text-foreground hover:underline" href={\`https://\${network}.lightscan.app/tx/\${t.jobCompletedTx}\`} target="_blank" rel="noopener noreferrer">
|
|
1133
|
-
completed
|
|
1134
|
-
</a>
|
|
1135
|
-
)}
|
|
1116
|
+
<>
|
|
1117
|
+
{turns.map((t, i) =>
|
|
1118
|
+
t.role === "user" ? (
|
|
1119
|
+
<div key={i} className="flex justify-end">
|
|
1120
|
+
<div className="w-fit max-w-[85%] whitespace-pre-wrap break-words rounded-2xl bg-surface-base-faint px-4 py-2.5 text-sm text-foreground">
|
|
1121
|
+
{t.text}
|
|
1122
|
+
</div>
|
|
1136
1123
|
</div>
|
|
1137
|
-
) :
|
|
1138
|
-
|
|
1139
|
-
|
|
1124
|
+
) : (
|
|
1125
|
+
<div key={i} className="group flex flex-col gap-2">
|
|
1126
|
+
<div className="prose-sm max-w-none text-sm leading-relaxed text-foreground [&_*:first-child]:mt-0 [&_*:last-child]:mb-0">
|
|
1127
|
+
<Streamdown>{t.text}</Streamdown>
|
|
1128
|
+
</div>
|
|
1129
|
+
<div className="flex flex-wrap items-center gap-3 text-[11px] text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100">
|
|
1130
|
+
<button
|
|
1131
|
+
type="button"
|
|
1132
|
+
onClick={() => navigator.clipboard?.writeText(t.text)}
|
|
1133
|
+
className="inline-flex items-center gap-1 hover:text-foreground"
|
|
1134
|
+
aria-label="Copy"
|
|
1135
|
+
>
|
|
1136
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
1137
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
1138
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
1139
|
+
</svg>
|
|
1140
|
+
Copy
|
|
1141
|
+
</button>
|
|
1142
|
+
{t.worker && (
|
|
1143
|
+
<a className="hover:text-foreground hover:underline" href={\`https://\${network}.lightscan.app/address/\${t.worker}\`} target="_blank" rel="noopener noreferrer">worker</a>
|
|
1144
|
+
)}
|
|
1145
|
+
{t.jobId && <span>job #{t.jobId}</span>}
|
|
1146
|
+
{t.submitTx && (
|
|
1147
|
+
<a className="hover:text-foreground hover:underline" href={\`https://\${network}.lightscan.app/tx/\${t.submitTx}\`} target="_blank" rel="noopener noreferrer">submitJob</a>
|
|
1148
|
+
)}
|
|
1149
|
+
{t.jobCompletedTx && (
|
|
1150
|
+
<a className="hover:text-foreground hover:underline" href={\`https://\${network}.lightscan.app/tx/\${t.jobCompletedTx}\`} target="_blank" rel="noopener noreferrer">completed</a>
|
|
1151
|
+
)}
|
|
1152
|
+
</div>
|
|
1153
|
+
</div>
|
|
1154
|
+
)
|
|
1155
|
+
)}
|
|
1156
|
+
{busy && (
|
|
1157
|
+
<div className="animate-pulse-dot text-sm text-muted-foreground">
|
|
1158
|
+
{busyStage || "Writing on chain..."}
|
|
1159
|
+
</div>
|
|
1160
|
+
)}
|
|
1161
|
+
</>
|
|
1140
1162
|
)}
|
|
1163
|
+
<div ref={endRef} />
|
|
1141
1164
|
</div>
|
|
1142
1165
|
|
|
1143
|
-
<div className="
|
|
1144
|
-
<
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1166
|
+
<div className="rounded-2xl border border-border bg-card p-3">
|
|
1167
|
+
<textarea
|
|
1168
|
+
value={input}
|
|
1169
|
+
onChange={(e) => setInput(e.target.value)}
|
|
1170
|
+
onKeyDown={(e) => {
|
|
1171
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); if (!busy && input.trim()) send(); }
|
|
1172
|
+
}}
|
|
1173
|
+
rows={1}
|
|
1174
|
+
placeholder="Send a message..."
|
|
1175
|
+
className="max-h-40 min-h-[44px] w-full resize-none bg-transparent px-2 py-2 text-sm text-foreground outline-none placeholder:text-muted-foreground"
|
|
1176
|
+
/>
|
|
1177
|
+
<div className="mt-1 flex items-center justify-between gap-2">
|
|
1178
|
+
<span className="inline-flex items-center gap-1.5 rounded-lg px-2 py-1 text-xs font-medium text-muted-foreground">
|
|
1179
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
1180
|
+
<rect x="4" y="4" width="16" height="16" rx="2" />
|
|
1181
|
+
<rect x="9" y="9" width="6" height="6" />
|
|
1182
|
+
<path d="M9 2v2M15 2v2M9 20v2M15 20v2M2 9h2M2 15h2M20 9h2M20 15h2" />
|
|
1183
|
+
</svg>
|
|
1184
|
+
{MODEL}
|
|
1185
|
+
</span>
|
|
1155
1186
|
<button
|
|
1156
1187
|
type="button"
|
|
1157
1188
|
onClick={() => send()}
|
|
1158
1189
|
disabled={busy || !input.trim() || !address || !network}
|
|
1159
|
-
className="shrink-0 rounded-
|
|
1190
|
+
className="flex size-9 shrink-0 items-center justify-center rounded-full bg-gradient-primary text-white transition hover:brightness-110 disabled:cursor-not-allowed disabled:bg-none disabled:bg-muted disabled:text-muted-foreground"
|
|
1191
|
+
aria-label={busy ? "Working" : "Send"}
|
|
1160
1192
|
>
|
|
1161
|
-
{busy ? (
|
|
1193
|
+
{busy ? (
|
|
1194
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="6" width="12" height="12" rx="2" /></svg>
|
|
1195
|
+
) : (
|
|
1196
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M12 19V5M5 12l7-7 7 7" /></svg>
|
|
1197
|
+
)}
|
|
1162
1198
|
</button>
|
|
1163
1199
|
</div>
|
|
1164
1200
|
{err && (
|
|
@@ -1274,59 +1310,67 @@ export default function InferenceWeb3() {
|
|
|
1274
1310
|
}
|
|
1275
1311
|
|
|
1276
1312
|
return (
|
|
1277
|
-
<main
|
|
1278
|
-
<
|
|
1279
|
-
<
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
<div style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12, margin: "12px 0", display: "flex", alignItems: "center", gap: 12 }}>
|
|
1289
|
-
<span>Connect a wallet to run inference.</span>
|
|
1290
|
-
<ConnectButton />
|
|
1313
|
+
<main className="mx-auto flex min-h-screen w-full max-w-2xl flex-col px-4 py-6">
|
|
1314
|
+
<header className="flex items-center justify-between gap-3 border-b border-border pb-4">
|
|
1315
|
+
<div className="min-w-0">
|
|
1316
|
+
<h1 className="text-base font-semibold text-foreground">Inference</h1>
|
|
1317
|
+
<p className="truncate text-xs text-muted-foreground">
|
|
1318
|
+
{network ? (
|
|
1319
|
+
<>Signed from your wallet on {network} · {feeLcai != null ? feeLcai + " LCAI" : "..."}/call + gas</>
|
|
1320
|
+
) : (
|
|
1321
|
+
"Connect a wallet on LightChain to start"
|
|
1322
|
+
)}
|
|
1323
|
+
</p>
|
|
1291
1324
|
</div>
|
|
1292
|
-
|
|
1325
|
+
<ConnectButton />
|
|
1326
|
+
</header>
|
|
1293
1327
|
|
|
1294
|
-
<
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
<textarea value={prompt} onChange={(e) => setPrompt(e.target.value)} rows={5}
|
|
1302
|
-
style={{ width: "100%", padding: 8, fontFamily: "monospace", fontSize: 12 }} />
|
|
1303
|
-
</label>
|
|
1304
|
-
<button type="button" onClick={() => run()} disabled={busy || !prompt.trim() || !address || !network}
|
|
1305
|
-
style={{ padding: "8px 16px", borderRadius: 8, cursor: busy ? "wait" : "pointer" }}>
|
|
1306
|
-
{busy ? (busyStage || "Running...") : "Run inference"}
|
|
1307
|
-
</button>
|
|
1328
|
+
<div className="flex flex-col gap-4 py-6">
|
|
1329
|
+
{!address && (
|
|
1330
|
+
<div className="flex items-center justify-between gap-3 rounded-xl border border-border bg-card px-4 py-3 text-sm text-muted-foreground">
|
|
1331
|
+
<span>Connect a wallet to run inference.</span>
|
|
1332
|
+
<ConnectButton />
|
|
1333
|
+
</div>
|
|
1334
|
+
)}
|
|
1308
1335
|
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
{
|
|
1312
|
-
|
|
1313
|
-
|
|
1336
|
+
<label className="flex flex-col gap-1.5">
|
|
1337
|
+
<span className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">System prompt</span>
|
|
1338
|
+
<textarea value={system} onChange={(e) => setSystem(e.target.value)} rows={2}
|
|
1339
|
+
className="resize-none rounded-xl border border-border bg-card px-3 py-2 font-mono text-xs text-foreground outline-none focus:ring-2 focus:ring-primary" />
|
|
1340
|
+
</label>
|
|
1341
|
+
<label className="flex flex-col gap-1.5">
|
|
1342
|
+
<span className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">Prompt</span>
|
|
1343
|
+
<textarea value={prompt} onChange={(e) => setPrompt(e.target.value)} rows={5}
|
|
1344
|
+
className="resize-none rounded-xl border border-border bg-card px-3 py-2 font-mono text-xs text-foreground outline-none focus:ring-2 focus:ring-primary" />
|
|
1345
|
+
</label>
|
|
1346
|
+
|
|
1347
|
+
<button type="button" onClick={() => run()} disabled={busy || !prompt.trim() || !address || !network}
|
|
1348
|
+
className="self-start rounded-xl bg-gradient-primary px-4 py-2 text-sm font-medium text-white transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40">
|
|
1349
|
+
{busy ? (busyStage || "Running...") : "Run inference"}
|
|
1350
|
+
</button>
|
|
1314
1351
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
<
|
|
1324
|
-
{result.
|
|
1325
|
-
|
|
1326
|
-
|
|
1352
|
+
{err && (
|
|
1353
|
+
<p className="rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
|
1354
|
+
{err}
|
|
1355
|
+
</p>
|
|
1356
|
+
)}
|
|
1357
|
+
|
|
1358
|
+
{result && (
|
|
1359
|
+
<div className="rounded-2xl border border-border bg-card p-4">
|
|
1360
|
+
<div className="mb-2 text-[11px] font-medium uppercase tracking-wide text-muted-foreground">Answer</div>
|
|
1361
|
+
<pre className="m-0 whitespace-pre-wrap break-words font-sans text-sm text-foreground">{result.answer}</pre>
|
|
1362
|
+
<div className="mt-3 flex flex-wrap gap-3 text-xs text-muted-foreground">
|
|
1363
|
+
<span>elapsed {Math.round(result.elapsedMs / 1000)}s</span>
|
|
1364
|
+
<span>job #{result.jobId}</span>
|
|
1365
|
+
<a className="hover:text-foreground hover:underline" href={\`https://\${network}.lightscan.app/address/\${result.worker}\`} target="_blank" rel="noopener noreferrer">worker</a>
|
|
1366
|
+
<a className="hover:text-foreground hover:underline" href={\`https://\${network}.lightscan.app/tx/\${result.submitJob}\`} target="_blank" rel="noopener noreferrer">submitJob</a>
|
|
1367
|
+
{result.jobCompleted && (
|
|
1368
|
+
<a className="hover:text-foreground hover:underline" href={\`https://\${network}.lightscan.app/tx/\${result.jobCompleted}\`} target="_blank" rel="noopener noreferrer">jobCompleted</a>
|
|
1369
|
+
)}
|
|
1370
|
+
</div>
|
|
1327
1371
|
</div>
|
|
1328
|
-
|
|
1329
|
-
|
|
1372
|
+
)}
|
|
1373
|
+
</div>
|
|
1330
1374
|
</main>
|
|
1331
1375
|
);
|
|
1332
1376
|
}
|
|
@@ -1459,72 +1503,80 @@ Reply with STRICT JSON only, matching: { "passed": boolean, "confidence": 0-1, "
|
|
|
1459
1503
|
}
|
|
1460
1504
|
|
|
1461
1505
|
return (
|
|
1462
|
-
<main
|
|
1463
|
-
<
|
|
1464
|
-
<
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
<div style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12, margin: "12px 0", display: "flex", alignItems: "center", gap: 12 }}>
|
|
1474
|
-
<span>Connect a wallet to submit.</span>
|
|
1475
|
-
<ConnectButton />
|
|
1506
|
+
<main className="mx-auto flex min-h-screen w-full max-w-2xl flex-col px-4 py-6">
|
|
1507
|
+
<header className="flex items-center justify-between gap-3 border-b border-border pb-4">
|
|
1508
|
+
<div className="min-w-0">
|
|
1509
|
+
<h1 className="text-base font-semibold text-foreground">AI Judge</h1>
|
|
1510
|
+
<p className="truncate text-xs text-muted-foreground">
|
|
1511
|
+
{network ? (
|
|
1512
|
+
<>Signed from your wallet on {network} · {feeLcai != null ? feeLcai + " LCAI" : "..."} + gas · verdict has on-chain proof</>
|
|
1513
|
+
) : (
|
|
1514
|
+
"Connect a wallet on LightChain to start"
|
|
1515
|
+
)}
|
|
1516
|
+
</p>
|
|
1476
1517
|
</div>
|
|
1477
|
-
|
|
1518
|
+
<ConnectButton />
|
|
1519
|
+
</header>
|
|
1478
1520
|
|
|
1479
|
-
<
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
<textarea value={evidence} onChange={(e) => setEvidence(e.target.value)} rows={5}
|
|
1487
|
-
style={{ width: "100%", padding: 8, fontFamily: "monospace", fontSize: 12 }} />
|
|
1488
|
-
</label>
|
|
1489
|
-
<button type="button" onClick={() => run()} disabled={busy || !criteria.trim() || !evidence.trim() || !address || !network}
|
|
1490
|
-
style={{ padding: "8px 16px", borderRadius: 8, cursor: busy ? "wait" : "pointer" }}>
|
|
1491
|
-
{busy ? (busyStage || "Judging...") : "Get AI verdict"}
|
|
1492
|
-
</button>
|
|
1521
|
+
<div className="flex flex-col gap-4 py-6">
|
|
1522
|
+
{!address && (
|
|
1523
|
+
<div className="flex items-center justify-between gap-3 rounded-xl border border-border bg-card px-4 py-3 text-sm text-muted-foreground">
|
|
1524
|
+
<span>Connect a wallet to submit.</span>
|
|
1525
|
+
<ConnectButton />
|
|
1526
|
+
</div>
|
|
1527
|
+
)}
|
|
1493
1528
|
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
{
|
|
1497
|
-
|
|
1498
|
-
|
|
1529
|
+
<label className="flex flex-col gap-1.5">
|
|
1530
|
+
<span className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">Criteria</span>
|
|
1531
|
+
<textarea value={criteria} onChange={(e) => setCriteria(e.target.value)} rows={2}
|
|
1532
|
+
className="resize-none rounded-xl border border-border bg-card px-3 py-2 font-mono text-xs text-foreground outline-none focus:ring-2 focus:ring-primary" />
|
|
1533
|
+
</label>
|
|
1534
|
+
<label className="flex flex-col gap-1.5">
|
|
1535
|
+
<span className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">Evidence (JSON)</span>
|
|
1536
|
+
<textarea value={evidence} onChange={(e) => setEvidence(e.target.value)} rows={5}
|
|
1537
|
+
className="resize-none rounded-xl border border-border bg-card px-3 py-2 font-mono text-xs text-foreground outline-none focus:ring-2 focus:ring-primary" />
|
|
1538
|
+
</label>
|
|
1539
|
+
|
|
1540
|
+
<button type="button" onClick={() => run()} disabled={busy || !criteria.trim() || !evidence.trim() || !address || !network}
|
|
1541
|
+
className="self-start rounded-xl bg-gradient-primary px-4 py-2 text-sm font-medium text-white transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40">
|
|
1542
|
+
{busy ? (busyStage || "Judging...") : "Get AI verdict"}
|
|
1543
|
+
</button>
|
|
1499
1544
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1545
|
+
{err && (
|
|
1546
|
+
<p className="rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
|
1547
|
+
{err}
|
|
1548
|
+
</p>
|
|
1549
|
+
)}
|
|
1550
|
+
|
|
1551
|
+
{result && (
|
|
1552
|
+
<div className="rounded-2xl border border-border bg-card p-4">
|
|
1553
|
+
<div className="mb-2 text-[11px] font-medium uppercase tracking-wide text-muted-foreground">Verdict</div>
|
|
1554
|
+
{result.verdict ? (
|
|
1555
|
+
<div>
|
|
1556
|
+
<div className={"flex items-baseline gap-3 text-2xl font-semibold " + (result.verdict.passed ? "text-success" : "text-destructive")}>
|
|
1557
|
+
{result.verdict.passed ? "PASSED" : "FAILED"}
|
|
1558
|
+
<span className="text-sm font-normal text-muted-foreground">
|
|
1559
|
+
confidence {Math.round(result.verdict.confidence * 100)}%
|
|
1560
|
+
</span>
|
|
1561
|
+
</div>
|
|
1562
|
+
<p className="mt-2 text-sm text-foreground">{result.verdict.reason}</p>
|
|
1510
1563
|
</div>
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
{result.raw}
|
|
1516
|
-
</pre>
|
|
1517
|
-
)}
|
|
1518
|
-
<div style={{ marginTop: 12, fontSize: 12, color: "#666", display: "flex", gap: 12, flexWrap: "wrap" }}>
|
|
1519
|
-
<span>job #{result.jobId}</span>
|
|
1520
|
-
<a href={\`https://\${network}.lightscan.app/address/\${result.worker}\`} target="_blank" rel="noopener noreferrer">worker</a>
|
|
1521
|
-
<a href={\`https://\${network}.lightscan.app/tx/\${result.submitJob}\`} target="_blank" rel="noopener noreferrer">submitJob</a>
|
|
1522
|
-
{result.jobCompleted && (
|
|
1523
|
-
<a href={\`https://\${network}.lightscan.app/tx/\${result.jobCompleted}\`} target="_blank" rel="noopener noreferrer">jobCompleted</a>
|
|
1564
|
+
) : (
|
|
1565
|
+
<pre className="m-0 whitespace-pre-wrap break-words font-mono text-xs text-muted-foreground">
|
|
1566
|
+
{result.raw}
|
|
1567
|
+
</pre>
|
|
1524
1568
|
)}
|
|
1569
|
+
<div className="mt-3 flex flex-wrap gap-3 text-xs text-muted-foreground">
|
|
1570
|
+
<span>job #{result.jobId}</span>
|
|
1571
|
+
<a className="hover:text-foreground hover:underline" href={\`https://\${network}.lightscan.app/address/\${result.worker}\`} target="_blank" rel="noopener noreferrer">worker</a>
|
|
1572
|
+
<a className="hover:text-foreground hover:underline" href={\`https://\${network}.lightscan.app/tx/\${result.submitJob}\`} target="_blank" rel="noopener noreferrer">submitJob</a>
|
|
1573
|
+
{result.jobCompleted && (
|
|
1574
|
+
<a className="hover:text-foreground hover:underline" href={\`https://\${network}.lightscan.app/tx/\${result.jobCompleted}\`} target="_blank" rel="noopener noreferrer">jobCompleted</a>
|
|
1575
|
+
)}
|
|
1576
|
+
</div>
|
|
1525
1577
|
</div>
|
|
1526
|
-
|
|
1527
|
-
|
|
1578
|
+
)}
|
|
1579
|
+
</div>
|
|
1528
1580
|
</main>
|
|
1529
1581
|
);
|
|
1530
1582
|
}
|
|
@@ -1809,7 +1861,8 @@ export function addChatWeb3(opts = {}) {
|
|
|
1809
1861
|
written.push(writeFile(path.join(cwd, "app/chat-web3/page.tsx"), NEXTJS_CHAT_WEB3_PAGE, force));
|
|
1810
1862
|
return {
|
|
1811
1863
|
written,
|
|
1812
|
-
|
|
1864
|
+
// streamdown renders the assistant answers as markdown (bold, lists, code).
|
|
1865
|
+
install: `npm install lightnode-sdk viem streamdown` + (hasWagmi ? "" : " wagmi @tanstack/react-query"),
|
|
1813
1866
|
template,
|
|
1814
1867
|
network,
|
|
1815
1868
|
needsWagmi: !hasWagmi,
|
|
@@ -1966,9 +2019,13 @@ export function ConnectButton() {
|
|
|
1966
2019
|
type="button"
|
|
1967
2020
|
onClick={() => connect({ connector })}
|
|
1968
2021
|
disabled={isPending}
|
|
1969
|
-
|
|
2022
|
+
className="bg-gradient-btn inline-flex items-center gap-2 rounded-[10px] px-5 py-2.5 text-sm font-medium tracking-wide text-white transition hover:brightness-110 disabled:opacity-60"
|
|
1970
2023
|
>
|
|
1971
|
-
|
|
2024
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
2025
|
+
<path d="M19 7V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2" />
|
|
2026
|
+
<path d="M21 12h-6a2 2 0 0 0 0 4h6v-4Z" />
|
|
2027
|
+
</svg>
|
|
2028
|
+
{isPending ? "Connecting..." : "Connect Wallet"}
|
|
1972
2029
|
</button>
|
|
1973
2030
|
);
|
|
1974
2031
|
}
|
|
@@ -1979,7 +2036,7 @@ export function ConnectButton() {
|
|
|
1979
2036
|
type="button"
|
|
1980
2037
|
onClick={() => switchChain({ chainId: 9200 })}
|
|
1981
2038
|
disabled={switching}
|
|
1982
|
-
|
|
2039
|
+
className="rounded-[10px] border border-destructive/40 bg-destructive/10 px-4 py-2 text-sm font-medium text-destructive transition hover:bg-destructive/20 disabled:opacity-60"
|
|
1983
2040
|
>
|
|
1984
2041
|
{switching ? "Switching..." : "Switch to LightChain"}
|
|
1985
2042
|
</button>
|
|
@@ -1990,9 +2047,12 @@ export function ConnectButton() {
|
|
|
1990
2047
|
<button
|
|
1991
2048
|
type="button"
|
|
1992
2049
|
onClick={() => disconnect()}
|
|
1993
|
-
|
|
2050
|
+
className="group inline-flex items-center gap-2 rounded-[10px] border border-border bg-card px-4 py-2 font-mono text-sm text-foreground transition hover:border-primary"
|
|
2051
|
+
title={\`\${chain?.name} - click to disconnect\`}
|
|
1994
2052
|
>
|
|
1995
|
-
|
|
2053
|
+
<span className="size-2 rounded-full bg-success" />
|
|
2054
|
+
{address ? shortAddress(address) : "(unknown)"}
|
|
2055
|
+
<span className="text-muted-foreground group-hover:text-foreground">disconnect</span>
|
|
1996
2056
|
</button>
|
|
1997
2057
|
);
|
|
1998
2058
|
}
|
|
@@ -2090,6 +2150,10 @@ export function patchLayoutWithProviders(cwd = process.cwd()) {
|
|
|
2090
2150
|
// installs default to the real look instead of the create-next-app starter.
|
|
2091
2151
|
export const SCAFFOLD_GLOBALS_CSS = `@import "tailwindcss";
|
|
2092
2152
|
|
|
2153
|
+
/* let Tailwind v4 see streamdown's classes so markdown answers are styled
|
|
2154
|
+
(harmless when streamdown isn't installed - the path just matches nothing) */
|
|
2155
|
+
@source "../node_modules/streamdown/dist/index.js";
|
|
2156
|
+
|
|
2093
2157
|
/* dark mode via .dark class (we default the app to dark) */
|
|
2094
2158
|
@custom-variant dark (&:is(.dark, .dark *));
|
|
2095
2159
|
|
|
@@ -2288,6 +2352,18 @@ body::before {
|
|
|
2288
2352
|
.bg-gradient-primary {
|
|
2289
2353
|
background-image: var(--color-gradient-primary);
|
|
2290
2354
|
}
|
|
2355
|
+
/* the lcai-chat connect-button gradient (pink -> purple) */
|
|
2356
|
+
.bg-gradient-btn {
|
|
2357
|
+
background-image: linear-gradient(94deg, #dd00ac 10.66%, #7130c3 53.03%, #410093 96.34%);
|
|
2358
|
+
background-size: 200% auto;
|
|
2359
|
+
}
|
|
2360
|
+
@keyframes pulse-dot {
|
|
2361
|
+
0%, 100% { opacity: 1; }
|
|
2362
|
+
50% { opacity: 0.4; }
|
|
2363
|
+
}
|
|
2364
|
+
.animate-pulse-dot {
|
|
2365
|
+
animation: pulse-dot 1.6s ease-in-out infinite;
|
|
2366
|
+
}
|
|
2291
2367
|
.text-gradient {
|
|
2292
2368
|
background: linear-gradient(94deg, #dd00ac 10%, #7130c3 53%, #7064e9 96%);
|
|
2293
2369
|
-webkit-background-clip: text;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lightnode-sdk",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
4
4
|
"description": "Read-only TypeScript client for LightChain AI: workers, jobs, models, on-chain registration, and per-model network analytics. Independent, community-built (not an official LightChain package).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|