Skip to content

Commit 4ad32cc

Browse files
committed
feat: by default disable remember and enhance security
1 parent 540b718 commit 4ad32cc

11 files changed

Lines changed: 235 additions & 16 deletions

File tree

.github/pages-index.html

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Themed.js – Demos</title>
7+
<style>
8+
* { margin: 0; padding: 0; box-sizing: border-box; }
9+
body {
10+
font-family: system-ui, -apple-system, sans-serif;
11+
background: #0f172a;
12+
color: #e2e8f0;
13+
min-height: 100vh;
14+
display: flex;
15+
flex-direction: column;
16+
align-items: center;
17+
justify-content: center;
18+
padding: 2rem;
19+
}
20+
h1 { font-size: 2rem; margin-bottom: 0.5rem; color: #f8fafc; }
21+
p { color: #94a3b8; margin-bottom: 2rem; }
22+
.links {
23+
display: flex;
24+
flex-wrap: wrap;
25+
gap: 1rem;
26+
justify-content: center;
27+
}
28+
a {
29+
display: inline-block;
30+
padding: 0.75rem 1.5rem;
31+
background: #334155;
32+
color: #f8fafc;
33+
text-decoration: none;
34+
border-radius: 0.5rem;
35+
font-weight: 500;
36+
transition: background 0.2s;
37+
}
38+
a:hover { background: #475569; }
39+
</style>
40+
</head>
41+
<body>
42+
<h1>Themed.js</h1>
43+
<p>Theme management with AI-powered generation. Pick a demo:</p>
44+
<div class="links">
45+
<a href="react/">React</a>
46+
<a href="vue/">Vue</a>
47+
<a href="vanilla/">Vanilla</a>
48+
</div>
49+
</body>
50+
</html>

.github/workflows/deploy-pages.yml

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Deploy Themed.js demos (React, Vue, Vanilla) to GitHub Pages
2+
# Site: https://<user>.github.io/themed.js/
3+
4+
name: Deploy to GitHub Pages
5+
6+
on:
7+
push:
8+
branches: [main]
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
pages: write
14+
id-token: write
15+
16+
concurrency:
17+
group: 'pages'
18+
cancel-in-progress: true
19+
20+
jobs:
21+
build:
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Checkout
25+
uses: actions/checkout@v4
26+
27+
- name: Setup Node
28+
uses: actions/setup-node@v4
29+
with:
30+
node-version: '20'
31+
cache: 'pnpm'
32+
33+
- name: Setup pnpm
34+
uses: pnpm/action-setup@v4
35+
with:
36+
version: 9
37+
38+
- name: Install dependencies
39+
run: pnpm install --frozen-lockfile
40+
41+
- name: Build core
42+
run: pnpm --filter @themed.js/core run build
43+
44+
- name: Build React example
45+
run: pnpm --filter themed-react-example run build
46+
env:
47+
VITE_BASE: /${{ github.event.repository.name }}/react/
48+
49+
- name: Build Vue example
50+
run: pnpm --filter themed-vue-example run build
51+
env:
52+
VITE_BASE: /${{ github.event.repository.name }}/vue/
53+
54+
- name: Build Vanilla example
55+
run: pnpm --filter themed-vanilla-example run build
56+
env:
57+
VITE_BASE: /${{ github.event.repository.name }}/vanilla/
58+
59+
- name: Prepare deploy directory
60+
run: |
61+
mkdir -p deploy/react deploy/vue deploy/vanilla
62+
cp -r examples/react/dist/* deploy/react/
63+
cp -r examples/vue/dist/* deploy/vue/
64+
cp -r examples/vanilla/dist/* deploy/vanilla/
65+
cp .github/pages-index.html deploy/index.html
66+
67+
- name: Setup Pages
68+
uses: actions/configure-pages@v5
69+
70+
- name: Upload artifact
71+
uses: actions/upload-pages-artifact@v4
72+
with:
73+
path: deploy
74+
75+
deploy:
76+
environment:
77+
name: github-pages
78+
url: ${{ steps.deployment.outputs.page_url }}
79+
runs-on: ubuntu-latest
80+
needs: build
81+
steps:
82+
- name: Deploy to GitHub Pages
83+
id: deployment
84+
uses: actions/deploy-pages@v4

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,24 @@ interface ThemeTokens {
266266
}
267267
```
268268

269+
## Deploying to GitHub Pages
270+
271+
The repo includes a workflow that builds the React, Vue, and Vanilla examples and deploys them to GitHub Pages.
272+
273+
1. **Enable GitHub Pages**
274+
In the repo: **Settings → Pages → Build and deployment**:
275+
- Source: **GitHub Actions**.
276+
277+
2. **Push to `main`**
278+
The workflow runs on every push to `main` (or trigger it manually via **Actions → Deploy to GitHub Pages → Run workflow**).
279+
280+
3. **Open the site**
281+
After deployment, the site is at:
282+
**https://\<your-username\>.github.io/themed.js/**
283+
- Landing: links to [React](https://starit.github.io/themed.js/react/), [Vue](https://starit.github.io/themed.js/vue/), [Vanilla](https://starit.github.io/themed.js/vanilla/) demos.
284+
285+
API keys are not embedded; users enter their own key in each demo’s UI (safe for public hosting).
286+
269287
## Development
270288

271289
```bash

examples/react/src/App.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function AIConfigPanel() {
2828
const { configureAI, isConfigured, modelInfo } = useAITheme();
2929
const [apiKey, setApiKey] = useState('');
3030
const [provider, setProvider] = useState<typeof PROVIDERS[number]['value']>('openai');
31-
const [remember, setRemember] = useState(true);
31+
const [remember, setRemember] = useState(false);
3232
const [expanded, setExpanded] = useState(!isConfigured);
3333

3434
// Load saved config on mount
@@ -40,6 +40,7 @@ function AIConfigPanel() {
4040
if (key) {
4141
setApiKey(key);
4242
setProvider(p || 'openai');
43+
setRemember(true);
4344
configureAI({
4445
provider: p || 'openai',
4546
apiKey: key,
@@ -70,6 +71,7 @@ function AIConfigPanel() {
7071
} else {
7172
localStorage.removeItem(AI_CONFIG_STORAGE_KEY);
7273
}
74+
setApiKey('');
7375
setExpanded(false);
7476
};
7577

@@ -90,7 +92,10 @@ function AIConfigPanel() {
9092
{expanded && (
9193
<div className="ai-config-form">
9294
<p className="ai-config-hint">
93-
Enter your API key to enable AI theme generation. It stays in your browser only.
95+
Your API key is stored only on your device and is never sent to our servers or collected. It is used only to call the AI provider you choose.
96+
</p>
97+
<p className="ai-config-security">
98+
We never collect or log your key. For stronger security, uncheck Remember so the key is not saved to disk (session only).
9499
</p>
95100
<div className="ai-config-row">
96101
<select
@@ -105,9 +110,11 @@ function AIConfigPanel() {
105110
))}
106111
</select>
107112
<input
108-
type="password"
109-
className="ai-config-input"
113+
type="text"
114+
autoComplete="off"
115+
className="ai-config-input ai-config-key-input"
110116
placeholder="API Key"
117+
spellCheck={false}
111118
value={apiKey}
112119
onChange={(e) => setApiKey(e.target.value)}
113120
onKeyDown={(e) => e.key === 'Enter' && handleSave()}
@@ -197,8 +204,8 @@ function AIGenerator() {
197204
try {
198205
await generate(prompt);
199206
setPrompt('');
200-
} catch (e) {
201-
console.error('Failed to generate:', e);
207+
} catch {
208+
// Error shown via useAITheme().error; do not log to avoid leaking any sensitive data
202209
}
203210
};
204211

examples/react/src/styles.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ p {
9797
margin: 0.75rem 0 0.5rem;
9898
}
9999

100+
.ai-config-security {
101+
font-size: var(--themed-font-size-xs);
102+
color: var(--themed-color-text-secondary);
103+
margin: 0 0 0.75rem;
104+
padding: 0.5rem 0.75rem;
105+
background: var(--themed-color-background);
106+
border-radius: 0.375rem;
107+
border: 1px solid var(--themed-color-border);
108+
}
109+
100110
.ai-config-row {
101111
display: flex;
102112
gap: 0.5rem;
@@ -130,6 +140,11 @@ p {
130140
border-color: var(--themed-color-primary);
131141
}
132142

143+
/* Mask API key so browser does not offer to save as password; type="text" + this avoids password-manager prompts */
144+
.ai-config-key-input {
145+
-webkit-text-security: disc;
146+
}
147+
133148
.ai-config-actions {
134149
display: flex;
135150
flex-wrap: wrap;

examples/react/vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { defineConfig } from 'vite';
22
import react from '@vitejs/plugin-react';
33

44
export default defineConfig({
5+
base: process.env.VITE_BASE ?? '/',
56
plugins: [react()],
67
server: {
78
port: 3001,

examples/vanilla/index.html

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@
104104
margin: 0.75rem 0 0.5rem;
105105
}
106106

107+
.ai-config-security {
108+
font-size: var(--themed-font-size-xs);
109+
color: var(--themed-color-text-secondary);
110+
margin: 0 0 0.75rem;
111+
padding: 0.5rem 0.75rem;
112+
background: var(--themed-color-background);
113+
border-radius: 0.375rem;
114+
border: 1px solid var(--themed-color-border);
115+
}
116+
107117
.ai-config-row {
108118
display: flex;
109119
gap: 0.5rem;
@@ -137,6 +147,11 @@
137147
border-color: var(--themed-color-primary);
138148
}
139149

150+
/* Mask API key so browser does not offer to save as password; type="text" + this avoids password-manager prompts */
151+
.ai-config-key-input {
152+
-webkit-text-security: disc;
153+
}
154+
140155
.ai-config-actions {
141156
display: flex;
142157
flex-wrap: wrap;
@@ -361,7 +376,10 @@ <h1>Themed.js Demo</h1>
361376
</button>
362377
<div class="ai-config-form" id="ai-config-form" style="display: none">
363378
<p class="ai-config-hint">
364-
Enter your API key to enable AI theme generation. It stays in your browser only.
379+
Your API key is stored only on your device and is never sent to our servers or collected. It is used only to call the AI provider you choose.
380+
</p>
381+
<p class="ai-config-security">
382+
We never collect or log your key. For stronger security, uncheck Remember so the key is not saved to disk (session only).
365383
</p>
366384
<div class="ai-config-row">
367385
<select id="ai-config-provider" class="ai-config-select">
@@ -373,15 +391,17 @@ <h1>Themed.js Demo</h1>
373391
<option value="deepseek">DeepSeek</option>
374392
</select>
375393
<input
376-
type="password"
394+
type="text"
377395
id="ai-config-key"
378-
class="ai-config-input"
396+
class="ai-config-input ai-config-key-input"
379397
placeholder="API Key"
398+
autocomplete="off"
399+
spellcheck="false"
380400
/>
381401
</div>
382402
<div class="ai-config-actions">
383403
<label class="ai-config-remember">
384-
<input type="checkbox" id="ai-config-remember" checked />
404+
<input type="checkbox" id="ai-config-remember" />
385405
Remember (localStorage)
386406
</label>
387407
<div class="ai-config-buttons">

examples/vanilla/vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { defineConfig } from 'vite';
22

33
export default defineConfig({
4+
base: process.env.VITE_BASE ?? '/',
45
server: {
56
port: 3000,
67
},

examples/vue/src/App.vue

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const { generate, isGenerating, error, isConfigured, modelInfo, configureAI } =
1818
1919
const apiKey = ref('');
2020
const provider = ref<(typeof PROVIDERS)[number]['value']>('openai');
21-
const remember = ref(true);
21+
const remember = ref(false);
2222
const configExpanded = ref(true);
2323
2424
onMounted(() => {
@@ -29,6 +29,7 @@ onMounted(() => {
2929
if (key) {
3030
apiKey.value = key;
3131
provider.value = p || 'openai';
32+
remember.value = true;
3233
const prov = PROVIDERS.find((x) => x.value === (p || 'openai'));
3334
configureAI({
3435
provider: p || 'openai',
@@ -67,6 +68,7 @@ const handleSaveConfig = () => {
6768
} else {
6869
localStorage.removeItem(AI_CONFIG_STORAGE_KEY);
6970
}
71+
apiKey.value = '';
7072
configExpanded.value = false;
7173
};
7274
@@ -96,8 +98,8 @@ const handleGenerate = async () => {
9698
try {
9799
await generate(prompt.value);
98100
prompt.value = '';
99-
} catch (e) {
100-
console.error('Failed to generate:', e);
101+
} catch {
102+
// Error shown via useAITheme().error; do not log to avoid leaking any sensitive data
101103
}
102104
};
103105
@@ -162,7 +164,10 @@ const downloadThemeColors = () => {
162164
</button>
163165
<div v-show="configExpanded" class="ai-config-form">
164166
<p class="ai-config-hint">
165-
Enter your API key to enable AI theme generation. It stays in your browser only.
167+
Your API key is stored only on your device and is never sent to our servers or collected. It is used only to call the AI provider you choose.
168+
</p>
169+
<p class="ai-config-security">
170+
We never collect or log your key. For stronger security, uncheck Remember so the key is not saved to disk (session only).
166171
</p>
167172
<div class="ai-config-row">
168173
<select v-model="provider" class="ai-config-select">
@@ -176,8 +181,10 @@ const downloadThemeColors = () => {
176181
</select>
177182
<input
178183
v-model="apiKey"
179-
type="password"
180-
class="ai-config-input"
184+
type="text"
185+
autocomplete="off"
186+
spellcheck="false"
187+
class="ai-config-input ai-config-key-input"
181188
placeholder="API Key"
182189
@keydown.enter="handleSaveConfig"
183190
/>

0 commit comments

Comments
 (0)