Skip to content

Commit 15fefb7

Browse files
committed
Merge branch 'main' into l10n-es
2 parents 26a31f1 + b320744 commit 15fefb7

File tree

145 files changed

+5556
-1822
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

145 files changed

+5556
-1822
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
}
77
],
88
"eslint.useFlatConfig": true,
9-
"eslint.validate": ["svelte"]
9+
"eslint.validate": ["svelte", "markdown"]
1010
}

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,57 @@ The article (and automatic l10ns of same) will be previewable.
8888

8989
If you are sufficiently changing prominent text, consider inspecting relevant l10ns as well as the original.
9090

91+
### Image Optimization
92+
93+
To improve performance, images are automatically processed and delivered in multiple formats (e.g., WebP, AVIF) and resolutions. This is handled by the `Image` Svelte component, which optimizes images located in the `src/assets/images` directory.
94+
95+
#### Usage
96+
97+
To use optimized images, first, ensure your image file (e.g., `my-image.png`) is located within the `src/assets/images` directory. Then, choose one of the following methods to embed it:
98+
99+
- **In Markdown Files:** Use standard Markdown image syntax. The system will automatically process the image through the `Image` component. The path in the Markdown should be relative to `src/assets/images` and start with a forward slash `/`.
100+
101+
Example:
102+
103+
```markdown
104+
![A description of my image](/my-image.png)
105+
```
106+
107+
If your image is in a subdirectory, for example `src/assets/images/illustrations/another-image.jpg`, the path would be:
108+
109+
```markdown
110+
![Another image description](/illustrations/another-image.jpg)
111+
```
112+
113+
- **In Svelte Components:** If you need to use the `Image` component directly in a Svelte component, import it and pass the `src` prop relative to `src/assets/images` and starting with a forward slash `/`.
114+
115+
Example:
116+
117+
```svelte
118+
<script>
119+
import Image from '$lib/components/Image.svelte'
120+
</script>
121+
122+
<Image src="/my-image.png" alt="A description of my image" />
123+
```
124+
125+
## Redirects
126+
127+
The `src/lib/redirects.ts` file defines server-side redirects for specific paths. This is useful for handling cases like old URLs or vanity URLs.
128+
129+
The `REDIRECTS` object maps incoming paths to their target paths. When a request comes in for a path defined in `REDIRECTS`, the `handleRedirects` function issues a 301 (Moved Permanently) redirect to the specified target.
130+
131+
Example:
132+
133+
```typescript
134+
const REDIRECTS: Record<string, string> = {
135+
'/old-path': '/new-path',
136+
'/legacy-page': '/current-page'
137+
}
138+
```
139+
140+
To add a new redirect, simply add a new entry to the `REDIRECTS` object in `src/lib/redirects.ts`.
141+
91142
## Deployment
92143

93144
The contents of the repository are continuously deployed to Netlify when code is pushed.

