@edadma/logo 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,29 +1,22 @@
1
1
  {
2
2
  "name": "@edadma/logo",
3
- "version": "0.2.3",
4
- "description": "Logo programming language interpreter with React component",
3
+ "version": "0.2.5",
4
+ "description": "Logo programming language interpreter",
5
5
  "main": "dist/main.js",
6
6
  "module": "dist/main.js",
7
7
  "types": "src/index.d.ts",
8
8
  "files": [
9
9
  "dist",
10
- "src",
11
- "react"
10
+ "src"
12
11
  ],
13
12
  "exports": {
14
13
  ".": {
15
14
  "import": "./dist/main.js",
16
15
  "types": "./src/index.d.ts"
17
- },
18
- "./react": {
19
- "import": "./react/index.ts",
20
- "types": "./react/index.ts"
21
16
  }
22
17
  },
23
18
  "scripts": {
24
- "build": "npm run build:scala && npm run build:react",
25
- "build:scala": "cd .. && sbt logoJS/fullLinkJS && cp js/target/scala-3.7.4/logo-opt/main.js js/target/scala-3.7.4/logo-opt/main.js.map js/dist/",
26
- "build:react": "tsc --project tsconfig.react.json",
19
+ "build": "cd .. && sbt logoJS/fullLinkJS && cp js/target/scala-3.7.4/logo-opt/main.js js/target/scala-3.7.4/logo-opt/main.js.map js/dist/",
27
20
  "prepublishOnly": "npm run build"
28
21
  },
29
22
  "keywords": [
@@ -32,7 +25,6 @@
32
25
  "turtle",
33
26
  "graphics",
34
27
  "education",
35
- "react",
36
28
  "canvas"
37
29
  ],
38
30
  "author": "Edward A. Maxedon, Sr.",
@@ -44,17 +36,5 @@
44
36
  "bugs": {
45
37
  "url": "https://github.com/edadma/logo/issues"
46
38
  },
47
- "homepage": "https://github.com/edadma/logo#readme",
48
- "peerDependencies": {
49
- "react": ">=17.0.0"
50
- },
51
- "peerDependenciesMeta": {
52
- "react": {
53
- "optional": true
54
- }
55
- },
56
- "devDependencies": {
57
- "@types/react": "^18.2.0",
58
- "typescript": "^5.0.0"
59
- }
39
+ "homepage": "https://github.com/edadma/logo#readme"
60
40
  }
@@ -57,6 +57,7 @@ class LogoJS(canvas: html.Canvas) extends js.Object:
57
57
  private var autoRender: Boolean = true
58
58
  private var initialized: Boolean = false
59
59
  private var backgroundColor: String = "white"
60
+ private var foregroundColor: (Int, Int, Int) = (0, 0, 0) // RGB for theme-aware drawing
60
61
  private var eventHandler: Option[js.Function0[Unit]] = None
61
62
 
62
63
  private val logo = new Logo:
@@ -100,9 +101,10 @@ class LogoJS(canvas: html.Canvas) extends js.Object:
100
101
  backgroundColor = color
101
102
  render()
102
103
 
103
- /** Set the default pen color (used after clear) */
104
+ /** Set the default pen color (used after clear and for theme-aware drawing) */
104
105
  def setForegroundColor(color: String): Unit =
105
106
  val rgb = parseColor(color)
107
+ foregroundColor = rgb
106
108
  logo.setDefaultColor(rgb)
107
109
  render()
108
110
 
@@ -172,21 +174,33 @@ class LogoJS(canvas: html.Canvas) extends js.Object:
172
174
  val lines = js.Array[LineData]()
173
175
  val labels = js.Array[LabelData]()
174
176
  val arcs = js.Array[ArcData]()
177
+ var currentColor: (Int, Int, Int) = foregroundColor
178
+ var currentWidth: Double = 1.0
175
179
 
