Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9cd4afa
package upgrades
msawired Apr 17, 2026
13efc4e
Initial integration of OpenProcessing API client and adapters
msawired Apr 24, 2026
1b16d0f
Cookie Consent is now sessionStorage based
msawired Apr 24, 2026
ab03cd6
unused cookie-consent endpoint removed
msawired May 1, 2026
b5939bd
OP API support added for simple sketch data
msawired May 1, 2026
5f316f2
Revert "Cookie Consent is now sessionStorage based"
msawired May 1, 2026
1efae78
CookieConsent is now only cookie based.
msawired May 1, 2026
ef9e919
renamed variable "ApiTokenReady" for clarity
msawired May 1, 2026
ec05376
bug: sketches now saved as "html" instead of "p5js"
msawired May 1, 2026
fd55b9b
code preselection now based on latest updatedOn date
msawired May 1, 2026
192554a
sketch listing updated to use X-Total-Count header
msawired May 8, 2026
ff6387f
Debounce sketch list API requests and cancel stale searches.
msawired May 8, 2026
32362e5
S3 and File List management support
msawired May 15, 2026
ba8cac1
added claude folder to gitignore
msawired May 21, 2026
9b10bd4
OP-Authorization and Social Logins Refactored
msawired Jun 3, 2026
d1f39c8
Back editor collections with OpenProcessing curations API
msawired Jun 4, 2026
f64b5ce
Merge pull request #2 from processing/develop
msawired Jun 10, 2026
5471bb0
Merge branch 'develop' into OP-backend/main
msawired Jun 10, 2026
4dd6153
initial pruning
msawired Jun 11, 2026
78c22cb
bug: auth request didn't include private access
msawired Jun 11, 2026
4b661ab
JSZip library added to create zip files on client
msawired Jun 18, 2026
6db86b5
Merge pull request #3 from processing/develop
msawired Jun 18, 2026
cf3c21d
Merge remote-tracking branch 'origin/develop' into OP-backend/main
msawired Jun 18, 2026
ffea67a
Add "Copy Path" option to S3-hosted file dropdowns
msawired Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 8 additions & 16 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
API_URL=/editor
API_URL=https://openprocessing.org/api
API_TOKEN=op_<your-api-token>
AWS_ACCESS_KEY=<your-aws-access-key>
AWS_REGION=<your-aws-region>
AWS_SECRET_KEY=<your-aws-secret-key>
CORS_ALLOW_LOCALHOST=true
EMAIL_SENDER=<transactional-email-sender>
EMAIL_VERIFY_SECRET_TOKEN=whatever_you_want_this_to_be_it_only_matters_for_production
EXAMPLE_USER_EMAIL=examples@p5js.org
EXAMPLE_USER_PASSWORD=hellop5js
GG_EXAMPLES_USERNAME=generativedesign
GG_EXAMPLES_EMAIL=benedikt.gross@generative-gestaltung.de
GG_EXAMPLES_PASS=generativedesign
GITHUB_ID=<your-github-client-id>
GITHUB_SECRET=<your-github-client-secret>
GOOGLE_ID=<your-google-client-id> (use google+ api)
GOOGLE_SECRET=<your-google-client-secret> (use google+ api)
MAILGUN_DOMAIN=<your-mailgun-domain>
MAILGUN_KEY=<your-mailgun-api-key>
ML5_LIBRARY_USERNAME=ml5
ML5_LIBRARY_EMAIL=examples@ml5js.org
ML5_LIBRARY_PASS=helloml5
MONGO_URL=mongodb://localhost:27017/p5js-web-editor
# Optional: public OpenProcessing sketch (visualID) used as the animated
# background on the 404 page. Leave unset for a plain 404 page.
OP_404_SKETCH_ID=
PORT=8000
PREVIEW_PORT=8002
EDITOR_URL=http://localhost:8000
PREVIEW_URL=http://localhost:8002
# Path to the OpenProcessing collection shown as the editor's "Examples" menu
# item, e.g. /username/collections/collectionId. Leave unset to hide Examples.
EXAMPLES_ENDPOINT=
S3_BUCKET=<your-s3-bucket>
S3_BUCKET_URL_BASE=<alt-for-s3-url>
SESSION_SECRET=whatever_you_want_this_to_be_it_only_matters_for_production
TRANSLATIONS_ENABLED=true
UI_ACCESS_TOKEN_ENABLED=false
UPLOAD_LIMIT=250000000
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ duplicates.json

coverage

