Skip to content

Commit 4bb3dc6

Browse files
committed
refac
An updated and more comprehensive section on using custom animation scripts, including a guide for converting standard JavaScript animations to be compatible with the Open WebUI theme engine.
1 parent e8bd071 commit 4bb3dc6

File tree

1 file changed

+135
-20
lines changed

1 file changed

+135
-20
lines changed

docs/tutorials/theming.mdx

Lines changed: 135 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ Here are the available toggles:
125125

126126
### CodeMirror Themes
127127

128-
You can customize the appearance of the code editor in Open WebUI by specifying a CodeMirror theme in the `codeMirrorTheme` property of your `theme.json`. A wide variety of themes are available to choose from.
128+
You can customize the appearance of the code editor in Open WebUI by specifying a `codeMirrorTheme` property of your `theme.json`. A wide variety of themes are available to choose from.
129129

130130
Here is a list of the available CodeMirror themes:
131131

@@ -288,37 +288,152 @@ Let's break down the `starfield` example:
288288

289289
#### Using a Custom Animation Script
290290

291-
For complete control over your theme's animation, you can provide a JavaScript script in the `animationScript` property. This script runs in a separate Web Worker to ensure the main application remains responsive.
291+
This section provides a step-by-step guide on how to convert a standard JavaScript canvas animation into a format that is compatible with the Open WebUI theme system.
292292

293-
**The Web Worker Environment**
293+
##### Understanding the Open WebUI Theme Engine
294294

295-
Because the script runs in a Web Worker, it has a few unique characteristics:
296-
- It does not have access to the `window` or `document` objects.
297-
- The global scope is `self`.
298-
- It communicates with the main application via messages.
295+
The Open WebUI theme engine runs custom animation scripts in a **Web Worker**. This is a crucial point to understand, as it imposes several restrictions on the code you can write.
299296

300-
**Receiving Messages**
297+
**Key characteristics of the Web Worker environment:**
301298

302-
Your script should include an `onmessage` handler to receive data and events from the main application.
299+
* **No DOM Access:** You do not have access to the `document` or `window` objects. This means you cannot create or manipulate DOM elements (like `<canvas>`) directly from the animation script.
300+
* **Communication via Messaging:** The only way to communicate between the main application and the animation script (the worker) is through the `self.onmessage` and `postMessage` APIs.
301+
* **Canvas is an `OffscreenCanvas`:** The main application will create a `<canvas>` element and transfer it to the worker as an `OffscreenCanvas`. You will receive this canvas object in the `init` message.
302+
303+
##### The Conversion Process
304+
305+
Here is a step-by-step guide to converting a typical JavaScript canvas animation.
306+
307+
###### 1. Identify and Isolate the Core Animation Logic
308+
309+
Most canvas animations have the following components:
310+
311+
* **Setup/Initialization Code:** This code runs once at the beginning to set up the canvas, create initial objects, etc. In a typical script, this might be at the top level of the script.
312+
* **Animation Loop:** This is a function that is called repeatedly to draw each frame of the animation. It usually uses `requestAnimationFrame` or `setTimeout`.
313+
* **Helper Functions:** These are utility functions for things like math, color manipulation, etc.
314+
* **Object Definitions:** These are classes or constructor functions for the objects in the animation (e.g., `Particle`).
315+
* **Event Listeners:** These are functions that respond to user input, like `mousemove` or `resize`.
316+
317+
Your goal is to separate these components and adapt them to the worker environment.
318+
319+
###### 2. Adapt the Code for the Worker Environment
320+
321+
Here is a template for a converted animation script. You will need to move the code from the original script into the appropriate sections of this template.
303322

