@hanzo/browser-extension 1.7.6 → 1.9.33
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 +9 -18
- package/dist/browser-extension/content-script.js +1642 -123
- package/images/icon128.png +0 -0
- package/images/icon16.png +0 -0
- package/images/icon48.png +0 -0
- package/manifest.json +7 -2
- package/package.json +28 -10
- package/src/answer/AnswerEngine.tsx +553 -0
- package/src/chat-widget.tsx +96 -0
- package/src/gui-primitives.tsx +116 -0
- package/src/icon128.png +0 -0
- package/src/icon16.png +0 -0
- package/src/icon32.png +0 -0
- package/src/icon48.png +0 -0
- package/src/manifest-firefox.json +32 -13
- package/src/manifest.json +35 -11
- package/src/newtab.css +277 -0
- package/src/newtab.html +13 -0
- package/src/package.json +1 -1
- package/src/popup.css +352 -110
- package/src/popup.html +186 -47
- package/src/popup.js +196 -0
- package/src/sidebar.css +772 -156
- package/src/sidebar.html +268 -85
- package/src/sidebar.js +885 -0
- package/.github/workflows/docs.yml +0 -44
- package/.github/workflows/release.yml +0 -55
- package/README.md +0 -126
- package/dist/browser-extension/README.md +0 -43
- package/dist/browser-extension/ai-worker.js +0 -30
- package/dist/browser-extension/background-firefox.js +0 -875
- package/dist/browser-extension/background.js +0 -2732
- package/dist/browser-extension/browser-control.js +0 -683
- package/dist/browser-extension/cdp-bridge-server.js +0 -444
- package/dist/browser-extension/chrome/ai-worker.js +0 -571
- package/dist/browser-extension/chrome/background.js +0 -2732
- package/dist/browser-extension/chrome/callback.html +0 -17
- package/dist/browser-extension/chrome/content-script.js +0 -1270
- package/dist/browser-extension/chrome/icon128.png +0 -0
- package/dist/browser-extension/chrome/icon16.png +0 -0
- package/dist/browser-extension/chrome/icon32.png +0 -0
- package/dist/browser-extension/chrome/icon48.png +0 -0
- package/dist/browser-extension/chrome/manifest.json +0 -57
- package/dist/browser-extension/chrome/popup.css +0 -519
- package/dist/browser-extension/chrome/popup.html +0 -242
- package/dist/browser-extension/chrome/popup.js +0 -278
- package/dist/browser-extension/chrome/sidebar.css +0 -1176
- package/dist/browser-extension/chrome/sidebar.html +0 -280
- package/dist/browser-extension/chrome/sidebar.js +0 -779
- package/dist/browser-extension/cli.js +0 -233
- package/dist/browser-extension/firefox/ai-worker.js +0 -571
- package/dist/browser-extension/firefox/background.js +0 -875
- package/dist/browser-extension/firefox/callback.html +0 -17
- package/dist/browser-extension/firefox/content-script.js +0 -1270
- package/dist/browser-extension/firefox/icon128.png +0 -0
- package/dist/browser-extension/firefox/icon16.png +0 -0
- package/dist/browser-extension/firefox/icon32.png +0 -0
- package/dist/browser-extension/firefox/icon48.png +0 -0
- package/dist/browser-extension/firefox/manifest.json +0 -66
- package/dist/browser-extension/firefox/popup.css +0 -519
- package/dist/browser-extension/firefox/popup.html +0 -242
- package/dist/browser-extension/firefox/popup.js +0 -278
- package/dist/browser-extension/firefox/sidebar.css +0 -1176
- package/dist/browser-extension/firefox/sidebar.html +0 -280
- package/dist/browser-extension/firefox/sidebar.js +0 -779
- package/dist/browser-extension/hanzo-browser-extension-firefox-1.1.0.zip +0 -0
- package/dist/browser-extension/icon128.png +0 -0
- package/dist/browser-extension/icon16.png +0 -0
- package/dist/browser-extension/icon32.png +0 -0
- package/dist/browser-extension/icon48.png +0 -0
- package/dist/browser-extension/manifest.json +0 -57
- package/dist/browser-extension/package.json +0 -41
- package/dist/browser-extension/popup.css +0 -468
- package/dist/browser-extension/popup.html +0 -191
- package/dist/browser-extension/popup.js +0 -278
- package/dist/browser-extension/safari/Info.plist +0 -21
- package/dist/browser-extension/safari/ai-worker.js +0 -571
- package/dist/browser-extension/safari/background.js +0 -2732
- package/dist/browser-extension/safari/callback.html +0 -17
- package/dist/browser-extension/safari/content-script.js +0 -1270
- package/dist/browser-extension/safari/icon128.png +0 -0
- package/dist/browser-extension/safari/icon16.png +0 -0
- package/dist/browser-extension/safari/icon32.png +0 -0
- package/dist/browser-extension/safari/icon48.png +0 -0
- package/dist/browser-extension/safari/popup.css +0 -519
- package/dist/browser-extension/safari/popup.html +0 -242
- package/dist/browser-extension/safari/popup.js +0 -278
- package/dist/browser-extension/safari/sidebar.css +0 -1176
- package/dist/browser-extension/safari/sidebar.html +0 -280
- package/dist/browser-extension/safari/sidebar.js +0 -779
- package/dist/browser-extension/sidebar.css +0 -718
- package/dist/browser-extension/sidebar.html +0 -184
- package/dist/browser-extension/sidebar.js +0 -779
- package/dist/browser-extension/webgpu-ai.js +0 -495
- package/dist/cdp-bridge-server.js +0 -411
- package/dist/hanzo-ai-chrome-1.7.2.zip +0 -0
- package/dist/hanzo-ai-chrome-1.7.3.zip +0 -0
- package/dist/hanzo-ai-firefox-1.7.2.zip +0 -0
- package/dist/hanzo-ai-firefox-1.7.3.zip +0 -0
- package/dist/safari/Hanzo AI/Hanzo AI.xcodeproj/project.pbxproj +0 -1047
- package/dist/safari/Hanzo AI/Hanzo AI.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AccentColor.colorset/Contents.json +0 -11
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -98
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-128@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-128@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-16@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-16@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-256@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-256@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-32@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-32@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-512@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-512@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/universal-icon-1024@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/Contents.json +0 -6
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/LargeIcon.imageset/Contents.json +0 -21
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/LargeIcon.imageset/icon128.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Base.lproj/Main.html +0 -20
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Icon.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Script.js +0 -24
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Style.css +0 -61
- package/dist/safari/Hanzo AI/Shared (App)/ViewController.swift +0 -81
- package/dist/safari/Hanzo AI/Shared (Extension)/SafariWebExtensionHandler.swift +0 -42
- package/dist/safari/Hanzo AI/iOS (App)/AppDelegate.swift +0 -24
- package/dist/safari/Hanzo AI/iOS (App)/Base.lproj/LaunchScreen.storyboard +0 -36
- package/dist/safari/Hanzo AI/iOS (App)/Base.lproj/Main.storyboard +0 -38
- package/dist/safari/Hanzo AI/iOS (App)/Info.plist +0 -27
- package/dist/safari/Hanzo AI/iOS (App)/SceneDelegate.swift +0 -18
- package/dist/safari/Hanzo AI/iOS (Extension)/Info.plist +0 -13
- package/dist/safari/Hanzo AI/macOS (App)/AppDelegate.swift +0 -21
- package/dist/safari/Hanzo AI/macOS (App)/Base.lproj/Main.storyboard +0 -124
- package/dist/safari/Hanzo AI/macOS (App)/Info.plist +0 -8
- package/dist/safari/Hanzo AI/macOS (Extension)/Info.plist +0 -13
- package/docs/_config.yml +0 -13
- package/docs/_layouts/default.html +0 -435
- package/docs/index.md +0 -54
- package/hanzo-ai-chrome-v1.7.6.zip +0 -0
- package/hanzo-ai-firefox-v1.7.6.zip +0 -0
- package/install-extension.sh +0 -75
- package/test/claude-integration.test.ts +0 -251
- package/test/server-frameworks-edge-cases.test.ts +0 -145
- package/test/server-frameworks.test.ts +0 -395
- package/test-cdp-connection.js +0 -140
- package/tsconfig.json +0 -19
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { flushSync } from 'react-dom';
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
Card,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardDescription,
|
|
9
|
+
CardHeader,
|
|
10
|
+
CardTitle,
|
|
11
|
+
Textarea,
|
|
12
|
+
} from '@hanzo/gui';
|
|
13
|
+
|
|
14
|
+
function LoginPrompt() {
|
|
15
|
+
return (
|
|
16
|
+
<Card className="chat-modern-login-card">
|
|
17
|
+
<CardHeader>
|
|
18
|
+
<CardTitle>Chat with Zen AI models</CardTitle>
|
|
19
|
+
<CardDescription>Models enabled in Hanzo Cloud are loaded automatically.</CardDescription>
|
|
20
|
+
</CardHeader>
|
|
21
|
+
<CardContent>
|
|
22
|
+
<Button id="auth-btn" className="chat-modern-auth-btn" type="button">
|
|
23
|
+
Sign in
|
|
24
|
+
</Button>
|
|
25
|
+
<Button id="signup-btn" className="secondary-btn" type="button">
|
|
26
|
+
Create account
|
|
27
|
+
</Button>
|
|
28
|
+
<p className="auth-note">
|
|
29
|
+
Browser tools work without sign-in.{' '}
|
|
30
|
+
<a href="https://docs.hanzo.ai" target="_blank" rel="noreferrer">
|
|
31
|
+
Learn more
|
|
32
|
+
</a>
|
|
33
|
+
</p>
|
|
34
|
+
</CardContent>
|
|
35
|
+
</Card>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function ChatComposer() {
|
|
40
|
+
return (
|
|
41
|
+
<div className="chat-modern-composer">
|
|
42
|
+
<div className="model-selector">
|
|
43
|
+
<select id="model-select">
|
|
44
|
+
<option value="claude-sonnet-4-20250514">Claude Sonnet 4</option>
|
|
45
|
+
<option value="claude-opus-4-20250514">Claude Opus 4</option>
|
|
46
|
+
<option value="gpt-4o">GPT-4o</option>
|
|
47
|
+
<option value="zen-coder-flash">Zen Coder Flash</option>
|
|
48
|
+
<option value="zen-max">Zen Max</option>
|
|
49
|
+
</select>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div className="chat-flags">
|
|
53
|
+
<label className="flag-toggle">
|
|
54
|
+
<input type="checkbox" id="rag-enabled" defaultChecked />
|
|
55
|
+
<span>RAG</span>
|
|
56
|
+
</label>
|
|
57
|
+
<label className="flag-toggle">
|
|
58
|
+
<input type="checkbox" id="tab-context-enabled" defaultChecked />
|
|
59
|
+
<span>Tab Context</span>
|
|
60
|
+
</label>
|
|
61
|
+
<span id="rag-status" className="rag-status">
|
|
62
|
+
Ready
|
|
63
|
+
</span>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div className="input-row">
|
|
67
|
+
<Textarea id="chat-input" className="chat-modern-input" placeholder="Ask anything..." rows={1} />
|
|
68
|
+
<Button id="send-btn" className="send-btn chat-modern-send" title="Send" type="button">
|
|
69
|
+
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" aria-hidden="true">
|
|
70
|
+
<path d="M4 10l12-6-6 12-2-6-4-2z" strokeWidth="1.5" fill="currentColor" />
|
|
71
|
+
</svg>
|
|
72
|
+
</Button>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function mountModernChatWidget() {
|
|
79
|
+
const loginTarget = document.getElementById('chat-login-prompt');
|
|
80
|
+
if (loginTarget) {
|
|
81
|
+
loginTarget.innerHTML = '';
|
|
82
|
+
const loginRoot = createRoot(loginTarget);
|
|
83
|
+
flushSync(() => {
|
|
84
|
+
loginRoot.render(<LoginPrompt />);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const composerTarget = document.getElementById('chat-composer');
|
|
89
|
+
if (composerTarget) {
|
|
90
|
+
composerTarget.innerHTML = '';
|
|
91
|
+
const composerRoot = createRoot(composerTarget);
|
|
92
|
+
flushSync(() => {
|
|
93
|
+
composerRoot.render(<ChatComposer />);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local @hanzo/gui primitives shim for the extension.
|
|
3
|
+
*
|
|
4
|
+
* This is the canonical surface the extension's React components import via
|
|
5
|
+
* `@hanzo/gui`. It deliberately does NOT pull in the upstream tamagui-based
|
|
6
|
+
* `@hanzo/gui` package because:
|
|
7
|
+
*
|
|
8
|
+
* 1. content-script + background bundles target the smallest possible
|
|
9
|
+
* payload — RNW + tamagui's runtime is far too heavy for a script
|
|
10
|
+
* injected into every page;
|
|
11
|
+
* 2. Hanzo brand is strict monochrome (see ~/work/hanzo/brand/brand.json,
|
|
12
|
+
* primaryColor #FFFFFF), so no color tokens travel with primitives
|
|
13
|
+
* anyway — pure structural elements styled by sibling CSS;
|
|
14
|
+
* 3. The full tamagui adoption ships in a future feature branch with the
|
|
15
|
+
* compiler + RNW peer dep wired correctly.
|
|
16
|
+
*
|
|
17
|
+
* The build aliases `@hanzo/gui` → this file. When the tamagui adoption
|
|
18
|
+
* lands, swap the alias target to the published `@hanzo/gui` package.
|
|
19
|
+
*
|
|
20
|
+
* Each primitive renders a plain DOM element with a stable, monochrome
|
|
21
|
+
* class hook. All visuals come from sidebar.css / popup.css using the
|
|
22
|
+
* brand token scale (white luminance ladder, no chromatic accents).
|
|
23
|
+
*/
|
|
24
|
+
import React from 'react';
|
|
25
|
+
|
|
26
|
+
type Classy = { className?: string; children?: React.ReactNode };
|
|
27
|
+
|
|
28
|
+
function cx(base: string, extra?: string): string {
|
|
29
|
+
return extra ? `${base} ${extra}` : base;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function Button(props: React.ButtonHTMLAttributes<HTMLButtonElement>) {
|
|
33
|
+
const { className, children, ...rest } = props;
|
|
34
|
+
return (
|
|
35
|
+
<button {...rest} className={cx('hanzo-gui-btn', className)}>
|
|
36
|
+
{children}
|
|
37
|
+
</button>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function Textarea(props: React.TextareaHTMLAttributes<HTMLTextAreaElement>) {
|
|
42
|
+
const { className, children, ...rest } = props;
|
|
43
|
+
return (
|
|
44
|
+
<textarea {...rest} className={cx('hanzo-gui-textarea', className)}>
|
|
45
|
+
{children}
|
|
46
|
+
</textarea>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function Input(props: React.InputHTMLAttributes<HTMLInputElement>) {
|
|
51
|
+
const { className, ...rest } = props;
|
|
52
|
+
return <input {...rest} className={cx('hanzo-gui-input', className)} />;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function Card({ className, children }: Classy) {
|
|
56
|
+
return <div className={cx('hanzo-gui-card', className)}>{children}</div>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function CardHeader({ className, children }: Classy) {
|
|
60
|
+
return <div className={cx('hanzo-gui-card-header', className)}>{children}</div>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function CardTitle({ className, children }: Classy) {
|
|
64
|
+
return <h3 className={cx('hanzo-gui-card-title', className)}>{children}</h3>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function CardDescription({ className, children }: Classy) {
|
|
68
|
+
return <p className={cx('hanzo-gui-card-description', className)}>{children}</p>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function CardContent({ className, children }: Classy) {
|
|
72
|
+
return <div className={cx('hanzo-gui-card-content', className)}>{children}</div>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Layout primitives — mirror the names used by the upstream Tamagui-based
|
|
76
|
+
@hanzo/gui (XStack/YStack) so call-sites can be ported without churn
|
|
77
|
+
when the full adoption lands. Pure flexbox under the hood. */
|
|
78
|
+
type Stack = Classy & React.HTMLAttributes<HTMLDivElement> & {
|
|
79
|
+
gap?: number | string;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export function XStack({ className, gap, style, children, ...rest }: Stack) {
|
|
83
|
+
const merged: React.CSSProperties = {
|
|
84
|
+
display: 'flex',
|
|
85
|
+
flexDirection: 'row',
|
|
86
|
+
gap,
|
|
87
|
+
...style,
|
|
88
|
+
};
|
|
89
|
+
return (
|
|
90
|
+
<div {...rest} style={merged} className={cx('hanzo-gui-xstack', className)}>
|
|
91
|
+
{children}
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function YStack({ className, gap, style, children, ...rest }: Stack) {
|
|
97
|
+
const merged: React.CSSProperties = {
|
|
98
|
+
display: 'flex',
|
|
99
|
+
flexDirection: 'column',
|
|
100
|
+
gap,
|
|
101
|
+
...style,
|
|
102
|
+
};
|
|
103
|
+
return (
|
|
104
|
+
<div {...rest} style={merged} className={cx('hanzo-gui-ystack', className)}>
|
|
105
|
+
{children}
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function Text({ className, children, ...rest }: Classy & React.HTMLAttributes<HTMLSpanElement>) {
|
|
111
|
+
return (
|
|
112
|
+
<span {...rest} className={cx('hanzo-gui-text', className)}>
|
|
113
|
+
{children}
|
|
114
|
+
</span>
|
|
115
|
+
);
|
|
116
|
+
}
|
package/src/icon128.png
CHANGED
|
Binary file
|
package/src/icon16.png
CHANGED
|
Binary file
|
package/src/icon32.png
CHANGED
|
Binary file
|
package/src/icon48.png
CHANGED
|
Binary file
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Hanzo AI",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "AI
|
|
4
|
+
"version": "1.9.33",
|
|
5
|
+
"description": "AI answer engine, search, and dev assistant — Hanzo AI in a new tab, address-bar search, source-cited answers, realtime news, WebGPU AI, and MCP.",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"activeTab",
|
|
8
8
|
"identity",
|
|
9
9
|
"storage",
|
|
10
10
|
"tabs",
|
|
11
11
|
"webNavigation",
|
|
12
|
-
"scripting"
|
|
12
|
+
"scripting",
|
|
13
|
+
"cookies",
|
|
14
|
+
"nativeMessaging",
|
|
15
|
+
"alarms"
|
|
13
16
|
],
|
|
14
17
|
"host_permissions": [
|
|
15
18
|
"http://localhost/*",
|
|
16
19
|
"https://localhost/*",
|
|
20
|
+
"https://claude.ai/*",
|
|
21
|
+
"https://chatgpt.com/*",
|
|
17
22
|
"<all_urls>"
|
|
18
23
|
],
|
|
19
24
|
"content_security_policy": {
|
|
@@ -21,13 +26,19 @@
|
|
|
21
26
|
},
|
|
22
27
|
"content_scripts": [
|
|
23
28
|
{
|
|
24
|
-
"matches": [
|
|
25
|
-
|
|
29
|
+
"matches": [
|
|
30
|
+
"<all_urls>"
|
|
31
|
+
],
|
|
32
|
+
"js": [
|
|
33
|
+
"content-script.js"
|
|
34
|
+
],
|
|
26
35
|
"run_at": "document_idle"
|
|
27
36
|
}
|
|
28
37
|
],
|
|
29
38
|
"background": {
|
|
30
|
-
"scripts": [
|
|
39
|
+
"scripts": [
|
|
40
|
+
"background.js"
|
|
41
|
+
],
|
|
31
42
|
"type": "module"
|
|
32
43
|
},
|
|
33
44
|
"action": {
|
|
@@ -38,18 +49,30 @@
|
|
|
38
49
|
"128": "icon128.png"
|
|
39
50
|
}
|
|
40
51
|
},
|
|
52
|
+
"chrome_url_overrides": {
|
|
53
|
+
"newtab": "newtab.html"
|
|
54
|
+
},
|
|
55
|
+
"omnibox": {
|
|
56
|
+
"keyword": "hanzo"
|
|
57
|
+
},
|
|
41
58
|
"sidebar_action": {
|
|
42
59
|
"default_title": "Hanzo AI",
|
|
43
60
|
"default_panel": "sidebar.html",
|
|
44
61
|
"default_icon": {
|
|
45
62
|
"16": "icon16.png",
|
|
46
63
|
"48": "icon48.png"
|
|
47
|
-
}
|
|
64
|
+
},
|
|
65
|
+
"open_at_install": false
|
|
48
66
|
},
|
|
49
67
|
"web_accessible_resources": [
|
|
50
68
|
{
|
|
51
|
-
"resources": [
|
|
52
|
-
|
|
69
|
+
"resources": [
|
|
70
|
+
"ai-worker.js",
|
|
71
|
+
"models/*"
|
|
72
|
+
],
|
|
73
|
+
"matches": [
|
|
74
|
+
"<all_urls>"
|
|
75
|
+
]
|
|
53
76
|
}
|
|
54
77
|
],
|
|
55
78
|
"browser_specific_settings": {
|
|
@@ -58,9 +81,5 @@
|
|
|
58
81
|
"strict_min_version": "109.0"
|
|
59
82
|
},
|
|
60
83
|
"gecko_android": {}
|
|
61
|
-
},
|
|
62
|
-
"data_collection_permissions": {
|
|
63
|
-
"purposes": ["functionality"],
|
|
64
|
-
"data_categories": ["technical_data"]
|
|
65
84
|
}
|
|
66
85
|
}
|
package/src/manifest.json
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Hanzo AI",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "AI
|
|
4
|
+
"version": "1.9.33",
|
|
5
|
+
"description": "AI answer engine, search, and dev assistant — Hanzo AI in a new tab, address-bar search, source-cited answers, realtime news, WebGPU AI, and MCP.",
|
|
6
|
+
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxfXGh0lPUT5m04GKfjUwrLsV6pLaK3VcZuFRPogqAir2tzyLYnQPRtHynue9yvDyguIVnlkwvcwfDOYZrvH76zbw4s6onPBG8HqTKO6LQ9K3kdO1qBBkMMjdOgULQ1MrWThEbpU7NSTiwLYpEta/jAvrKRCAeKIlQE8p6htZmPy9aRUZuae66JgLcAlzD2vviX9sVB1asFABJVswL1RgZ55/8IzZaUrFjzOo9OHK4hmEOtudzkML+5silsAYdC+1BZugph2x94ai17YmZTCL1XyUa5Ke4q80cj+i9rOTgzhZs+mruyhL/AvNVOXilsgqCdNqSz77naWzC3pVGbxOewIDAQAB",
|
|
6
7
|
"permissions": [
|
|
7
8
|
"activeTab",
|
|
8
9
|
"identity",
|
|
9
|
-
"sidePanel",
|
|
10
10
|
"storage",
|
|
11
11
|
"tabs",
|
|
12
12
|
"webNavigation",
|
|
13
13
|
"scripting",
|
|
14
|
-
"
|
|
14
|
+
"cookies",
|
|
15
|
+
"debugger",
|
|
16
|
+
"alarms",
|
|
17
|
+
"nativeMessaging"
|
|
15
18
|
],
|
|
16
19
|
"host_permissions": [
|
|
17
20
|
"http://localhost/*",
|
|
18
21
|
"https://localhost/*",
|
|
22
|
+
"https://claude.ai/*",
|
|
23
|
+
"https://chatgpt.com/*",
|
|
19
24
|
"<all_urls>"
|
|
20
25
|
],
|
|
21
26
|
"content_security_policy": {
|
|
@@ -23,8 +28,12 @@
|
|
|
23
28
|
},
|
|
24
29
|
"content_scripts": [
|
|
25
30
|
{
|
|
26
|
-
"matches": [
|
|
27
|
-
|
|
31
|
+
"matches": [
|
|
32
|
+
"<all_urls>"
|
|
33
|
+
],
|
|
34
|
+
"js": [
|
|
35
|
+
"content-script.js"
|
|
36
|
+
],
|
|
28
37
|
"run_at": "document_idle"
|
|
29
38
|
}
|
|
30
39
|
],
|
|
@@ -40,18 +49,33 @@
|
|
|
40
49
|
"128": "icon128.png"
|
|
41
50
|
}
|
|
42
51
|
},
|
|
43
|
-
"side_panel": {
|
|
44
|
-
"default_path": "sidebar.html"
|
|
45
|
-
},
|
|
46
52
|
"icons": {
|
|
47
53
|
"16": "icon16.png",
|
|
48
54
|
"48": "icon48.png",
|
|
49
55
|
"128": "icon128.png"
|
|
50
56
|
},
|
|
57
|
+
"chrome_url_overrides": {
|
|
58
|
+
"newtab": "newtab.html"
|
|
59
|
+
},
|
|
60
|
+
"chrome_settings_overrides": {
|
|
61
|
+
"search_provider": {
|
|
62
|
+
"name": "Hanzo",
|
|
63
|
+
"keyword": "hanzo",
|
|
64
|
+
"search_url": "https://hanzo.chat/c/new?q={searchTerms}&submit=true",
|
|
65
|
+
"favicon_url": "https://hanzo.ai/favicon.ico",
|
|
66
|
+
"encoding": "UTF-8",
|
|
67
|
+
"is_default": true
|
|
68
|
+
}
|
|
69
|
+
},
|
|
51
70
|
"web_accessible_resources": [
|
|
52
71
|
{
|
|
53
|
-
"resources": [
|
|
54
|
-
|
|
72
|
+
"resources": [
|
|
73
|
+
"ai-worker.js",
|
|
74
|
+
"models/*"
|
|
75
|
+
],
|
|
76
|
+
"matches": [
|
|
77
|
+
"<all_urls>"
|
|
78
|
+
]
|
|
55
79
|
}
|
|
56
80
|
]
|
|
57
81
|
}
|
package/src/newtab.css
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/* Hanzo Answer Engine — strict monochrome (brand primaryColor #FFFFFF).
|
|
2
|
+
Shared tokens mirror popup.css / sidebar.css. */
|
|
3
|
+
:root {
|
|
4
|
+
--bg: #0a0a0a;
|
|
5
|
+
--surface: #0f0f0f;
|
|
6
|
+
--surface-2: #171717;
|
|
7
|
+
--surface-3: #1e1e1e;
|
|
8
|
+
--text: #fafafa;
|
|
9
|
+
--text-2: #a3a3a3;
|
|
10
|
+
--text-3: #666666;
|
|
11
|
+
--border: rgba(255, 255, 255, 0.10);
|
|
12
|
+
--border-2: rgba(255, 255, 255, 0.16);
|
|
13
|
+
--glass: rgba(255, 255, 255, 0.04);
|
|
14
|
+
--glass-2: rgba(255, 255, 255, 0.07);
|
|
15
|
+
--radius: 12px;
|
|
16
|
+
--radius-sm: 8px;
|
|
17
|
+
--radius-xs: 6px;
|
|
18
|
+
--sidebar-w: 232px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
* { box-sizing: border-box; }
|
|
22
|
+
|
|
23
|
+
html, body {
|
|
24
|
+
margin: 0;
|
|
25
|
+
height: 100%;
|
|
26
|
+
background: var(--bg);
|
|
27
|
+
color: var(--text);
|
|
28
|
+
font-family: 'Geist Sans', -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', sans-serif;
|
|
29
|
+
-webkit-font-smoothing: antialiased;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
a { color: inherit; text-decoration: none; }
|
|
33
|
+
|
|
34
|
+
.ae-root {
|
|
35
|
+
display: grid;
|
|
36
|
+
grid-template-columns: var(--sidebar-w) 1fr;
|
|
37
|
+
height: 100vh;
|
|
38
|
+
transition: grid-template-columns 0.18s ease;
|
|
39
|
+
}
|
|
40
|
+
.ae-root.collapsed { grid-template-columns: 60px 1fr; }
|
|
41
|
+
|
|
42
|
+
/* ── Sidebar ─────────────────────────────────────────────── */
|
|
43
|
+
.ae-sidebar {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
gap: 6px;
|
|
47
|
+
padding: 14px 12px;
|
|
48
|
+
border-right: 1px solid var(--border);
|
|
49
|
+
background: var(--surface);
|
|
50
|
+
min-width: 0;
|
|
51
|
+
}
|
|
52
|
+
.ae-side-top { display: flex; align-items: center; justify-content: space-between; }
|
|
53
|
+
.ae-brand {
|
|
54
|
+
display: flex; align-items: center; gap: 9px;
|
|
55
|
+
background: none; border: none; color: var(--text);
|
|
56
|
+
font-size: 15px; font-weight: 600; letter-spacing: -0.01em;
|
|
57
|
+
cursor: pointer; padding: 4px; min-width: 0;
|
|
58
|
+
}
|
|
59
|
+
.ae-brand span { white-space: nowrap; overflow: hidden; }
|
|
60
|
+
.ae-collapse, .ae-new, .ae-signin {
|
|
61
|
+
cursor: pointer; font-family: inherit;
|
|
62
|
+
}
|
|
63
|
+
.ae-collapse {
|
|
64
|
+
background: none; border: none; color: var(--text-3);
|
|
65
|
+
padding: 4px; border-radius: var(--radius-xs); display: flex;
|
|
66
|
+
}
|
|
67
|
+
.ae-collapse:hover { color: var(--text); background: var(--glass); }
|
|
68
|
+
.ae-new {
|
|
69
|
+
display: flex; align-items: center; gap: 8px;
|
|
70
|
+
margin-top: 8px; padding: 9px 12px;
|
|
71
|
+
background: var(--text); color: #000; border: none;
|
|
72
|
+
border-radius: var(--radius-sm); font-size: 13px; font-weight: 600;
|
|
73
|
+
white-space: nowrap; overflow: hidden;
|
|
74
|
+
}
|
|
75
|
+
.ae-new:hover { background: #fff; }
|
|
76
|
+
.ae-nav { display: flex; flex-direction: column; margin-top: 12px; gap: 1px; }
|
|
77
|
+
.ae-nav a {
|
|
78
|
+
padding: 8px 10px; border-radius: var(--radius-xs);
|
|
79
|
+
color: var(--text-2); font-size: 13px; white-space: nowrap;
|
|
80
|
+
}
|
|
81
|
+
.ae-nav a:hover { background: var(--glass); color: var(--text); }
|
|
82
|
+
.ae-side-bottom { margin-top: auto; }
|
|
83
|
+
.ae-signin {
|
|
84
|
+
width: 100%; padding: 9px 12px;
|
|
85
|
+
background: var(--glass-2); border: 1px solid var(--border);
|
|
86
|
+
color: var(--text); border-radius: var(--radius-sm);
|
|
87
|
+
font-size: 12.5px; font-weight: 500; white-space: nowrap; overflow: hidden;
|
|
88
|
+
}
|
|
89
|
+
.ae-signin:hover { border-color: var(--border-2); background: var(--surface-2); }
|
|
90
|
+
.ae-signin.big { width: auto; padding: 10px 18px; font-size: 14px; }
|
|
91
|
+
.collapsed .ae-brand span, .collapsed .ae-new span, .collapsed .ae-nav { display: none; }
|
|
92
|
+
|
|
93
|
+
/* ── Main ────────────────────────────────────────────────── */
|
|
94
|
+
.ae-main { min-width: 0; overflow: hidden; display: flex; flex-direction: column; }
|
|
95
|
+
|
|
96
|
+
/* Hero (empty state) */
|
|
97
|
+
.ae-hero {
|
|
98
|
+
flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
99
|
+
gap: 26px; padding: 24px; overflow-y: auto;
|
|
100
|
+
}
|
|
101
|
+
.ae-wordmark {
|
|
102
|
+
display: flex; align-items: center; gap: 14px;
|
|
103
|
+
font-size: 46px; font-weight: 600; letter-spacing: -0.03em; color: var(--text);
|
|
104
|
+
}
|
|
105
|
+
.ae-hero-card { width: 100%; max-width: 720px; }
|
|
106
|
+
.ae-news { width: 100%; max-width: 720px; }
|
|
107
|
+
.ae-news-head, .ae-followups-head, .ae-model-group-label {
|
|
108
|
+
font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em;
|
|
109
|
+
color: var(--text-3); margin-bottom: 10px;
|
|
110
|
+
}
|
|
111
|
+
.ae-news-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
|
|
112
|
+
.ae-news-item {
|
|
113
|
+
display: flex; flex-direction: column; gap: 4px;
|
|
114
|
+
padding: 11px 13px; border: 1px solid var(--border);
|
|
115
|
+
border-radius: var(--radius-sm); background: var(--surface);
|
|
116
|
+
}
|
|
117
|
+
.ae-news-item:hover { border-color: var(--border-2); background: var(--surface-2); }
|
|
118
|
+
.ae-news-title { font-size: 13px; color: var(--text); line-height: 1.35; }
|
|
119
|
+
.ae-news-meta { font-size: 11px; color: var(--text-3); }
|
|
120
|
+
|
|
121
|
+
/* ── Input card ──────────────────────────────────────────── */
|
|
122
|
+
.ae-card {
|
|
123
|
+
background: var(--surface-2);
|
|
124
|
+
border: 1px solid var(--border-2);
|
|
125
|
+
border-radius: var(--radius);
|
|
126
|
+
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(255, 255, 255, 0.02);
|
|
127
|
+
overflow: hidden;
|
|
128
|
+
}
|
|
129
|
+
.ae-input {
|
|
130
|
+
display: block; width: 100%; resize: none;
|
|
131
|
+
padding: 16px 18px 6px; min-height: 30px; max-height: 200px;
|
|
132
|
+
background: transparent; border: none; outline: none;
|
|
133
|
+
color: var(--text); font-family: inherit; font-size: 15px; line-height: 1.5;
|
|
134
|
+
}
|
|
135
|
+
.ae-input::placeholder { color: var(--text-3); }
|
|
136
|
+
.ae-card-row {
|
|
137
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
138
|
+
gap: 8px; padding: 8px 10px 10px; flex-wrap: wrap;
|
|
139
|
+
}
|
|
140
|
+
.ae-chips { display: flex; gap: 4px; flex-wrap: wrap; }
|
|
141
|
+
.ae-chip {
|
|
142
|
+
padding: 4px 9px; border-radius: 999px; cursor: pointer;
|
|
143
|
+
background: var(--glass); border: 1px solid var(--border);
|
|
144
|
+
color: var(--text-3); font-size: 11px; font-family: inherit;
|
|
145
|
+
}
|
|
146
|
+
.ae-chip:hover { color: var(--text-2); }
|
|
147
|
+
.ae-chip.on { background: var(--text); color: #000; border-color: var(--text); }
|
|
148
|
+
.ae-card-right { display: flex; align-items: center; gap: 6px; }
|
|
149
|
+
.ae-modes { display: flex; gap: 2px; background: var(--glass); border-radius: var(--radius-xs); padding: 2px; }
|
|
150
|
+
.ae-mode {
|
|
151
|
+
padding: 4px 9px; border: none; background: none; cursor: pointer;
|
|
152
|
+
color: var(--text-3); font-size: 11.5px; font-family: inherit; border-radius: 5px;
|
|
153
|
+
}
|
|
154
|
+
.ae-mode:hover { color: var(--text-2); }
|
|
155
|
+
.ae-mode.on { background: var(--surface-3); color: var(--text); }
|
|
156
|
+
.ae-send {
|
|
157
|
+
width: 34px; height: 34px; flex-shrink: 0; cursor: pointer;
|
|
158
|
+
display: flex; align-items: center; justify-content: center;
|
|
159
|
+
background: var(--text); color: #000; border: none; border-radius: 50%;
|
|
160
|
+
}
|
|
161
|
+
.ae-send:disabled { opacity: 0.4; cursor: default; }
|
|
162
|
+
|
|
163
|
+
/* ── Model picker ────────────────────────────────────────── */
|
|
164
|
+
.ae-picker { position: relative; }
|
|
165
|
+
.ae-picker-btn {
|
|
166
|
+
display: flex; align-items: center; gap: 6px; cursor: pointer;
|
|
167
|
+
padding: 6px 10px; background: var(--glass); border: 1px solid var(--border);
|
|
168
|
+
border-radius: var(--radius-xs); color: var(--text-2); font-size: 12px; font-family: inherit;
|
|
169
|
+
}
|
|
170
|
+
.ae-picker-btn:hover { color: var(--text); border-color: var(--border-2); }
|
|
171
|
+
.ae-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-2); }
|
|
172
|
+
.ae-picker-menu {
|
|
173
|
+
position: absolute; bottom: calc(100% + 6px); right: 0; z-index: 40;
|
|
174
|
+
width: 320px; max-height: 420px; display: flex; flex-direction: column;
|
|
175
|
+
background: var(--surface-2); border: 1px solid var(--border-2);
|
|
176
|
+
border-radius: var(--radius-sm); box-shadow: 0 12px 48px rgba(0, 0, 0, 0.6); overflow: hidden;
|
|
177
|
+
}
|
|
178
|
+
.ae-picker-filter {
|
|
179
|
+
margin: 8px; padding: 8px 10px; background: var(--bg);
|
|
180
|
+
border: 1px solid var(--border); border-radius: var(--radius-xs);
|
|
181
|
+
color: var(--text); font-family: inherit; font-size: 13px; outline: none;
|
|
182
|
+
}
|
|
183
|
+
.ae-picker-list { overflow-y: auto; padding: 0 6px 6px; }
|
|
184
|
+
.ae-model-group-label { padding: 8px 8px 4px; margin: 0; }
|
|
185
|
+
.ae-model-row {
|
|
186
|
+
display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left;
|
|
187
|
+
padding: 8px 10px; background: none; border: none; cursor: pointer;
|
|
188
|
+
border-radius: var(--radius-xs); color: var(--text); font-family: inherit;
|
|
189
|
+
}
|
|
190
|
+
.ae-model-row:hover { background: var(--glass); }
|
|
191
|
+
.ae-model-row.active { background: var(--glass-2); }
|
|
192
|
+
.ae-model-name { font-size: 13px; display: flex; align-items: center; gap: 7px; }
|
|
193
|
+
.ae-model-desc { font-size: 11px; color: var(--text-3); line-height: 1.3; }
|
|
194
|
+
.ae-badge {
|
|
195
|
+
font-size: 9px; text-transform: uppercase; letter-spacing: 0.04em;
|
|
196
|
+
padding: 1px 5px; border-radius: 4px; background: var(--glass-2); color: var(--text-2);
|
|
197
|
+
}
|
|
198
|
+
.ae-plan-banner {
|
|
199
|
+
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
200
|
+
padding: 10px 12px; border-top: 1px solid var(--border);
|
|
201
|
+
font-size: 11.5px; color: var(--text-2); background: var(--surface);
|
|
202
|
+
}
|
|
203
|
+
.ae-plan-tiers { display: flex; gap: 8px; }
|
|
204
|
+
.ae-plan-tier b { color: var(--text); }
|
|
205
|
+
|
|
206
|
+
/* ── Answer view ─────────────────────────────────────────── */
|
|
207
|
+
.ae-answer-wrap { flex: 1; display: flex; flex-direction: column; min-height: 0; }
|
|
208
|
+
.ae-answer-scroll { flex: 1; overflow-y: auto; padding: 32px 28px 20px; }
|
|
209
|
+
.ae-answer-scroll > * { max-width: 760px; margin-left: auto; margin-right: auto; }
|
|
210
|
+
.ae-question { font-size: 26px; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 20px; }
|
|
211
|
+
.ae-sources { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 16px; margin-bottom: 8px; }
|
|
212
|
+
.ae-source {
|
|
213
|
+
flex: 0 0 190px; display: flex; flex-direction: column; gap: 6px;
|
|
214
|
+
padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm);
|
|
215
|
+
background: var(--surface);
|
|
216
|
+
}
|
|
217
|
+
.ae-source:hover { border-color: var(--border-2); background: var(--surface-2); }
|
|
218
|
+
.ae-source-head { display: flex; align-items: center; gap: 6px; }
|
|
219
|
+
.ae-source-head img { border-radius: 3px; }
|
|
220
|
+
.ae-source-host { font-size: 11px; color: var(--text-3); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
221
|
+
.ae-source-idx { font-size: 10px; color: var(--text-3); border: 1px solid var(--border); border-radius: 4px; padding: 0 5px; }
|
|
222
|
+
.ae-source-title { font-size: 12.5px; line-height: 1.35; color: var(--text); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
|
|
223
|
+
|
|
224
|
+
.ae-status { display: flex; align-items: center; gap: 10px; color: var(--text-2); font-size: 14px; padding: 8px 0; }
|
|
225
|
+
|
|
226
|
+
.ae-answer.markdown { font-size: 15px; line-height: 1.65; color: var(--text); }
|
|
227
|
+
.markdown h2 { font-size: 20px; margin: 22px 0 10px; font-weight: 600; }
|
|
228
|
+
.markdown h3 { font-size: 17px; margin: 18px 0 8px; font-weight: 600; }
|
|
229
|
+
.markdown h4, .markdown h5 { font-size: 15px; margin: 14px 0 6px; font-weight: 600; }
|
|
230
|
+
.markdown p { margin: 0 0 12px; }
|
|
231
|
+
.markdown ul, .markdown ol { margin: 0 0 12px; padding-left: 22px; }
|
|
232
|
+
.markdown li { margin: 4px 0; }
|
|
233
|
+
.markdown a { color: var(--text); text-decoration: underline; text-decoration-color: var(--text-3); text-underline-offset: 2px; }
|
|
234
|
+
.markdown a:hover { text-decoration-color: var(--text); }
|
|
235
|
+
.markdown code { background: var(--surface-2); padding: 1px 5px; border-radius: 4px; font-size: 0.9em; font-family: 'Geist Mono', ui-monospace, monospace; }
|
|
236
|
+
|
|
237
|
+
.ae-followups { margin-top: 26px; border-top: 1px solid var(--border); padding-top: 16px; }
|
|
238
|
+
.ae-followup {
|
|
239
|
+
display: flex; align-items: center; justify-content: space-between; width: 100%;
|
|
240
|
+
padding: 12px 4px; background: none; border: none; border-bottom: 1px solid var(--border);
|
|
241
|
+
color: var(--text); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;
|
|
242
|
+
}
|
|
243
|
+
.ae-followup:hover { color: var(--text); }
|
|
244
|
+
.ae-followup:hover .ae-followup-arrow { color: var(--text); transform: translateX(2px); }
|
|
245
|
+
.ae-followup-arrow { color: var(--text-3); transition: transform 0.12s ease; }
|
|
246
|
+
|
|
247
|
+
.ae-error { color: var(--text-2); padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
|
|
248
|
+
.ae-auth { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; padding: 8px 0; }
|
|
249
|
+
.ae-auth p { margin: 0; color: var(--text-2); font-size: 15px; }
|
|
250
|
+
|
|
251
|
+
.ae-answer-composer { padding: 12px 28px 20px; border-top: 1px solid var(--border); background: var(--bg); }
|
|
252
|
+
.ae-answer-composer > .ae-card { max-width: 760px; margin: 0 auto; }
|
|
253
|
+
|
|
254
|
+
/* ── Spinner ─────────────────────────────────────────────── */
|
|
255
|
+
.ae-spinner {
|
|
256
|
+
width: 14px; height: 14px; border-radius: 50%; display: inline-block;
|
|
257
|
+
border: 2px solid var(--border-2); border-top-color: var(--text);
|
|
258
|
+
animation: ae-spin 0.7s linear infinite;
|
|
259
|
+
}
|
|
260
|
+
@keyframes ae-spin { to { transform: rotate(360deg); } }
|
|
261
|
+
|
|
262
|
+
@media (max-width: 720px) {
|
|
263
|
+
.ae-root, .ae-root.collapsed { grid-template-columns: 1fr; }
|
|
264
|
+
.ae-sidebar { display: none; }
|
|
265
|
+
.ae-news-grid { grid-template-columns: 1fr; }
|
|
266
|
+
.ae-hero { gap: 20px; padding: 20px 16px; justify-content: flex-start; padding-top: 48px; }
|
|
267
|
+
.ae-wordmark { font-size: 34px; }
|
|
268
|
+
.ae-answer-scroll { padding: 20px 16px 16px; }
|
|
269
|
+
.ae-answer-composer { padding: 10px 16px 16px; }
|
|
270
|
+
.ae-question { font-size: 21px; }
|
|
271
|
+
/* Input card: let the control row wrap so the model picker + send stay
|
|
272
|
+
reachable at narrow widths (mobile-first). */
|
|
273
|
+
.ae-card-row { flex-direction: column; align-items: stretch; gap: 10px; }
|
|
274
|
+
.ae-card-right { justify-content: space-between; }
|
|
275
|
+
.ae-modes { flex: 1; justify-content: space-between; }
|
|
276
|
+
.ae-source { flex-basis: 150px; }
|
|
277
|
+
}
|