*.tsbuildinfo
*.tsbuildinfo
/docs
/.claude
6 changes: 6 additions & 0 deletions .verify_boot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
process.env.NODE_ENV='test'; process.env.PORT='0'; process.env.PREVIEW_PORT='0';
process.env.API_URL='https://example.org/api'; process.env.EDITOR_URL='http://localhost:8000';
require('@babel/register')({extensions:['.js','.jsx','.ts','.tsx'],presets:['@babel/preset-env','@babel/preset-typescript']});
require('regenerator-runtime/runtime');
try { require('./server/server.js'); require('./server/previewServer.js'); console.log('OK: both servers boot'); }
catch(e){ console.error('BOOT ERROR:', e.message); process.exit(1); }
2 changes: 1 addition & 1 deletion Procfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
web: MONGO_URL=$MONGO_URI MAILGUN_KEY=$MAILGUN_API_KEY npm run start:prod
web: MAILGUN_KEY=$MAILGUN_API_KEY npm run start:prod
51 changes: 2 additions & 49 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,15 @@
"logo": "https://p5js.org/assets/img/p5js.svg",
"keywords": ["processing", "p5js", "p5.js"],
"addons": [
{
"plan": "mongolab:sandbox",
"as": "MONGO"
},
{
"plan": "mailgun:starter",
"as": "MAILGUN"
}
],
"env": {
"API_URL": {
"value": "/editor"
"description": "Base URL of the OpenProcessing API the editor reads from.",
"value": "https://openprocessing.org/api"
},
"AWS_ACCESS_KEY": {
"description": "AWS Access Key",
Expand All @@ -34,43 +31,6 @@
"description": "The sending email address for transactional emails.",
"value": "no-reply@mydomain.com"
},
"EMAIL_VERIFY_SECRET_TOKEN": {
"description": "A secret key for...? Not sure where used.",
"generator": "secret"
},
"EXAMPLE_USER_EMAIL": {
"description": "The email address for the account holding the default Example sketches",
"value": "examples@p5js.org"
},
"EXAMPLE_USER_PASSWORD": {
"value": "hellop5js"
},
"GG_EXAMPLES_EMAIL": {
"description": "The email address for the account holding the Generative Design Example sketches",
"value": "benedikt.gross@generative-gestaltung.de"
},
"GG_EXAMPLES_PASS": {
"value": "generativedesign"
},
"GG_EXAMPLES_USERNAME": {
"value": "generative-design"
},
"GITHUB_ID": {
"description": "The GitHub Client Id for sign in with GitHub support",
"value": "placeholder"
},
"GITHUB_SECRET": {
"description": "The GitHub Client Secret",
"value": "placeholder"
},
"GOOGLE_ID": {
"description": "The Google Client Id for sign in with Google support",
"value": "placeholder"
},
"GOOGLE_SECRET": {
"description": "The Google Client Secret",
"value": "placeholder"
},
"NODE_ENV": {
"value": "production"
},
Expand All @@ -81,13 +41,6 @@
"S3_BUCKET_URL_BASE": {
"description": "S3 bucket URL base",
"required": false
},
"SESSION_SECRET": {
"description": "A secret key for verifying the integrity of signed cookies.",
"generator": "secret"
}
},
"scripts": {
"postdeploy": "MONGO_URL=$MONGO_URI MAILGUN_KEY=$MAILGUN_API_KEY npm run fetch-examples:prod"
}
}
18 changes: 2 additions & 16 deletions client/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ export const AUTH_USER = 'AUTH_USER';
export const UNAUTH_USER = 'UNAUTH_USER';
export const AUTH_ERROR = 'AUTH_ERROR';

export const SETTINGS_UPDATED = 'SETTINGS_UPDATED';

export const API_KEY_CREATED = 'API_KEY_CREATED';
export const API_KEY_REMOVED = 'API_KEY_REMOVED';

export const SET_PROJECT_NAME = 'SET_PROJECT_NAME';
export const RENAME_PROJECT = 'RENAME_PROJECT';

Expand All @@ -37,6 +32,7 @@ export const SET_PROJECTS_FOR_COLLECTION_LIST =
'SET_PROJECTS_FOR_COLLECTION_LIST';

export const SET_COLLECTIONS = 'SET_COLLECTIONS';
export const SET_COLLECTION = 'SET_COLLECTION';
export const CREATE_COLLECTION = 'CREATED_COLLECTION';
export const DELETE_COLLECTION = 'DELETE_COLLECTION';

Expand Down Expand Up @@ -104,15 +100,6 @@ export const END_SKETCH_REFRESH = 'END_SKETCH_REFRESH';
export const DETECT_INFINITE_LOOPS = 'DETECT_INFINITE_LOOPS';
export const RESET_INFINITE_LOOPS = 'RESET_INFINITE_LOOPS';

export const RESET_PASSWORD_INITIATE = 'RESET_PASSWORD_INITIATE';
export const RESET_PASSWORD_RESET = 'RESET_PASSWORD_RESET';
export const INVALID_RESET_PASSWORD_TOKEN = 'INVALID_RESET_PASSWORD_TOKEN';