176
180
  logo.drawing.foreach {
177
- case DrawLine(x1, y1, x2, y2, (r, g, b), width) =>
181
+ case DrawSetColor(colorOpt) =>
182
+ currentColor = colorOpt.getOrElse(foregroundColor)
183
+
184
+ case DrawSetWidth(width) =>
185
+ currentWidth = width
186
+
187
+ case DrawLine(x1, y1, x2, y2) =>
188
+ val (r, g, b) = currentColor
178
189
  lines.push(js.Dynamic.literal(
179
190
  x1 = x1, y1 = y1, x2 = x2, y2 = y2,
180
- color = s"rgb($r,$g,$b)", width = width
191
+ color = s"rgb($r,$g,$b)", width = currentWidth
181
192
  ).asInstanceOf[LineData])
193
+
182
194
  case DrawLabel(x, y, heading, text) =>
183
195
  labels.push(js.Dynamic.literal(
184
196
  x = x, y = y, heading = heading, text = text
185
197
  ).asInstanceOf[LabelData])
186
- case DrawArc(x, y, heading, angle, radius, (r, g, b), width) =>
198
+
199
+ case DrawArc(x, y, heading, angle, radius) =>
200
+ val (r, g, b) = currentColor
187
201
  arcs.push(js.Dynamic.literal(
188
202
  x = x, y = y, heading = heading, angle = angle, radius = radius,
189
- color = s"rgb($r,$g,$b)", width = width
203
+ color = s"rgb($r,$g,$b)", width = currentWidth
190
204
  ).asInstanceOf[ArcData])
191
205
  }
192
206
 
@@ -203,6 +217,8 @@ class LogoJS(canvas: html.Canvas) extends js.Object:
203
217
  private def renderWithPaths(): Unit =
204
218
  case class Style(color: (Int, Int, Int), width: Double)
205
219
 
220
+ var currentColor: (Int, Int, Int) = foregroundColor
221
+ var currentWidth: Double = 1.0
206
222
  var currentStyle: Option[Style] = None
207
223
  var pathStarted = false
208
224
  var lastX: Double = 0
@@ -220,8 +236,14 @@ class LogoJS(canvas: html.Canvas) extends js.Object:
220
236
  currentStyle = None
221
237
 
