swisseph-wasm 0.0.2 → 0.0.4

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.
@@ -0,0 +1,434 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Swiss Ephemeris Playground</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
9
+ </head>
10
+ <body class="bg-gray-50 min-h-screen">
11
+ <div id="app">
12
+ <!-- Header -->
13
+ <header class="bg-white border-b border-gray-200">
14
+ <div class="container mx-auto px-4 py-6">
15
+ <div class="flex items-center justify-between">
16
+ <div>
17
+ <h1 class="text-2xl font-bold text-gray-900">
18
+ Swiss Ephemeris Playground
19
+ </h1>
20
+ <p class="text-gray-600 mt-1">Write and execute custom astronomical programs</p>
21
+ </div>
22
+ <div class="text-right">
23
+ <div class="flex gap-3 mb-2">
24
+ <a href="api-explorer.html" class="text-sm text-blue-600 hover:text-blue-800">API Explorer</a>
25
+ <a href="tests.html" class="text-sm text-blue-600 hover:text-blue-800">Tests</a>
26
+ </div>
27
+ <div class="text-sm" :class="status.color">{{ status.text }}</div>
28
+ <div class="text-xs text-gray-400 mt-1">{{ version }}</div>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ </header>
33
+
34
+ <div class="container mx-auto px-4 py-6">
35
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
36
+ <!-- Left: Code Editor -->
37
+ <div class="space-y-4">
38
+ <div class="bg-white rounded border border-gray-200 p-4">
39
+ <div class="flex items-center justify-between mb-3">
40
+ <h2 class="text-lg font-semibold text-gray-900">Code Editor</h2>
41
+ <button @click="runCode"
42
+ class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 transition-all font-medium text-sm">
43
+ Run Code
44
+ </button>
45
+ </div>
46
+
47
+ <textarea v-model="code"
48
+ class="w-full h-[500px] bg-gray-50 border border-gray-300 rounded p-3 font-mono text-sm text-gray-900 focus:outline-none focus:border-blue-500 resize-none"
49
+ placeholder="Write your code here..."></textarea>
50
+
51
+ <div class="mt-2 text-xs text-gray-500">
52
+ Tip: Use <code class="bg-gray-100 px-2 py-0.5 rounded">swisseph</code> to access all methods
53
+ </div>
54
+ </div>
55
+
56
+ <!-- Quick Reference -->
57
+ <div class="bg-white rounded border border-gray-200 p-3">
58
+ <h3 class="text-sm font-semibold text-gray-900 mb-2">Quick Reference</h3>
59
+ <div class="space-y-1 text-xs">
60
+ <div><code class="text-blue-600">swisseph.julday(2000, 1, 1, 12.0)</code> - Convert date to JD</div>
61
+ <div><code class="text-blue-600">swisseph.calc(jd, swisseph.SE_SUN, ...)</code> - Planet position</div>
62
+ <div><code class="text-blue-600">swisseph.houses(jd, lat, lon, 'P')</code> - House cusps</div>
63
+ </div>
64
+ </div>
65
+ </div>
66
+
67
+ <!-- Right: Templates + Output + Saved -->
68
+ <div class="space-y-4">
69
+ <!-- Program Templates - Compact Batch -->
70
+ <div class="bg-white rounded border border-gray-200 p-3">
71
+ <h2 class="text-sm font-semibold text-gray-900 mb-2">Program Templates</h2>
72
+ <div class="grid grid-cols-3 gap-2">
73
+ <button v-for="template in templates" :key="template.name"
74
+ @click="loadTemplate(template)"
75
+ class="p-2 bg-gray-50 rounded border border-gray-200 hover:border-blue-400 hover:bg-blue-50 transition-all text-center">
76
+ <div class="font-medium text-gray-900 text-xs">{{ template.name }}</div>
77
+ </button>
78
+ </div>
79
+ </div>
80
+
81
+ <!-- Output -->
82
+ <div class="bg-white rounded border border-gray-200 p-4">
83
+ <div class="flex items-center justify-between mb-3">
84
+ <h2 class="text-lg font-semibold text-gray-900">Output</h2>
85
+ <button @click="clearOutput"
86
+ class="px-3 py-1 bg-gray-200 text-gray-700 rounded hover:bg-gray-300 transition-all text-sm">
87
+ Clear
88
+ </button>
89
+ </div>
90
+
91
+ <div class="bg-gray-50 rounded border border-gray-200 p-3 h-80 overflow-y-auto font-mono text-sm">
92
+ <div v-if="output.length === 0" class="text-gray-400 italic">
93
+ No output yet. Run some code to see results.
94
+ </div>
95
+ <div v-for="(item, idx) in output" :key="idx" class="mb-2">
96
+ <div v-if="item.type === 'log'" class="text-blue-600">
97
+ <span class="text-gray-400">›</span> {{ item.value }}
98
+ </div>
99
+ <div v-else-if="item.type === 'result'" class="text-green-600">
100
+ <span class="text-gray-400">→</span> {{ item.value }}
101
+ </div>
102
+ <div v-else-if="item.type === 'error'" class="text-red-600">
103
+ <span class="text-gray-400">✗</span> {{ item.value }}
104
+ </div>
105
+ <div v-else-if="item.type === 'object'" class="text-gray-700">
106
+ <pre class="whitespace-pre-wrap">{{ item.value }}</pre>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </div>
111
+
112
+ <!-- Saved Programs -->
113
+ <div class="bg-white rounded border border-gray-200 p-3">
114
+ <div class="flex items-center justify-between mb-2">
115
+ <h2 class="text-sm font-semibold text-gray-900">Saved Programs</h2>
116
+ <button @click="saveProgram"
117
+ class="px-2 py-1 bg-blue-600 text-white rounded hover:bg-blue-700 transition-all text-xs">
118
+ + Save
119
+ </button>
120
+ </div>
121
+
122
+ <div v-if="savedPrograms.length === 0" class="text-gray-400 text-xs italic">
123
+ No saved programs yet
124
+ </div>
125
+ <div v-else class="space-y-2 max-h-40 overflow-y-auto">
126
+ <div v-for="(program, idx) in savedPrograms" :key="idx"
127
+ class="bg-gray-50 rounded border border-gray-200 p-2 hover:bg-gray-100 transition-all">
128
+ <div class="flex items-center justify-between">
129
+ <div class="flex-1 min-w-0">
130
+ <div class="font-medium text-gray-900 text-xs truncate">{{ program.name }}</div>
131
+ <div class="text-xs text-gray-500">{{ program.date }}</div>
132
+ </div>
133
+ <div class="flex gap-1 ml-2">
134
+ <button @click="loadSavedProgram(program)"
135
+ class="px-2 py-1 bg-blue-100 text-blue-700 rounded hover:bg-blue-200 text-xs">
136
+ Load
137
+ </button>
138
+ <button @click="deleteSavedProgram(idx)"
139
+ class="px-2 py-1 bg-red-100 text-red-700 rounded hover:bg-red-200 text-xs">
140
+ Del
141
+ </button>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ </div>
150
+ </div>
151
+
152
+ <script type="module">
153
+ import SwissEph from '../src/swisseph.js';
154
+
155
+ const { createApp } = Vue;
156
+
157
+ createApp({
158
+ data() {
159
+ return {
160
+ swisseph: null,
161
+ status: { text: 'Loading...', color: 'text-yellow-600' },
162
+ version: '',
163
+ code: '',
164
+ output: [],
165
+ savedPrograms: [],
166
+ templates: [
167
+ {
168
+ name: 'Birth Chart',
169
+ description: 'Calculate natal chart positions',
170
+ code: `// Birth Chart Calculator
171
+ const birthDate = { year: 1990, month: 6, day: 15, hour: 14.5 };
172
+ const birthPlace = { lat: 40.7128, lon: -74.0060 }; // New York
173
+
174
+ // Convert to Julian Day
175
+ const jd = swisseph.julday(birthDate.year, birthDate.month, birthDate.day, birthDate.hour);
176
+ console.log('Julian Day:', jd);
177
+
178
+ // Calculate Sun position
179
+ const sun = swisseph.calc(jd, swisseph.SE_SUN, swisseph.SEFLG_SWIEPH);
180
+ console.log('Sun:', sun.longitude.toFixed(2) + '°');
181
+
182
+ // Calculate Moon position
183
+ const moon = swisseph.calc(jd, swisseph.SE_MOON, swisseph.SEFLG_SWIEPH);
184
+ console.log('Moon:', moon.longitude.toFixed(2) + '°');
185
+
186
+ // Calculate Ascendant
187
+ const houses = swisseph.houses(jd, birthPlace.lat, birthPlace.lon, 'P');
188
+ console.log('Ascendant:', houses.ascmc[0].toFixed(2) + '°');
189
+
190
+ return {
191
+ sun: sun.longitude,
192
+ moon: moon.longitude,
193
+ ascendant: houses.ascmc[0]
194
+ };`
195
+ },
196
+ {
197
+ name: 'Planet Positions',
198
+ description: 'Get all planet positions',
199
+ code: `// All Planet Positions
200
+ const jd = swisseph.julday(2024, 1, 1, 12.0);
201
+
202
+ const planets = [
203
+ { id: swisseph.SE_SUN, name: 'Sun' },
204
+ { id: swisseph.SE_MOON, name: 'Moon' },
205
+ { id: swisseph.SE_MERCURY, name: 'Mercury' },
206
+ { id: swisseph.SE_VENUS, name: 'Venus' },
207
+ { id: swisseph.SE_MARS, name: 'Mars' },
208
+ { id: swisseph.SE_JUPITER, name: 'Jupiter' },
209
+ { id: swisseph.SE_SATURN, name: 'Saturn' }
210
+ ];
211
+
212
+ const positions = {};
213
+
214
+ planets.forEach(planet => {
215
+ const pos = swisseph.calc(jd, planet.id, swisseph.SEFLG_SWIEPH);
216
+ positions[planet.name] = pos.longitude.toFixed(2) + '°';
217
+ console.log(planet.name + ':', positions[planet.name]);
218
+ });
219
+
220
+ return positions;`
221
+ },
222
+ {
223
+ name: 'House System',
224
+ description: 'Calculate house cusps',
225
+ code: `// House System Calculator
226
+ const jd = swisseph.julday(2024, 1, 1, 12.0);
227
+ const lat = 51.5074; // London
228
+ const lon = -0.1278;
229
+
230
+ // Calculate houses (Placidus)
231
+ const houses = swisseph.houses(jd, lat, lon, 'P');
232
+
233
+ console.log('House Cusps (Placidus):');
234
+ for (let i = 1; i <= 12; i++) {
235
+ console.log('House ' + i + ':', houses.cusps[i].toFixed(2) + '°');
236
+ }
237
+
238
+ console.log('\\nAngles:');
239
+ console.log('Ascendant:', houses.ascmc[0].toFixed(2) + '°');
240
+ console.log('MC:', houses.ascmc[1].toFixed(2) + '°');
241
+
242
+ return {
243
+ cusps: houses.cusps.slice(1, 13),
244
+ ascendant: houses.ascmc[0],
245
+ mc: houses.ascmc[1]
246
+ };`
247
+ },
248
+ {
249
+ name: 'Sidereal Time',
250
+ description: 'Calculate sidereal time',
251
+ code: `// Sidereal Time Calculator
252
+ const now = new Date();
253
+ const jd = swisseph.julday(
254
+ now.getFullYear(),
255
+ now.getMonth() + 1,
256
+ now.getDate(),
257
+ now.getHours() + now.getMinutes() / 60
258
+ );
259
+
260
+ const sidtime = swisseph.sidtime(jd);
261
+ const hours = Math.floor(sidtime);
262
+ const minutes = Math.floor((sidtime - hours) * 60);
263
+ const seconds = Math.floor(((sidtime - hours) * 60 - minutes) * 60);
264
+
265
+ console.log('Current Date:', now.toLocaleDateString());
266
+ console.log('Julian Day:', jd.toFixed(4));
267
+ console.log('Sidereal Time:', hours + 'h ' + minutes + 'm ' + seconds + 's');
268
+
269
+ return {
270
+ julianDay: jd,
271
+ siderealTime: sidtime,
272
+ formatted: hours + ':' + minutes + ':' + seconds
273
+ };`
274
+ },
275
+ {
276
+ name: 'Ayanamsa',
277
+ description: 'Calculate ayanamsa values',
278
+ code: `// Ayanamsa Calculator
279
+ const jd = swisseph.julday(2024, 1, 1, 0.0);
280
+
281
+ // Set Lahiri ayanamsa
282
+ swisseph.set_sid_mode(swisseph.SE_SIDM_LAHIRI, 0, 0);
283
+
284
+ const ayanamsa = swisseph.get_ayanamsa(jd);
285
+ const name = swisseph.get_ayanamsa_name(swisseph.SE_SIDM_LAHIRI);
286
+
287
+ console.log('Ayanamsa System:', name);
288
+ console.log('Ayanamsa Value:', ayanamsa.toFixed(6) + '°');
289
+
290
+ // Calculate tropical vs sidereal Sun
291
+ const tropicalSun = swisseph.calc(jd, swisseph.SE_SUN, swisseph.SEFLG_SWIEPH);
292
+ const siderealSun = tropicalSun.longitude - ayanamsa;
293
+
294
+ console.log('\\nSun Position:');
295
+ console.log('Tropical:', tropicalSun.longitude.toFixed(2) + '°');
296
+ console.log('Sidereal:', siderealSun.toFixed(2) + '°');
297
+
298
+ return {
299
+ ayanamsa: ayanamsa,
300
+ tropical: tropicalSun.longitude,
301
+ sidereal: siderealSun
302
+ };`
303
+ },
304
+ {
305
+ name: 'Date Conversion',
306
+ description: 'Convert between date formats',
307
+ code: `// Date Conversion Examples
308
+ const date = { year: 2024, month: 6, day: 21, hour: 12.0 };
309
+
310
+ // Convert to Julian Day
311
+ const jd = swisseph.julday(date.year, date.month, date.day, date.hour);
312
+ console.log('Julian Day:', jd);
313
+
314
+ // Convert back to calendar
315
+ const revDate = swisseph.revjul(jd, 1);
316
+ console.log('Reverse:', revDate.year + '-' + revDate.month + '-' + revDate.day + ' ' + revDate.hour + 'h');
317
+
318
+ // Calculate day of week
319
+ const dow = swisseph.day_of_week(jd);
320
+ const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
321
+ console.log('Day of Week:', days[dow]);
322
+
323
+ // Delta T
324
+ const deltaT = swisseph.deltat(jd);
325
+ console.log('Delta T:', (deltaT * 86400).toFixed(2) + ' seconds');
326
+
327
+ return {
328
+ julianDay: jd,
329
+ dayOfWeek: days[dow],
330
+ deltaT: deltaT
331
+ };`
332
+ }
333
+ ]
334
+ };
335
+ },
336
+ async mounted() {
337
+ await this.init();
338
+ this.loadSavedPrograms();
339
+ },
340
+ methods: {
341
+ async init() {
342
+ try {
343
+ this.status = { text: 'Initializing...', color: 'text-yellow-600' };
344
+ this.swisseph = new SwissEph();
345
+ await this.swisseph.initSwissEph();
346
+
347
+ this.version = 'Swiss Ephemeris v' + this.swisseph.version();
348
+ this.status = { text: 'Ready', color: 'text-green-600' };
349
+
350
+ // Load first template by default
351
+ this.loadTemplate(this.templates[0]);
352
+ } catch (error) {
353
+ this.status = { text: 'Error: ' + error.message, color: 'text-red-600' };
354
+ }
355
+ },
356
+ loadTemplate(template) {
357
+ this.code = template.code;
358
+ this.output = [];
359
+ },
360
+ runCode() {
361
+ this.output = [];
362
+
363
+ // Create a custom console
364
+ const customConsole = {
365
+ log: (...args) => {
366
+ args.forEach(arg => {
367
+ if (typeof arg === 'object') {
368
+ this.output.push({
369
+ type: 'object',
370
+ value: JSON.stringify(arg, null, 2)
371
+ });
372
+ } else {
373
+ this.output.push({ type: 'log', value: String(arg) });
374
+ }
375
+ });
376
+ }
377
+ };
378
+
379
+ try {
380
+ // Create function with swisseph and console in scope
381
+ const func = new Function('swisseph', 'console', this.code);
382
+ const result = func(this.swisseph, customConsole);
383
+
384
+ if (result !== undefined) {
385
+ if (typeof result === 'object') {
386
+ this.output.push({
387
+ type: 'object',
388
+ value: JSON.stringify(result, null, 2)
389
+ });
390
+ } else {
391
+ this.output.push({ type: 'result', value: String(result) });
392
+ }
393
+ }
394
+ } catch (error) {
395
+ this.output.push({ type: 'error', value: error.message });
396
+ }
397
+ },
398
+ clearOutput() {
399
+ this.output = [];
400
+ },
401
+ saveProgram() {
402
+ const name = prompt('Enter a name for this program:');
403
+ if (!name) return;
404
+
405
+ const program = {
406
+ name: name,
407
+ code: this.code,
408
+ date: new Date().toLocaleString()
409
+ };
410
+
411
+ this.savedPrograms.push(program);
412
+ localStorage.setItem('swisseph_programs', JSON.stringify(this.savedPrograms));
413
+ },
414
+ loadSavedProgram(program) {
415
+ this.code = program.code;
416
+ this.output = [];
417
+ },
418
+ deleteSavedProgram(idx) {
419
+ if (confirm('Delete this program?')) {
420
+ this.savedPrograms.splice(idx, 1);
421
+ localStorage.setItem('swisseph_programs', JSON.stringify(this.savedPrograms));
422
+ }
423
+ },
424
+ loadSavedPrograms() {
425
+ const saved = localStorage.getItem('swisseph_programs');
426
+ if (saved) {
427
+ this.savedPrograms = JSON.parse(saved);
428
+ }
429
+ }
430
+ }
431
+ }).mount('#app');
432
+ </script>
433
+ </body>
434
+ </html>