export const EMAIL_VERIFICATION_INITIATE = 'EMAIL_VERIFICATION_INITIATE';
export const EMAIL_VERIFICATION_VERIFY = 'EMAIL_VERIFICATION_VERIFY';
export const EMAIL_VERIFICATION_VERIFIED = 'EMAIL_VERIFICATION_VERIFIED';
export const EMAIL_VERIFICATION_INVALID = 'EMAIL_VERIFICATION_INVALID';

// eventually, handle errors more specifically and better
export const ERROR = 'ERROR';

Expand All @@ -121,6 +108,7 @@ export const RESET_JUST_OPENED_PROJECT = 'RESET_JUST_OPENED_PROJECT';

export const SET_PROJECT_SAVED_TIME = 'SET_PROJECT_SAVED_TIME';
export const RESET_PROJECT_SAVED_TIME = 'RESET_PROJECT_SAVED_TIME';
export const SET_SAVED_CODE_TITLES = 'SET_SAVED_CODE_TITLES';
export const SET_PREVIOUS_PATH = 'SET_PREVIOUS_PATH';
export const SHOW_ERROR_MODAL = 'SHOW_ERROR_MODAL';
export const HIDE_ERROR_MODAL = 'HIDE_ERROR_MODAL';
Expand All @@ -140,7 +128,5 @@ export const CLOSE_SKETCHLIST_MODAL = 'CLOSE_SKETCHLIST_MODAL';
export const START_SAVING_PROJECT = 'START_SAVING_PROJECT';
export const END_SAVING_PROJECT = 'END_SAVING_PROJECT';

export const SET_COOKIE_CONSENT = 'SET_COOKIE_CONSENT';

export const CONSOLE_EVENT = 'CONSOLE_EVENT';
export const CLEAR_CONSOLE = 'CLEAR_CONSOLE';
74 changes: 65 additions & 9 deletions client/modules/IDE/actions/assets.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,69 @@
import { apiClient } from '../../../utils/apiClient';
import { opApiClient } from '../../../utils/opApiClient';
import * as ActionTypes from '../../../constants';
import { startLoader, stopLoader } from '../reducers/loading';
import { assetsActions } from '../reducers/assets';
import { showToast } from './toast';

const { setAssets, deleteAsset } = assetsActions;

function encodeFilePath(filePath) {
return filePath.split('/').map(encodeURIComponent).join('/');
}

function getTransactionErrorMessage(error, fallbackMessage) {
const data = error?.response?.data;
return (
data?.message ||
data?.error ||
(typeof data === 'string' ? data : undefined) ||
error?.message ||
fallbackMessage
);
}

function normalizeOpAsset(asset) {
const visualID = asset.visualID == null ? null : String(asset.visualID);
return {
key: `${visualID ?? 'unknown'}:${asset.name}:${asset.url}`,
name: asset.name,
url: asset.url,
size: asset.size,
lastModified: asset.lastModified,
visualID,
sketchId: visualID,
sketchName: asset.visualTitle
};
}

export function getAssets() {
return async (dispatch) => {
return async (dispatch, getState) => {
const { user } = getState();
const userID = user?.id;

// Wait until auth hydration (/whoami) provides the current user's ID.
if (!userID) {
return;
}

dispatch(startLoader());
try {
const response = await apiClient.get('/S3/objects');
const response = await opApiClient.get(`/user/${userID}/files`);
const assets = response.data.map(normalizeOpAsset);

const assetData = {
assets: response.data.assets,
totalSize: response.data.totalSize
assets,
totalSize: assets.reduce((total, asset) => total + asset.size, 0)
};

dispatch(setAssets(assetData));
dispatch(stopLoader());
} catch (error) {
dispatch(
showToast(
getTransactionErrorMessage(error, 'Failed to load assets.'),
5000
)
);
dispatch({
type: ActionTypes.ERROR
});
Expand All @@ -28,14 +73,25 @@ export function getAssets() {
}

export function deleteAssetRequest(assetKey) {
return async (dispatch) => {
return async (dispatch, getState) => {
try {
const path = assetKey.split('/').pop();
await apiClient.delete(
`/S3/delete?objectKey=${encodeURIComponent(path)}`
const asset = getState().assets.list.find(
(item) => item.key === assetKey
);
if (!asset?.visualID) {
throw new Error('Only sketch files can be deleted.');
}
await opApiClient.delete(
`/sketch/${asset.visualID}/files/${encodeFilePath(asset.name)}`
);
dispatch(deleteAsset(assetKey));
} catch (error) {
dispatch(
showToast(
getTransactionErrorMessage(error, 'Failed to delete asset.'),
5000
)
);
dispatch({
type: ActionTypes.ERROR
});
Expand Down
Loading