222
238
  logo.drawing.foreach {
223
- case DrawLine(x1, y1, x2, y2, color, width) =>
224
- val style = Style(color, width)
239
+ case DrawSetColor(colorOpt) =>
240
+ currentColor = colorOpt.getOrElse(foregroundColor)
241
+
242
+ case DrawSetWidth(width) =>
243
+ currentWidth = width
244
+
245
+ case DrawLine(x1, y1, x2, y2) =>
246
+ val style = Style(currentColor, currentWidth)
225
247
 
226
248
  if !currentStyle.contains(style) then
227
249
  flushPath()
@@ -243,9 +265,9 @@ class LogoJS(canvas: html.Canvas) extends js.Object:
243
265
  lastX = x2
244
266
  lastY = y2
245
267
 
246
- case DrawArc(x, y, heading, angleDeg, radius, color, width) =>
268
+ case DrawArc(x, y, heading, angleDeg, radius) =>
247
269
  flushPath()
248
- renderArc(x, y, heading, angleDeg, radius, color, width)
270
+ renderArc(x, y, heading, angleDeg, radius, currentColor, currentWidth)
249
271
 
250
272
  case DrawLabel(x, y, heading, text) =>
251
273
  flushPath()
@@ -255,17 +277,27 @@ class LogoJS(canvas: html.Canvas) extends js.Object:
255
277
  flushPath()
256
278
 
257
279
  private def renderWithLines(): Unit =
280
+ var currentColor: (Int, Int, Int) = foregroundColor
281
+ var currentWidth: Double = 1.0
282
+
258
283
  logo.drawing.foreach {
259
- case DrawLine(x1, y1, x2, y2, (r, g, b), width) =>
284
+ case DrawSetColor(colorOpt) =>
285
+ currentColor = colorOpt.getOrElse(foregroundColor)
286
+
287
+ case DrawSetWidth(width) =>
288
+ currentWidth = width
289
+
290
+ case DrawLine(x1, y1, x2, y2) =>
291
+ val (r, g, b) = currentColor
260
292
  ctx.strokeStyle = s"rgb($r,$g,$b)"
261
- ctx.lineWidth = width
293
+ ctx.lineWidth = currentWidth
262
294
  ctx.beginPath()
263
295
  ctx.moveTo(x1, y1)
264
296
  ctx.lineTo(x2, y2)
265
297
  ctx.stroke()
266
298
 
267
- case DrawArc(x, y, heading, angleDeg, radius, (r, g, b), width) =>
268
- renderArc(x, y, heading, angleDeg, radius, (r, g, b), width)
299
+ case DrawArc(x, y, heading, angleDeg, radius) =>
300
+ renderArc(x, y, heading, angleDeg, radius, currentColor, currentWidth)
269
301
 
270
302
  case DrawLabel(x, y, heading, text) =>
271
303
  renderLabel(x, y, heading, text)
@@ -312,22 +344,72 @@ class LogoJS(canvas: html.Canvas) extends js.Object:
312
344
  ctx.stroke()
313
345
 
314
346
  private def drawTurtle(x: Double, y: Double, heading: Double): Unit =
315
- val w = 15.0
316
- val h = 20.0
317
-
318
347
  ctx.save()
319
348
  ctx.translate(x, y)
320
- ctx.rotate(heading - Pi / 2)
349
+ ctx.rotate(heading + Pi / 2)
321
350
 
351
+ // Tail (behind shell)
322
352
  ctx.beginPath()
323
- ctx.moveTo(0, 0)
324
- ctx.lineTo(-w / 2, h / 2)
325
- ctx.lineTo(0, h)
326
- ctx.lineTo(w / 2, h / 2)
327
- ctx.closePath()
328
-
329
- ctx.strokeStyle = "green"
353
+ ctx.moveTo(0, 10)
354
+ ctx.lineTo(0, 14)
355
+ ctx.strokeStyle = "#4a7a44"
330
356
  ctx.lineWidth = 2
357
+ ctx.lineCap = "round"
358
+ ctx.stroke()
359
+
360
+ // Legs (behind shell)
361
+ ctx.fillStyle = "#4a7a44"
362
+ ctx.strokeStyle = "#1a3a18"
363
+ ctx.lineWidth = 1
364
+ // Front legs
365
+ ctx.beginPath()
366
+ ctx.ellipse(-7, -6, 3, 5, 0.4, 0, 2 * Pi)
367
+ ctx.fill()
368
+ ctx.stroke()
369
+ ctx.beginPath()
370
+ ctx.ellipse(7, -6, 3, 5, -0.4, 0, 2 * Pi)
371
+ ctx.fill()
372
+ ctx.stroke()
373
+ // Back legs
374
+ ctx.beginPath()
375
+ ctx.ellipse(-6, 6, 3, 4, 0.3, 0, 2 * Pi)
376
+ ctx.fill()
377
+ ctx.stroke()
378
+ ctx.beginPath()
379
+ ctx.ellipse(6, 6, 3, 4, -0.3, 0, 2 * Pi)
380
+ ctx.fill()
381
+ ctx.stroke()
382
+
383
+ // Head (behind shell)
384
+ ctx.beginPath()
385
+ ctx.ellipse(0, -14, 4, 5, 0, 0, 2 * Pi)
386
+ ctx.fillStyle = "#4a7a44"
387
+ ctx.fill()
388
+ ctx.strokeStyle = "#1a3a18"
389
+ ctx.lineWidth = 1.5
390
+ ctx.stroke()
391
+
392
+ // Eyes
393
+ ctx.fillStyle = "black"
394
+ ctx.beginPath()
395
+ ctx.arc(-1.5, -15, 1, 0, 2 * Pi)
396
+ ctx.arc(1.5, -15, 1, 0, 2 * Pi)
397
+ ctx.fill()
398
+
399
+ // Shell (on top)
400
+ ctx.beginPath()
401
+ ctx.ellipse(0, 0, 8, 10, 0, 0, 2 * Pi)
402
+ ctx.fillStyle = "#2d5a27"
403
+ ctx.fill()
404
+ ctx.strokeStyle = "#1a3a18"
405
+ ctx.lineWidth = 1.5
406
+ ctx.stroke()
407
+
408
+ // Shell pattern
409
+ ctx.strokeStyle = "#3d7a37"
410
+ ctx.lineWidth = 1
411
+ ctx.beginPath()
412
+ ctx.ellipse(0, 0, 5, 6, 0, 0, 2 * Pi)
331
413
  ctx.stroke()
332
414
 
333
415
  ctx.restore()
@@ -1,74 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useRef, useEffect, useImperativeHandle, forwardRef } from 'react';
3
- export const LogoCanvas = forwardRef(({ width = 600, height = 400, program, pathRendering = true, onError, onComplete, className, style, }, ref) => {
4
- const canvasRef = useRef(null);
5
- const logoRef = useRef(null);
6
- // Initialize Logo instance
7
- useEffect(() => {
8
- if (canvasRef.current && typeof Logo !== 'undefined') {
9
- logoRef.current = new Logo(canvasRef.current);
10
- logoRef.current.setPathRendering(pathRendering);
11
- }
12
- }, []);
13
- // Update path rendering setting
14
- useEffect(() => {
15
- if (logoRef.current) {
16
- logoRef.current.setPathRendering(pathRendering);
17
- }
18
- }, [pathRendering]);
19
- // Run program when it changes
20
- useEffect(() => {
21
- if (logoRef.current && program !== undefined) {
22
- try {
23
- logoRef.current.clear();
24
- logoRef.current.run(program);
25
- onComplete?.();
26
- }
27
- catch (e) {
28
- onError?.(e instanceof Error ? e : new Error(String(e)));
29
- }
30
- }
31
- }, [program, onError, onComplete]);
32
- // Expose methods via ref
33
- useImperativeHandle(ref, () => ({
34
- execute: (command) => {
35
- if (logoRef.current) {
36
- try {
37
- logoRef.current.execute(command);
38
- }
39
- catch (e) {
40
- onError?.(e instanceof Error ? e : new Error(String(e)));
41
- }
42
- }
43
- },
44
- run: (prog) => {
45
- if (logoRef.current) {
46
- try {
47
- logoRef.current.run(prog);
48
- onComplete?.();
49
- }
50
- catch (e) {
51
- onError?.(e instanceof Error ? e : new Error(String(e)));
52
- }
53
- }
54
- },
55
- clear: () => {
56
- logoRef.current?.clear();
57
- },
58
- getDrawing: () => {
59
- return logoRef.current?.getDrawing() ?? { lines: [], labels: [] };
60
- },
61
- getTurtle: () => {
62
- return (logoRef.current?.getTurtle() ?? {
63
- x: 0,
64
- y: 0,
65
- heading: Math.PI / 2,
66
- visible: true,
67
- });
68
- },
69
- getLogo: () => logoRef.current,
70
- }));
71
- return (_jsx("canvas", { ref: canvasRef, width: width, height: height, className: className, style: { backgroundColor: 'white', ...style } }));
72
- });
73
- LogoCanvas.displayName = 'LogoCanvas';
74
- export default LogoCanvas;
@@ -1,2 +0,0 @@
1
- export { LogoCanvas } from './LogoCanvas';
2
- export { LogoCanvas as default } from './LogoCanvas';
@@ -1,174 +0,0 @@
1
- import React, { useRef, useEffect, useImperativeHandle, forwardRef } from 'react';
2
-
3
- // Type definitions for the Logo class from Scala.js
4
- interface LogoDrawing {
5
- lines: Array<{
6
- x1: number;
7
- y1: number;
8
- x2: number;
9
- y2: number;
10
- color: string;
11
- width: number;
12
- }>;
13
- labels: Array<{
14
- x: number;
15
- y: number;
16
- heading: number;
17
- text: string;
18
- }>;
19
- }
20
-
21
- interface TurtleState {
22
- x: number;
23
- y: number;
24
- heading: number;
25
- visible: boolean;
26
- }
27
-
28
- interface LogoInstance {
29
- run(program: string): void;
30
- execute(command: string): void;
31
- clear(): void;
32
- render(): void;
33
- setPathRendering(enabled: boolean): void;
34
- setAutoRender(enabled: boolean): void;
35
- getDrawing(): LogoDrawing;
36
- getTurtle(): TurtleState;
37
- }
38
-
39
- declare const Logo: new (canvas: HTMLCanvasElement) => LogoInstance;
40
-
41
- export interface LogoCanvasProps {
42
- /** Width of the canvas in pixels */
43
- width?: number;
44
- /** Height of the canvas in pixels */
45
- height?: number;
46
- /** Logo program to run */
47
- program?: string;
48
- /** Whether to use path-based rendering (smoother curves) */
49
- pathRendering?: boolean;
50
- /** Callback when an error occurs */
51
- onError?: (error: Error) => void;
52
- /** Callback when program execution completes */
53
- onComplete?: () => void;
54
- /** Additional CSS class for the canvas */
55
- className?: string;
56
- /** Additional inline styles for the canvas */
57
- style?: React.CSSProperties;
58
- }
59
-
60
- export interface LogoCanvasRef {
61
- /** Execute a Logo command */
62
- execute: (command: string) => void;
63
- /** Run a full Logo program */
64
- run: (program: string) => void;
65
- /** Clear the canvas and reset turtle */
66
- clear: () => void;
67
- /** Get the current drawing data */
68
- getDrawing: () => LogoDrawing;
69
- /** Get the current turtle state */
70
- getTurtle: () => TurtleState;
71
- /** Get the underlying Logo instance */
72
- getLogo: () => LogoInstance | null;
73
- }
74
-
75
- export const LogoCanvas = forwardRef<LogoCanvasRef, LogoCanvasProps>(
76
- (
77
- {
78
- width = 600,
79
- height = 400,
80
- program,
81
- pathRendering = true,
82
- onError,
83
- onComplete,
84
- className,
85
- style,
86
- },
87
- ref
88
- ) => {
89
- const canvasRef = useRef<HTMLCanvasElement>(null);
90
- const logoRef = useRef<LogoInstance | null>(null);
91
-
92
- // Initialize Logo instance
93
- useEffect(() => {
94
- if (canvasRef.current && typeof Logo !== 'undefined') {
95
- logoRef.current = new Logo(canvasRef.current);
96
- logoRef.current.setPathRendering(pathRendering);
97
- }
98
- }, []);
99
-
100
- // Update path rendering setting
101
- useEffect(() => {
102
- if (logoRef.current) {
103
- logoRef.current.setPathRendering(pathRendering);
104
- }
105
- }, [pathRendering]);
106
-
107
- // Run program when it changes
108
- useEffect(() => {
109
- if (logoRef.current && program !== undefined) {
110
- try {
111
- logoRef.current.clear();
112
- logoRef.current.run(program);
113
- onComplete?.();
114
- } catch (e) {
115
- onError?.(e instanceof Error ? e : new Error(String(e)));
116
- }
117
- }
118
- }, [program, onError, onComplete]);
119
-
120
- // Expose methods via ref
121
- useImperativeHandle(ref, () => ({
122
- execute: (command: string) => {
123
- if (logoRef.current) {
124
- try {
125
- logoRef.current.execute(command);
126
- } catch (e) {
127
- onError?.(e instanceof Error ? e : new Error(String(e)));
128
- }
129
- }
130
- },
131
- run: (prog: string) => {
132
- if (logoRef.current) {
133
- try {
134
- logoRef.current.run(prog);
135
- onComplete?.();
136
- } catch (e) {
137
- onError?.(e instanceof Error ? e : new Error(String(e)));
138
- }
139
- }
140
- },
141
- clear: () => {
142
- logoRef.current?.clear();
143
- },
144
- getDrawing: () => {
145
- return logoRef.current?.getDrawing() ?? { lines: [], labels: [] };
146
- },
147
- getTurtle: () => {
148
- return (
149
- logoRef.current?.getTurtle() ?? {
150
- x: 0,
151
- y: 0,
152
- heading: Math.PI / 2,
153
- visible: true,
154
- }
155
- );
156
- },
157
- getLogo: () => logoRef.current,
158
- }));
159
-
160
- return (
161
- <canvas
162
- ref={canvasRef}
163
- width={width}
164
- height={height}
165
- className={className}
166
- style={{ backgroundColor: 'white', ...style }}
167
- />
168
- );
169
- }
170
- );
171
-
172
- LogoCanvas.displayName = 'LogoCanvas';
173
-
174
- export default LogoCanvas;
package/react/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export { LogoCanvas, type LogoCanvasProps, type LogoCanvasRef } from './LogoCanvas';
2
- export { LogoCanvas as default } from './LogoCanvas';