SELFIE_SETUP.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Selfie Upload Feature Setup
2+
3+
This feature allows supporters to upload selfies for the AI safety book campaign collage.
4+
5+
(touched to provoke PR deploy)
6+
7+
## Quick Start for Developers
8+
9+
You have two options:
10+
11+
### Option A: Use Anthony's Shared Test Account (Easiest)
12+
13+
- Cloud name and API key are already in `.env`
14+
- Get the `CLOUDINARY_API_SECRET` from Psono (shared password manager)
15+
- You're sharing Anthony's test account - that's fine for prototyping!
16+
- Note: Production will use a different account once we're done testing
17+
18+
### Option B: Use Your Own Free Cloudinary Account
19+
20+
Follow the setup below if you want your own isolated testing environment.
21+
22+
#### Creating Your Own Free Account:
23+
24+
1. Sign up at https://cloudinary.com/users/register_free (use GitHub login if you prefer)
25+
2. You'll get:
26+
- 25GB storage
27+
- 25GB bandwidth/month
28+
- 3 user accounts
29+
- All features we need for testing
30+
31+
### 2. Configure Upload Preset
32+
33+
In your Cloudinary dashboard:
34+
35+
1. Go to **Settings****Upload****Upload Presets**
36+
2. Click **Add upload preset**
37+
3. Configure with these EXACT settings:
38+
- **Preset name**: `selfie`
39+
- **Signing Mode**: **Unsigned**
40+
- **Folder**: Leave empty (we set in code)
41+
- **Access control**: Leave as is
42+
43+
4. Under **Upload Manipulations****Incoming Transformation**:
44+
- Edit → Add transformation
45+
- Enter: `c_fill,g_face,ar_3:4,w_1500,h_2000,q_auto`
46+
- This auto-detects faces and creates consistent 3:4 portraits
47+
48+
5. Under **Upload Control****Moderation**:
49+
- Set to **Manual**
50+
- This makes photos appear in the Moderation tab for review
51+
52+
6. Save the preset
53+
54+
### 3. Get Your Credentials
55+
56+
From your Cloudinary dashboard homepage, you'll see:
57+
58+
- **Cloud Name**: (e.g., `dyjlw1syg`)
59+
- **API Key**: (e.g., `123456789012345`)
60+
- **API Secret**: (keep this private!)
61+
62+
### 4. Environment Variables
63+
64+
Add to your `.env` file:
65+
66+
```bash
67+
# Selfie Campaign - Cloudinary settings
68+
PUBLIC_CLOUDINARY_CLOUD_NAME="your-cloud-name" # Can be shared
69+
CLOUDINARY_API_KEY="your-api-key" # Can be shared
70+
CLOUDINARY_API_SECRET="your-api-secret" # KEEP PRIVATE - never commit!
71+
```
72+
73+
**Important**:
74+
75+
- `API_SECRET` must NEVER be committed to source control
76+
- `CLOUD_NAME` and `API_KEY` are safe to share
77+
- For production, all three will be set in Netlify environment variables
78+
79+
### 5. Optional: Create Second Preset for Blurred Images
80+
81+
If you want blur to create a permanent blurred version (not just display blur):
82+
83+
1. Create another preset called `selfie_masked`
84+
2. Same settings as above, but incoming transformation:
85+
- `c_fill,g_face,ar_3:4,w_1500,h_2000,q_auto/e_blur_faces:2000`
86+
87+
## Testing the Feature
88+
89+
1. Run development server: `pnpm dev`
90+
2. Visit: http://localhost:5173/selfie
91+
3. Upload a photo (camera, file, or social media)
92+
4. Test the flow:
93+
- Photo should auto-crop to face
94+
- Blur button should work (requires API credentials)
95+
- Remove button should delete from Cloudinary
96+
- Email submission should add to metadata
97+
98+
## How It Works
99+
100+
### Upload Flow:
101+
102+
1. **Client**: Unsigned upload via widget → Cloudinary
103+
2. **Cloudinary**: Applies face detection & crop
104+
3. **Client**: Shows confirmation screen
105+
4. **User** can:
106+
- Blur face (server endpoint replaces image)
107+
- Add email (server endpoint updates metadata)
108+
- Remove (server endpoint deletes from Cloudinary)
109+
110+
### File Organization in Cloudinary:
111+
112+
```
113+
test_prototype/ # Test phase uploads (all in one folder)
114+
```
115+
116+
### Moderation Status (Automatic):
117+
118+
Photos uploaded with `moderation: 'manual'` automatically get:
119+
120+
- `pending` status - Shows in Moderation tab for review
121+
- Can be changed to `approved` or `rejected` via Cloudinary UI
122+
123+
### Tags Applied:
124+
125+
- `test_prototype` - Development/testing phase
126+
- `selfie` - Identifies as selfie upload
127+
- `has_email` - User provided email (added if email provided)
128+
- `face_masked` - User requested blur
129+
130+
### Metadata Stored:
131+
132+
- `uploaded_at` - Timestamp
133+
- `email` - If provided by user
134+
- Plus EXIF data (camera, location if present)
135+
136+
## Review Process
137+
138+
1. Log into Cloudinary Console
139+
2. Go to **Media Library → Moderation** tab
140+
3. Photos appear automatically with "pending" status
141+
4. Review and click to approve or reject each photo
142+
5. Approved photos remain in the library
143+
6. Rejected photos can be deleted or kept separate
144+
145+
## Troubleshooting
146+
147+
**"Server configuration error"**:
148+
149+
- Check your API credentials in `.env`
150+
- Ensure `CLOUDINARY_API_SECRET` is set
151+
152+
**Face detection not working**:
153+
154+
- Check upload preset has the incoming transformation
155+
- Some angles/lighting may challenge detection
156+
157+
**Blur not applying**:
158+
159+
- Requires valid API credentials
160+
- Check server console for errors
161+
162+
## Production Notes
163+
164+
For production deployment:
165+
166+
- Cloudinary nonprofit discount available (typically 25-50% off)
167+
- Set environment variables in Netlify dashboard
168+
- Consider webhook integration for automated approval workflow
169+
- Email service integration for notifications
170+
171+
## Privacy & GDPR
172+
173+
- Emails stored in Cloudinary metadata (not publicly accessible)
174+
- Users can request removal via the interface
175+
- Face blur option provided for privacy
176+
- Consider adding privacy policy link