304323
```javascript
305-
self.onmessage = (e) => {
324+
// 1. Paste all helper functions and object definitions here.
325+
// (e.g., randomIntFromRange, Particle, etc.)
326+
327+
// 2. Define global variables for the worker script.
328+
let canvas;
329+
let ctx;
330+
let w, h;
331+
// ... any other global variables your animation needs
332+
333+
// 3. The main message handler for the worker.
334+
self.onmessage = function(e) {
306335
if (e.data.type === 'init') {
307-
// Initialization logic
336+
// This is where the setup/initialization code goes.
337+
canvas = e.data.canvas;
338+
ctx = canvas.getContext('2d');
339+
w = canvas.width = e.data.width;
340+
h = canvas.height = e.data.height;
341+
342+
// Call your initialization function here.
343+
init();
344+
// Start the animation loop.
345+
animate();
346+
308347
} else if (e.data.type === 'resize') {
309-
// Resize logic
348+
// This is where the resize handling code goes.
349+
w = canvas.width = e.data.width;
350+
h = canvas.height = e.data.height;
351+
// You might need to re-initialize your animation on resize.
352+
init();
353+
310354
} else if (e.data.type === 'mousemove') {
311-
// Mouse move logic
355+
// This is where the mouse move handling code goes.
356+
// Update your mouse coordinates object here.
357+
mouse.x = e.data.x;
358+
mouse.y = e.data.y;
312359
}
313360
};
361+
362+
// 4. Your initialization function.
363+
function init() {
364+
// ... your init code here ...
365+
}
366+
367+
// 5. Your animation loop.
368+
function animate() {
369+
// ... your drawing code here ...
370+
requestAnimationFrame(animate);
371+
}
372+
```
373+
374+
###### 3. Conversion Checklist
375+
376+
Here is a checklist of common things to look for and change in the original script:
377+
378+
* **`document.querySelector('canvas')` or `document.getElementById('canvas')`:** Remove these lines. The canvas is provided to you.
379+
* **`canvas.getContext('2d')`:** Move this line inside the `init` message handler.
380+
* **`canvas.width = window.innerWidth` and `canvas.height = window.innerHeight`:** Remove these lines from the top level of the script. The canvas dimensions are provided in the `init` and `resize` messages.
381+
* **`addEventListener('mousemove', ...)` and `addEventListener('resize', ...)`:** Remove these event listeners. This functionality is now handled by the `self.onmessage` handler.
382+
* **`requestAnimationFrame(animate)` or `setTimeout(animate, ...)`:** Make sure the animation loop is started by calling `animate()` once from within the `init` message handler. The loop itself should use `requestAnimationFrame(animate)`.
383+
* **Global variables:** Make sure any global variables from the original script are defined at the top level of your worker script (outside the `self.onmessage` handler).
384+
385+
###### Example: Before and After
386+
387+
Here is an example of a simple animation before and after conversion.
388+
389+
**Before:**
390+
```javascript
391+
const canvas = document.querySelector('canvas');
392+
const ctx = canvas.getContext('2d');
393+
canvas.width = window.innerWidth;
394+
canvas.height = window.innerHeight;
395+
396+
let x = 0;
397+
398+
function animate() {
399+
ctx.clearRect(0, 0, canvas.width, canvas.height);
400+
ctx.fillRect(x, 100, 50, 50);
401+
x += 1;
402+
requestAnimationFrame(animate);
403+
}
404+
405+
animate();
406+
```
407+
408+
**After:**
409+
```javascript
410+
let canvas;
411+
let ctx;
412+
let w, h;
413+
let x = 0;
414+
415+
self.onmessage = function(e) {
416+
if (e.data.type === 'init') {
417+
canvas = e.data.canvas;
418+
ctx = canvas.getContext('2d');
419+
w = canvas.width = e.data.width;
420+
h = canvas.height = e.data.height;
421+
animate();
422+
}
423+
};
424+
425+
function animate() {
426+
ctx.clearRect(0, 0, w, h);
427+
ctx.fillRect(x, 100, 50, 50);
428+
x += 1;
429+
if (x > w) {
430+
x = 0;
431+
}
432+
requestAnimationFrame(animate);
433+
}
314434
```
315435

316-
- **`init` message**: This is the first message your script will receive. The `e.data` object will contain:
317-
- `canvas`: An `OffscreenCanvas` object that you can draw on.
318-
- `width`: The initial width of the canvas.
319-
- `height`: The initial height of the canvas.
320-
- **`resize` message**: This message is sent whenever the application window is resized. The `e.data` object will contain the new `width` and `height`. You should use this to update your animation's dimensions.
321-
- **`mousemove` message**: This message is sent when the user moves their mouse over the application. The `e.data` object will contain the `x` and `y` coordinates of the mouse relative to the canvas.
436+
By following these instructions, you should be able to convert most JavaScript canvas animations to work with the Open WebUI theme system.
322437

323438
### Example 4: The `Matrix Rain` theme
324439

@@ -360,4 +475,4 @@ This theme adds a background image to the chat and the overall system.
360475

361476
### Sharing Your Theme
362477

363-
To make your theme easily shareable and updatable, host the `theme.json` file somewhere with a raw file link (like GitHub Gist or a public repository) and set the `sourceUrl` property to that link. This allows others to install your theme with one click and receive updates automatically.
478+
To make your theme easily shareable and updatable, host the `theme.json` file somewhere with a raw file link (like GitHub Gist or a public repository) and set the `sourceUrl` property to that link. This allows others to install your theme with one click and receive updates automatically.

0 commit comments

Comments
 (0)