Skip to content

Commit 707d633

Browse files
authored
Merge pull request #130 from owulveryck/claude-improvement2
Various UI Improvements
2 parents 375c25a + e43778f commit 707d633

File tree

17 files changed

+1084
-301
lines changed

17 files changed

+1084
-301
lines changed

.github/workflows/go.yml

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,30 @@ name: Go
22

33
on:
44
push:
5-
branches: [ main ]
5+
branches: [main]
66
pull_request:
7-
branches: [ main ]
7+
branches: [main]
88

99
jobs:
10-
1110
build:
1211
runs-on: ubuntu-latest
1312
steps:
14-
- uses: actions/checkout@v2
13+
- uses: actions/checkout@v2
1514

16-
- name: Set up Go
17-
uses: actions/setup-go@v2
18-
with:
19-
go-version: 1.22
15+
- name: Set up Go
16+
uses: actions/setup-go@v2
17+
with:
18+
go-version: 1.24.0
2019

21-
- name: Build
22-
run: go build -v ./...
20+
- name: Build
21+
run: go build -v ./...
2322

24-
- name: Test
25-
run: go test -v ./...
23+
- name: Test
24+
run: go test -v ./...
2625

27-
- name: GoGitOps Step
28-
id: gogitops
29-
uses: beaujr/[email protected]
30-
with:
31-
github-actions-user: owulveryck
32-
github-actions-token: ${{secrets.GITHUB_TOKEN}}
26+
- name: GoGitOps Step
27+
id: gogitops
28+
uses: beaujr/[email protected]
29+
with:
30+
github-actions-user: owulveryck
31+
github-actions-token: ${{secrets.GITHUB_TOKEN}}

.github/workflows/goreleaser.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- name: Set up Go
1717
uses: actions/setup-go@v2
1818
with:
19-
go-version: 1.23
19+
go-version: 1.24.0
2020
- name: Run GoReleaser
2121
uses: goreleaser/goreleaser-action@v2
2222
with:

README.md

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ When running on the Remarkable Paper Pro, `RLE_COMPRESSION` environment variable
3838
- **Laser Pointer**: Features a laser pointer that activates on hovering.
3939
- **Gesture Support**: Full integration with Reveal.js, allowing slide switching directly from the reMarkable.
4040
- **Overlay Feature**: Allows overlaying over existing websites that support iframe embedding.
41-
- **Built-in Ngrok**: Enables streaming over different networks easily.
4241
- **Live Parameter Tweaking**: Side menu for live adjustments, including screen rotation.
42+
- **Dark Mode**: Toggle between light and dark themes for comfortable viewing in any environment.
43+
- **Version API**: Check the current version via the `/version` endpoint.
4344

4445
## Quick Start
4546