eslint.config.js

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
/* https://sveltejs.github.io/eslint-plugin-svelte/user-guide/ */
22
import js from '@eslint/js'
3+
import markdown from '@eslint/markdown'
34
import prettier from 'eslint-config-prettier'
45
import svelte from 'eslint-plugin-svelte'
5-
import { globalIgnores } from 'eslint/config'
6+
import { defineConfig, globalIgnores } from 'eslint/config'
67
import globals from 'globals'
78
import ts from 'typescript-eslint'
89
import svelteConfig from './svelte.config.js'
910

10-
export default ts.config(
11+
export default defineConfig(
1112
js.configs.recommended,
1213
...ts.configs.recommended,
13-
...svelte.configs.recommended,
1414
prettier,
1515
{
1616
languageOptions: {
@@ -20,8 +20,20 @@ export default ts.config(
2020
}
2121
}
2222
},
23+
{
24+
files: ['**/*.md'],
25+
extends: [markdown.configs.recommended],
26+
rules: {
27+
'markdown/fenced-code-language': 'off',
28+
'markdown/no-missing-atx-heading-space': 'off', // rule is broken
29+
'markdown/no-missing-label-refs': 'off',
30+
'markdown/require-alt-text': 'warn',
31+
'no-irregular-whitespace': 'off' // not supported by markdown parser
32+
}
33+
},
2334
{
2435
files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
36+
extends: [svelte.configs.recommended],
2537
// See more details at: https://typescript-eslint.io/packages/parser/
2638
languageOptions: {
2739
parserOptions: {
@@ -41,26 +53,47 @@ export default ts.config(
4153
// explicitly specifying it ensures better compatibility and functionality.
4254
svelteConfig
4355
}
56+
},
57+
rules: {
58+
// Override or add rule settings here, such as:
59+
// 'svelte/rule-name': 'error'
60+
61+
// disabled
62+
'svelte/no-navigation-without-resolve': 'off',
63+
'svelte/require-each-key': 'off',
64+
65+
// enabled
66+
'svelte/no-restricted-html-elements': [
67+
'warn',
68+
{
69+
elements: ['a'],
70+
message:
71+
'Use $lib/components/Link.svelte or $lib/components/LinkWithoutIcon.svelte instead'
72+
}
73+
],
74+
'no-restricted-imports': [
75+
'error',
76+
{
77+
patterns: [
78+
{
79+
group: ['**/custom'],
80+
message:
81+
'This component serves as an adapter for mdsvex, import the corresponding component directly instead'
82+
}
83+
]
84+
}
85+
]
4486
}
4587
},
4688
{
4789
rules: {
48-
// Override or add rule settings here, such as:
49-
// 'svelte/rule-name': 'error'
90+
// configured
5091
'@typescript-eslint/no-unused-vars': [
5192
'error',
5293
{
5394
argsIgnorePattern: '^_',
5495
destructuredArrayIgnorePattern: '^_'
5596
}
56-
],
57-
'svelte/require-each-key': 'off',
58-
'svelte/no-restricted-html-elements': [
59-
'warn',
60-
{
61-
elements: ['a'],
62-
message: 'Use $lib/components/custom/a.svelte instead'
63-
}
6497
]
6598
}
6699
},

messages/en.json

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,17 @@
2323
"home_quotes_all": "See all quotes",
2424
"home_quotes_bengio_text": "Rogue AI may be dangerous for the whole of humanity. Banning powerful AI systems (say beyond the abilities of GPT-4) that are given autonomy and agency would be a good start.",
2525
"home_quotes_bengio_title": "AI Turing Award winner",
26+
"home_quotes_asi_statement_text": "We call for a prohibition on the development of superintelligence, not lifted before there is broad scientific consensus that it will be done safely and controllably, and strong public buy-in",
27+
"home_quotes_asi_statement_author": "Statement on Superintelligence",
28+
"home_quotes_asi_statement_title": "110,000+ signatories including AI researchers, political, faith and industry leaders, artists and media celebrities",
2629
"home_quotes_cais_text": "Mitigating the risk of extinction from AI should be a global priority alongside other societal-scale risks such as pandemics and nuclear war.",
2730
"home_quotes_cais_author": "Statement on AI Risk",
2831
"home_quotes_cais_title": "Signed by hundreds of experts, including the top AI labs and scientists",
2932
"home_quotes_hawking_text": "The development of full artificial intelligence could spell the end of the human race.",
3033
"home_quotes_hawking_title": "Theoretical physicist and cosmologist",
3134
"home_quotes_hinton_text": "If you take the existential risk seriously, as I now do, it might be quite sensible to just stop developing these things any further.",
3235
"home_quotes_hinton_title": "Nobel Prize winner & \"Godfather of AI\"",
33-
"home_quotes_russell_text": "If we pursue [our current approach], then we will eventually lose control over the machines",
36+
"home_quotes_russell_text": "If we pursue [our current approach], then we will eventually lose control over the machines.",
3437
"home_quotes_russell_title": "Writer of the AI textbook",
3538
"home_quotes_turing_text": "... we should have to expect the machines to take control.",
3639
"home_quotes_turing_title": "Inventor of the modern computer",
@@ -88,13 +91,9 @@
8891
"footer_other_l10n": "Suggest translation improvement",
8992
"newsletter_heading": "Subscribe to our newsletter",
9093
"newsletter_description": "Stay updated on our efforts to advocate for AI safety.",
91-
"newsletter_disclaimer": "Our newsletter is completely free. No paid subscription necessary. You can close Substack once signed up.",
9294
"newsletter_email_placeholder": "Your email",
9395
"newsletter_button": "Subscribe",
94-
"newsletter_success": "Thank you for subscribing!",
95-
"newsletter_error_default": "Failed to subscribe. Please try again.",
96-
"newsletter_error_network": "Network error. Please try again later.",
97-
"newsletter_loading": "Loading...",
96+
"newsletter_success": "Done. Expect email from Substack.",
9897
"uk_email_mp_title": "Email Your MP",
9998
"uk_email_mp_description": "Contact your Member of Parliament about AI safety",
10099
"uk_email_mp_uk_only_notice": "<b>This tool is for the UK only.</b> For other countries, use the [global email builder](/email-builder).",

0 commit comments

Comments
 (0)