@@ -173,23 +174,12 @@ Add query parameters to the URL (`?parameter=value&otherparameter=value`):
173174
- `rate`: (integer, 100-...) Set the frame rate.
174175
- `flip`: (true/false) Enable or disable flipping 180 degree.
175176
176-
## Tunneling
177-
Tunneling with built-in Ngrok allows for streaming across different networks.
178-
This feature is particularly useful for remote presentations or collaborative sessions.
179-
To set up tunneling, simply enable Ngrok in the tool's settings and follow the instructions provided in the user interface.
180-
181-
If your reMarkable is on a different network than the displaying device, you can use the `ngrok` builtin feature for automatic tunneling.
182-
To utilize this tunneling, you need to sign up for an ngrok account and [obtain a token from the dashboard](https://dashboard.ngrok.com/get-started/your-authtoken).
183-
Once you have the token, launch reMarkable using the following command:
184-
185-
`NGROK_AUTHTOKEN=YOURTOKEN RK_SERVER_BIND_ADDR=ngrok ./goMarkableStream`
186-
187-
The app will start, displaying a message similar to:
188-
189-
`2023/09/29 16:49:20 listening on 72e5-22-159-32-48.ngrok-free.app`
190-
191-
Then, connect to `https://72e5-22-159-32-48.ngrok-free.app` to view the result.
192-
177+
### API Endpoints
178+
- `/`: Main web interface
179+
- `/stream`: The image data stream
180+
- `/events`: WebSocket endpoint for pen input events
181+
- `/gestures`: Endpoint for touch events
182+
- `/version`: Returns the current version of goMarkableStream
193183
194184
## Presentation Mode
195185
`goMarkableStream` introduces an innovative experimental feature that allows users to set a presentation or video in the background, enabling live annotations using a reMarkable tablet.
@@ -218,9 +208,9 @@ This includes a variety of presentation and video platforms.
218208
Switch slides or navigate through your presentation directly from your reMarkable tablet.
219209
This seamless integration enhances the experience of both presenting and viewing, making it ideal for educational and professional environments.
220210
221-
Howto: add the `?present=https://your-reveal-js-presentation`
211+
How to: add the `?present=https://your-reveal-js-presentation`
222212
223-
_note_: due to browser restrictions, the URL mus
213+
_note_: due to browser restrictions, the URL must be HTTPS.
224214
225215
### Limitations and Performance
226216
@@ -229,12 +219,12 @@ _note_: due to browser restrictions, the URL mus
229219
Users must use the side menu for navigation and control.
230220
- This feature operates seamlessly, with no additional load on the reMarkable tablet, as all rendering is done in the client's browser.
231221
232-
### Feedback and Contributions
222+
### UI Features
233223
234-
- As this is an experimental feature, your feedback is crucial for its development.
235-
Please share your experiences, suggestions, and any issues encountered using the GitHub issues section of this repository.
236-
237-
---
224+
- **Dark Mode**: Toggle between light and dark themes using the sun/moon icon in the sidebar
225+
- **Modern Interface**: Improved UI with better typography and layout
226+
- **Tooltips**: Helpful tooltips on hover for all buttons
227+
- **Feedback Messages**: Visual feedback for user actions
238228
239229
## Technical Details
240230
@@ -246,16 +236,7 @@ This tool suits my need and is an ongoing development. You can find various info
246236
247237
This is a standalone application that runs directly on a Remarkable tablet.
248238
It does not have any dependencies on third-party libraries, making it a completely self-sufficient solution.
249-
This application exposes an HTTP server with two endpoints:
250-
### Endpoints
251-
252-
- `/`: This endpoint serves an embedded HTML and JavaScript file containing the necessary logic to display an image from the Remarkable tablet on a client's web browser.
253-
254-
- `/stream`: This endpoint streams the image data from the Remarkable tablet to the client continuously.
255-
- `/events`: This endpoint streams the pen input events via websockets
256-
- `gestures`: This endpoints streams the touch events in binary
257-
258-
**Caution**: the API may change over time
239+
This application exposes an HTTP server with several endpoints.
259240
260241
### Implementation
261242
@@ -296,5 +277,4 @@ Feel free to modify, distribute, and use the tool in accordance with the terms o
296277
## Tipping
297278
298279
If you plan to buy a reMarkable 2, you can use my [referal program link](https://remarkable.com/referral/PY5B-PH8U).
299-
It will provide a discount for you and also for me.
300-
280+
It will provide a discount for you and also for me.

client/glCanvas.js

Lines changed: 144 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
// WebGL initialization
2-
//const gl = visibleCanvas.getContext('webgl');
3-
//const gl = canvas.getContext('webgl', { antialias: true, preserveDrawingBuffer: true });
4-
let laserX = 0; // Initialize with default values
5-
let laserY = 0;
6-
const gl = canvas.getContext('webgl', { antialias: true });
2+
// Use -10,-10 as the default laser coordinate (off-screen) to hide the pointer initially
3+
let laserX = -10;
4+
let laserY = -10;
5+
const gl = canvas.getContext('webgl', {
6+
antialias: true,
7+
preserveDrawingBuffer: true, // Important for proper rendering
8+
alpha: true // Enable transparency
9+
});
710

811

912
if (!gl) {
@@ -19,30 +22,64 @@ uniform float uScaleFactor;
1922
varying highp vec2 vTextureCoord;
2023
2124
void main(void) {
22-
gl_Position = uRotationMatrix * vec4(aVertexPosition.xy * uScaleFactor, aVertexPosition.zw);
23-
vTextureCoord = aTextureCoord;
25+
// Apply scaling and rotation transformations
26+
gl_Position = uRotationMatrix * vec4(aVertexPosition.xy * uScaleFactor, aVertexPosition.zw);
27+
28+
// Pass texture coordinates to fragment shader
29+
vTextureCoord = aTextureCoord;
2430
}
2531
`;
2632

2733
// Fragment shader program
2834
const fsSource = `
29-
precision mediump float;
35+
precision highp float;
3036
3137
varying highp vec2 vTextureCoord;
3238
uniform sampler2D uSampler;
3339
uniform float uLaserX;
3440
uniform float uLaserY;
41+
uniform bool uDarkMode;
42+
uniform float uContrastLevel;
43+
44+
// Constants for laser pointer visualization
45+
const float LASER_RADIUS = 6.0;
46+
const float LASER_EDGE_SOFTNESS = 2.0;
47+
const vec3 LASER_COLOR = vec3(1.0, 0.0, 0.0);
48+
49+
// Constants for image processing
50+
const float BRIGHTNESS = 0.05; // Slight brightness boost
51+
const float SHARPNESS = 0.5; // Sharpness level
52+
53+
// Get texture color without any sharpening - better for handwriting
54+
vec4 getBaseTexture(sampler2D sampler, vec2 texCoord) {
55+
return texture2D(sampler, texCoord);
56+
}
3557
3658
void main(void) {
59+
// Get base texture color directly - no sharpening for clearer handwriting
60+
vec4 texColor = getBaseTexture(uSampler, vTextureCoord);
61+
62+
// Apply contrast adjustments based on the slider value
63+
vec3 adjusted = (texColor.rgb - 0.5) * uContrastLevel + 0.5;
64+
texColor.rgb = clamp(adjusted, 0.0, 1.0);
65+
66+
// Calculate laser pointer effect
3767
float dx = gl_FragCoord.x - uLaserX;
3868
float dy = gl_FragCoord.y - uLaserY;
3969
float distance = sqrt(dx * dx + dy * dy);
40-
float radius = 5.0; // Radius of the dot, adjust as needed
41-
42-
if(distance < radius) {
43-
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // Red color for the laser dot
70+
71+
if (uDarkMode) {
72+
// Invert colors in dark mode, but preserve alpha
73+
texColor.rgb = 1.0 - texColor.rgb;
74+
}
75+
76+
// Simple laser pointer - more reliable rendering
77+
if (distance < 8.0 && uLaserX > 0.0 && uLaserY > 0.0) {
78+
// Create solid circle with slight fade at edge
79+
float fade = 1.0 - smoothstep(6.0, 8.0, distance);
80+
gl_FragColor = vec4(1.0, 0.0, 0.0, fade); // Red with fade at edge
4481
} else {
45-
gl_FragColor = texture2D(uSampler, vTextureCoord);
82+
gl_FragColor = texColor;
4683
}
4784
}
4885
`;
@@ -107,6 +144,8 @@ const programInfo = {
107144
uSampler: gl.getUniformLocation(shaderProgram, 'uSampler'),
108145
uLaserX: gl.getUniformLocation(shaderProgram, 'uLaserX'),
109146
uLaserY: gl.getUniformLocation(shaderProgram, 'uLaserY'),
147+
uDarkMode: gl.getUniformLocation(shaderProgram, 'uDarkMode'),
148+
uContrastLevel: gl.getUniformLocation(shaderProgram, 'uContrastLevel'),
110149
},
111150
};
112151

@@ -172,18 +211,34 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
172211
let imageData = new ImageData(screenWidth, screenHeight);
173212
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageData);
174213

214+
// Variables to track display state
215+
let isDarkMode = false;
216+
let contrastValue = 1.15; // Default contrast value
217+
175218
// Draw the scene
176219
function drawScene(gl, programInfo, positionBuffer, textureCoordBuffer, texture) {
220+
// Handle canvas resize for proper rendering
177221
if (resizeGLCanvas(gl.canvas)) {
178222
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
179223
}
180-
gl.clearColor(0.5, 0.5, 0.5, 0.25); // Gray with 75% transparency
181-
gl.clearDepth(1.0); // Clear everything
182-
gl.enable(gl.DEPTH_TEST); // Enable depth testing
183-
gl.depthFunc(gl.LEQUAL); // Near things obscure far things
224+
225+
// Adjust background color based on dark mode
226+
const bgColor = isDarkMode
227+
? [0.12, 0.12, 0.13, 0.25] // Darker, more neutral dark mode bg
228+
: [0.98, 0.98, 0.98, 0.25]; // Nearly white light mode bg
229+
gl.clearColor(bgColor[0], bgColor[1], bgColor[2], bgColor[3]);
230+
231+
// Enable alpha blending for transparency
232+
gl.enable(gl.BLEND);
233+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
234+
235+
// Setup depth buffer
236+
gl.clearDepth(1.0);
237+
gl.enable(gl.DEPTH_TEST);
238+
gl.depthFunc(gl.LEQUAL);
184239

185240
// Clear the canvas before we start drawing on it.
186-
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
241+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
187242

188243
// Tell WebGL to use our program when drawing
189244
gl.useProgram(programInfo.program);
@@ -206,9 +261,13 @@ function drawScene(gl, programInfo, positionBuffer, textureCoordBuffer, texture)
206261
// Tell the shader we bound the texture to texture unit 0
207262
gl.uniform1i(programInfo.uniformLocations.uSampler, 0);
208263

209-
// Set the laser coordinates
264+
// Set the laser coordinates
210265
gl.uniform1f(programInfo.uniformLocations.uLaserX, laserX);
211266
gl.uniform1f(programInfo.uniformLocations.uLaserY, laserY);
267+
268+
// Set display flags
269+
gl.uniform1i(programInfo.uniformLocations.uDarkMode, isDarkMode ? 1 : 0);
270+
gl.uniform1f(programInfo.uniformLocations.uContrastLevel, contrastValue);
212271

213272
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
214273
}
@@ -263,9 +322,72 @@ function resizeGLCanvas(canvas) {
263322
return false; // indicates no change in size
264323
}
265324

325+
// Direct laser pointer position - no animation for more reliability
266326
function updateLaserPosition(x, y) {
267-
laserX = x / screenWidth * gl.canvas.width;
268-
laserY = gl.canvas.height - (y / screenHeight * gl.canvas.height);
269-
327+
// If x and y are valid positive values
328+
if (x > 0 && y > 0) {
329+
// Position is now directly proportional to canvas size
330+
laserX = x * (gl.canvas.width / screenWidth);
331+
laserY = gl.canvas.height - (y * (gl.canvas.height / screenHeight));
332+
} else {
333+
// Hide the pointer by moving it off-screen
334+
laserX = -10;
335+
laserY = -10;
336+
}
337+
338+
// Redraw immediately
270339
drawScene(gl, programInfo, positionBuffer, textureCoordBuffer, texture);
271340
}
341+
342+
// Function to update dark mode state with transition effect
343+
let darkModeTransition = 0; // 0 = light mode, 1 = dark mode
344+
let transitionActive = false;
345+
346+
function setDarkMode(darkModeEnabled) {
347+
isDarkMode = darkModeEnabled;
348+
349+
// If not already transitioning, start a smooth transition
350+
if (!transitionActive) {
351+
transitionActive = true;
352+
const startTime = performance.now();
353+
const duration = 300; // transition duration in ms
354+
355+
function animateDarkModeTransition(timestamp) {
356+
const elapsed = timestamp - startTime;
357+
const progress = Math.min(elapsed / duration, 1);
358+
359+
// Update transition value (0 to 1 for light to dark)
360+
darkModeTransition = darkModeEnabled ? progress : 1 - progress;
361+
362+
// Render with current transition value
363+
drawScene(gl, programInfo, positionBuffer, textureCoordBuffer, texture);
364+
365+
// Continue animation if not complete
366+
if (progress < 1) {
367+
requestAnimationFrame(animateDarkModeTransition);
368+
} else {
369+
transitionActive = false;
370+
}
371+
}
372+
373+
requestAnimationFrame(animateDarkModeTransition);
374+
} else {
375+
// Just update the scene if already transitioning
376+
drawScene(gl, programInfo, positionBuffer, textureCoordBuffer, texture);
377+
}
378+
}
379+
380+
// Function to set contrast level
381+
function setContrast(contrastLevel) {
382+
// Store the contrast value (between 1.0 and 3.0)
383+
contrastValue = parseFloat(contrastLevel);
384+
385+
// If the value is valid, update rendering
386+
if (!isNaN(contrastValue) && contrastValue >= 1.0 && contrastValue <= 3.0) {
387+
// Update the scene with new contrast
388+
drawScene(gl, programInfo, positionBuffer, textureCoordBuffer, texture);
389+
390+
// Save user preference to localStorage
391+
localStorage.setItem('contrastLevel', contrastValue);
392+
}
393+
}

0 commit comments

Comments
 (0)