@inventure71/paddockjs 0.1.0

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.
Files changed (44) hide show
  1. package/AGENTS.md +86 -0
  2. package/CHANGELOG.md +5 -0
  3. package/LICENSE +176 -0
  4. package/README.md +165 -0
  5. package/assets/f1-broadcast-panel-surface.png +0 -0
  6. package/assets/f1-car-sprite-game.png +0 -0
  7. package/assets/f1-driver-helmet.png +0 -0
  8. package/assets/f1-safety-car-sprite.png +0 -0
  9. package/assets/f1-texture-asphalt.png +0 -0
  10. package/assets/f1Logo.png +0 -0
  11. package/docs/architecture.md +209 -0
  12. package/docs/concepts.md +178 -0
  13. package/docs/data_contract.md +360 -0
  14. package/docs/data_model.md +167 -0
  15. package/docs/index.md +15 -0
  16. package/docs/learnings.md +47 -0
  17. package/docs/rules.md +181 -0
  18. package/docs/system_specs.md +211 -0
  19. package/package.json +85 -0
  20. package/src/api/PaddockSimulatorController.js +233 -0
  21. package/src/app/F1SimulatorApp.js +1293 -0
  22. package/src/app/domBindings.js +64 -0
  23. package/src/config/defaultAssets.js +29 -0
  24. package/src/config/defaultOptions.js +184 -0
  25. package/src/data/championship.js +225 -0
  26. package/src/data/customFields.js +14 -0
  27. package/src/data/demoDrivers.js +134 -0
  28. package/src/data/driverData.js +71 -0
  29. package/src/data/normalizeDrivers.js +21 -0
  30. package/src/data/vehicleData.js +81 -0
  31. package/src/index.d.ts +399 -0
  32. package/src/index.js +116 -0
  33. package/src/rendering/proceduralTrackAsset.js +517 -0
  34. package/src/rendering/renderSnapshot.js +23 -0
  35. package/src/simulation/driverController.js +253 -0
  36. package/src/simulation/raceSimulation.js +933 -0
  37. package/src/simulation/simMath.js +34 -0
  38. package/src/simulation/trackModel.js +607 -0
  39. package/src/simulation/units.js +30 -0
  40. package/src/simulation/vehiclePhysics.js +104 -0
  41. package/src/standalone.js +15 -0
  42. package/src/styles.css +1818 -0
  43. package/src/ui/componentTemplates.js +229 -0
  44. package/src/ui/shellTemplate.js +43 -0
package/AGENTS.md ADDED
@@ -0,0 +1,86 @@
1
+ # AGENTS.md
2
+
3
+ ## Project
4
+
5
+ PaddockJS is an installable browser component package for an F1-style project race simulator.
6
+
7
+ The package lives independently from the portfolio website. Host websites install it and mount it with:
8
+
9
+ ```js
10
+ import { mountF1Simulator } from '@inventure71/paddockjs';
11
+ ```
12
+
13
+ It can also be mounted as rearrangeable package-owned pieces with:
14
+
15
+ ```js
16
+ import { createPaddockSimulator } from '@inventure71/paddockjs';
17
+ ```
18
+
19
+ ## What Is In This Repo
20
+
21
+ - `src/index.js`: public package API.
22
+ - `src/api/`: public composable mounting controller.
23
+ - `src/app/`: browser runtime, DOM bindings, PixiJS app lifecycle, controls, readouts, and camera orchestration.
24
+ - `src/config/`: default options and bundled default asset mapping.
25
+ - `src/data/`: driver/project data normalization, driver/car pairing, rating conversion, and demo data.
26
+ - `src/rendering/`: PixiJS track rendering helpers and render interpolation.
27
+ - `src/simulation/`: race rules, driver AI, vehicle physics, track model, and math utilities.
28
+ - `src/ui/`: generated markup for the all-in-one shell and individually mounted UI surfaces.
29
+ - `src/__tests__/`: package tests.
30
+ - `assets/`: simulator-owned default assets.
31
+ - `docs/`: package specs and design documentation.
32
+ - `INSTALL_AND_UPDATE.md`: install/update workflow.
33
+
34
+ ## Required Documentation Discipline
35
+
36
+ When changing simulator behavior, update docs in the same change.
37
+
38
+ Use this mapping:
39
+
40
+ - Public API, lifecycle, install assumptions: update `docs/system_specs.md` and `README.md` if needed.
41
+ - Race behavior, DRS, safety car, starts, physics, collisions: update `docs/rules.md`.
42
+ - Vocabulary or domain model: update `docs/concepts.md`.
43
+ - Driver/entry/options/assets callback shapes: update `docs/data_contract.md`.
44
+ - Module ownership or data/control flow: update `docs/architecture.md`.
45
+ - Local install/update workflow: update `INSTALL_AND_UPDATE.md`.
46
+
47
+ Do not leave docs describing old behavior after code changes.
48
+
49
+ ## Linear Work Logging
50
+
51
+ For any implementation that is more than a trivial/no-op change, log the work in the PaddockJS project on Linear.
52
+
53
+ The Linear update must include:
54
+
55
+ - what task was done
56
+ - how it was solved
57
+ - any important verification or remaining follow-up
58
+
59
+ ## Engineering Rules
60
+
61
+ - Preserve the package boundary: PaddockJS should stay reusable and host-agnostic.
62
+ - Host-specific routing belongs in `onDriverOpen(driver)`, not package internals.
63
+ - Host-specific project data should be passed as `drivers` and `entries`, not hardcoded into the runtime.
64
+ - The package should own default simulator assets so hosts do not need to copy them.
65
+ - Treat the timing board width and broadcast proportions as package-owned. Do not resize or retune timing-board width as a casual fix; preserve its intended width and solve layout bugs through internal column/content constraints unless the user explicitly asks to redesign the timing board sizing.
66
+ - Prefer small, focused modules when extracting from `F1SimulatorApp.js`.
67
+ - Keep simulation logic deterministic for the same seed, track seed, drivers, entries, and rules.
68
+ - Do not claim completion without running verification.
69
+
70
+ ## Verification
71
+
72
+ Run before claiming package changes are complete:
73
+
74
+ ```bash
75
+ npm run check
76
+ ```
77
+
78
+ For portfolio integration changes, also run from the portfolio repo:
79
+
80
+ ```bash
81
+ cd /Users/inventure71/VSProjects/Inventure71.github.io
82
+ npm install ../PaddockJS
83
+ npm run check
84
+ ```
85
+
86
+ Browser behavior changes need a browser smoke test against the host page.
package/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The npm release workflow updates this changelog from committed Changesets.
package/LICENSE ADDED
@@ -0,0 +1,176 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
package/README.md ADDED
@@ -0,0 +1,165 @@
1
+ # PaddockJS
2
+
3
+ This folder is the extractable F1 simulator component. It owns the simulator source, bundled simulator assets, CSS, demo data, and the public mount API.
4
+
5
+ ## Install
6
+
7
+ From npm:
8
+
9
+ ```bash
10
+ npm install @inventure71/paddockjs
11
+ ```
12
+
13
+ For local sibling-repo development:
14
+
15
+ ```bash
16
+ npm install ../PaddockJS
17
+ ```
18
+
19
+ ## Documentation
20
+
21
+ Start with [docs/index.md](docs/index.md) for system specs, rules, concepts, data contracts, and architecture notes.
22
+
23
+ ## Package Workflow
24
+
25
+ The repo now includes the full package-release boundary:
26
+
27
+ - a tracked showcase host in `local-preview/`
28
+ - public TypeScript declarations in `src/index.d.ts`
29
+ - GitHub Actions CI in `.github/workflows/ci.yml`
30
+ - Changesets release automation in `.github/workflows/release.yml`
31
+
32
+ Useful commands:
33
+
34
+ ```bash
35
+ npm run check
36
+ npm run showcase:dev
37
+ npm run showcase:build
38
+ npm run changeset
39
+ ```
40
+
41
+ `npm run check` verifies the runtime tests, public declaration file, dry package contents, and showcase build.
42
+
43
+ ## API
44
+
45
+ All-in-one mount:
46
+
47
+ ```js
48
+ import { mountF1Simulator } from '@inventure71/paddockjs';
49
+
50
+ const simulator = await mountF1Simulator(document.getElementById('sim-root'), {
51
+ preset: 'timing-overlay',
52
+ drivers: [
53
+ {
54
+ id: 'budget',
55
+ name: 'Budget Buddy',
56
+ color: '#ff2d55',
57
+ link: '/project_details/project-budget-buddy.html',
58
+ raceData: ['AI finance coach', 'Python + LLM', 'Budget guardrails'],
59
+ },
60
+ ],
61
+ entries: [
62
+ {
63
+ driverId: 'budget',
64
+ driverNumber: 71,
65
+ timingName: 'Budget',
66
+ driver: { pace: 52, racecraft: 74, aggression: 38, riskTolerance: 47, patience: 81, consistency: 86 },
67
+ vehicle: { id: 'budget-bb01', name: 'BB-01 Ledger', power: 48, braking: 72, aero: 55, dragEfficiency: 66, mechanicalGrip: 63, weightControl: 58, tireCare: 82 },
68
+ },
69
+ ],
70
+ onDriverOpen(driver) {
71
+ window.location.href = driver.link;
72
+ },
73
+ onRaceFinish({ winner, classification }) {
74
+ console.log('Race winner:', winner.name, classification);
75
+ },
76
+ });
77
+ ```
78
+
79
+ Composable mount:
80
+
81
+ ```js
82
+ import { createPaddockSimulator } from '@inventure71/paddockjs';
83
+
84
+ const simulator = createPaddockSimulator({
85
+ drivers,
86
+ entries,
87
+ onDriverOpen(driver) {
88
+ window.location.href = driver.link;
89
+ },
90
+ });
91
+
92
+ simulator.mountRaceControls(document.getElementById('sim-controls'));
93
+ simulator.mountCameraControls(document.getElementById('sim-camera-controls'));
94
+ simulator.mountSafetyCarControl(document.getElementById('sim-safety-car'));
95
+ simulator.mountTimingTower(document.getElementById('sim-timing'));
96
+ simulator.mountRaceCanvas(document.getElementById('sim-race'), {
97
+ includeRaceDataPanel: true,
98
+ });
99
+ simulator.mountTelemetryPanel(document.getElementById('sim-telemetry'));
100
+ simulator.mountCarDriverOverview(document.getElementById('sim-overview'));
101
+
102
+ await simulator.start();
103
+ ```
104
+
105
+ The race canvas can also own the timing tower when a host wants a single reusable race-window component:
106
+
107
+ ```js
108
+ simulator.mountRaceCanvas(document.getElementById('sim-race'), {
109
+ includeTimingTower: true,
110
+ includeRaceDataPanel: true,
111
+ timingTowerVerticalFit: 'scroll',
112
+ });
113
+ ```
114
+
115
+ `drivers` is the host-owned project/pilot list. `entries` is the optional driver/car/team pairing sheet. The car/driver overview uses the existing driver and vehicle rating components from each entry. Driver and vehicle entries can also include `customFields` as extra label/value metadata. Entries can include `team: { id, name, color, icon }`; the timing tower uses the team icon and defaults team color to the car color when omitted. Assets, including the default car image and generic driver helmet, are bundled by default, so the host website does not need to provide simulator images or textures.
116
+
117
+ The returned object supports:
118
+
119
+ - `destroy()`
120
+ - `restart(nextOptions)`
121
+ - `selectDriver(driverId)`
122
+ - `setSafetyCarDeployed(deployed)`
123
+ - `callSafetyCar()`
124
+ - `clearSafetyCar()`
125
+ - `toggleSafetyCar()`
126
+ - `getSnapshot()`
127
+
128
+ Useful UI options:
129
+
130
+ ```js
131
+ preset: 'timing-overlay',
132
+ theme: {
133
+ accentColor: '#00ff84',
134
+ timingTowerMaxWidth: '360px',
135
+ raceViewMinHeight: '720px',
136
+ },
137
+ ui: {
138
+ layoutPreset: 'left-tower-overlay',
139
+ cameraControls: 'external',
140
+ showFps: false,
141
+ telemetryIncludesOverview: false,
142
+ raceDataBanners: {
143
+ initial: 'project',
144
+ enabled: ['project', 'radio'],
145
+ },
146
+ raceDataBannerSize: 'auto',
147
+ timingTowerVerticalFit: 'expand-race-view',
148
+ }
149
+ ```
150
+
151
+ `preset` is resolved before explicit host options. Available presets are `dashboard`, `timing-overlay`, `compact-race`, and `full-dashboard`; hosts can start from a preset and override any `ui` or `theme` field. `theme` maps to package CSS variables for the stable sizing/color contract: `accentColor`, `greenColor`, `yellowColor`, `timingTowerMaxWidth`, and `raceViewMinHeight`.
152
+
153
+ If `trackSeed` is omitted, each mounted browser simulator creates a fresh procedural circuit. Passing `trackSeed` makes the track deterministic so multiple embeds can share the same generated circuit; repeated procedural seeds are cached within the page runtime.
154
+
155
+ `layoutPreset: 'left-tower-overlay'` creates a left broadcast gutter inside the race view, frames the camera around the remaining race area, and places the timing tower in that gutter without covering camera controls. The project/radio lower-third stays inside the race window and can intentionally render over the timing sidebar instead of shrinking around it. Composable hosts should pass `{ includeRaceDataPanel: true }` to `mountRaceCanvas()` when they want that lower-third clipped and layered by the race window; `mountRaceDataPanel()` remains available for hosts that intentionally want the banner as a standalone surface. `includeTimingTower: true` embeds the timing tower directly inside the race canvas and reserves camera space from the measured tower gutter. `raceDataBanners.initial` selects the starting banner state (`'project'`, `'radio'`, or `'hidden'`), while `raceDataBanners.enabled` chooses which banner types may appear. `raceDataBannerSize: 'auto'` uses the race space to the right of the timing board when it is wide enough and falls back to the full lower-third overlap when it is not; `'custom'` preserves the default CSS-variable-driven lower-third size for hosts that want to tune their own banner geometry. `timingTowerVerticalFit: 'expand-race-view'` lets the race window grow tall enough for the tower; `'scroll'` keeps the race window height and scrolls the timing list inside the cropped tower. The same fit values can be passed to `mountRaceCanvas()` when `includeTimingTower` is enabled. Standalone timing towers are capped by `--timing-board-max-width` and fill their mount root height; placing the root in a fixed-height container makes only the timing entries scroll. Timing entries always stack from the top as fixed rows, so P1/P2 occupy the same vertical positions whether the race has 2 cars or 20. The timing tower includes an `Int`/`Gap` broadcast switch: `Int` shows the interval to the car ahead, while `Gap` shows total gap to the leader. `cameraControls: 'external'` moves view controls out of the canvas so hosts can mount them with `mountCameraControls()`. `showFps: false` hides the FPS readout. `telemetryIncludesOverview: false` keeps telemetry text-only so hosts can mount `mountCarDriverOverview()` separately.
156
+
157
+ Mounted package surfaces include a lightweight red start-light loading overlay. The runtime removes each overlay after PixiJS, assets, controls, and the initial readouts are ready.
158
+
159
+ The runtime also pauses its render ticker when the race canvas is offscreen or the browser tab is hidden. This keeps pages with multiple PaddockJS embeds responsive without requiring host code to manually start and stop each simulator.
160
+
161
+ Lifecycle callbacks are optional and host-owned. PaddockJS emits `onLoadingChange`, `onReady`, `onError`, `onDriverSelect`, `onRaceEvent`, `onLapChange`, and `onRaceFinish`; callback errors are routed to `onError` when provided and do not stop the simulator loop. Race snapshots include per-car interval timing, leader-gap timing, calibrated `speedKph`, finish state, `raceControl.winner` after the first finisher, and final `raceControl.classification` only after the whole field finishes. The field then circulates in safety-car mode and the race canvas shows a package-owned winner banner.
162
+
163
+ ## License
164
+
165
+ PaddockJS is released under `Apache-2.0`. See [LICENSE](LICENSE).
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,209 @@
1
+ # Architecture
2
+
3
+ ## Package Boundary
4
+
5
+ PaddockJS is the simulator package. A host website imports it and passes data.
6
+
7
+ The package owns:
8
+
9
+ - Simulator source.
10
+ - Bundled assets.
11
+ - CSS.
12
+ - DOM shell generation.
13
+ - PixiJS renderer.
14
+ - Simulation rules and physics.
15
+ - Demo data.
16
+ - Tests.
17
+
18
+ The host owns:
19
+
20
+ - Project-specific driver data.
21
+ - Navigation behavior.
22
+ - Page placement.
23
+ - Host build and deployment.
24
+
25
+ ## Main Flow
26
+
27
+ All-in-one flow:
28
+
29
+ ```txt
30
+ host page
31
+ -> mountF1Simulator(root, options)
32
+ -> resolveF1SimulatorOptions(options)
33
+ -> normalizeSimulatorDrivers(drivers, entries)
34
+ -> createF1SimulatorShell(options)
35
+ -> new F1SimulatorApp(shell, resolvedOptions)
36
+ -> createRaceSimulation(...)
37
+ -> PixiJS render loop + DOM readout updates
38
+ ```
39
+
40
+ Composable flow:
41
+
42
+ ```txt
43
+ host page
44
+ -> createPaddockSimulator(options)
45
+ -> resolveF1SimulatorOptions(options)
46
+ -> simulator.mountRaceControls(controlsRoot)
47
+ -> simulator.mountCameraControls(cameraControlsRoot)
48
+ -> simulator.mountSafetyCarControl(safetyCarRoot)
49
+ -> simulator.mountTimingTower(timingRoot)
50
+ -> simulator.mountRaceCanvas(canvasRoot, { includeRaceDataPanel })
51
+ -> simulator.mountTelemetryPanel(telemetryRoot)
52
+ -> simulator.mountRaceDataPanel(raceDataRoot) [optional standalone alternative]
53
+ -> simulator.start()
54
+ -> new F1SimulatorApp(compositeRoot, resolvedOptions)
55
+ -> PixiJS render loop + DOM readout updates
56
+ ```
57
+
58
+ ## Public Entry
59
+
60
+ `src/index.js` is the public package entry.
61
+
62
+ Responsibilities:
63
+
64
+ - Import CSS.
65
+ - Export package API and data helpers.
66
+ - Validate the root element.
67
+ - Resolve options.
68
+ - Create the DOM shell.
69
+ - Construct and initialize `F1SimulatorApp`.
70
+ - Return the controller methods.
71
+ - Re-export the composable mount API.
72
+
73
+ `src/api/PaddockSimulatorController.js` owns composable mounting.
74
+
75
+ Responsibilities:
76
+
77
+ - Resolve host options once for component templates and runtime.
78
+ - Render each independently mounted component into its host root.
79
+ - Build a composite DOM query surface for `F1SimulatorApp`.
80
+ - Initialize and control the shared simulator runtime through `start()`, `restart()`, `destroy()`, and state methods.
81
+ - Expose explicit safety-car methods for callers that want to deploy or release the safety car without using package-rendered buttons.
82
+
83
+ ## Runtime App
84
+
85
+ `src/app/F1SimulatorApp.js` owns browser runtime behavior.
86
+
87
+ Responsibilities:
88
+
89
+ - PixiJS application setup.
90
+ - Asset loading.
91
+ - Sprite creation.
92
+ - Control event binding.
93
+ - Fixed-step simulation pacing.
94
+ - Viewport/document visibility throttling for the PixiJS ticker.
95
+ - Camera modes.
96
+ - Overlay-safe camera framing for the left timing-tower preset.
97
+ - Timing tower rendering.
98
+ - Telemetry rendering.
99
+ - Race data panel rendering.
100
+ - Safety car button.
101
+ - Multiple package-rendered safety-car buttons bound to one simulation state.
102
+ - Restart behavior.
103
+ - Lifecycle cleanup.
104
+
105
+ This file is still large. When changing it substantially, prefer extracting cohesive modules rather than adding unrelated responsibilities.
106
+
107
+ `src/app/domBindings.js` owns DOM selector lookup and null-safe readout text writes for package-generated UI surfaces.
108
+
109
+ ## Simulation Core
110
+
111
+ `src/simulation/raceSimulation.js` owns race state and rules.
112
+
113
+ Responsibilities:
114
+
115
+ - Race-control mode.
116
+ - Start sequence.
117
+ - Safety car.
118
+ - Car creation.
119
+ - Race ordering.
120
+ - Lap calculation.
121
+ - Timing history.
122
+ - DRS eligibility.
123
+ - Collision response.
124
+ - Snapshot creation.
125
+
126
+ `src/simulation/driverController.js` owns AI control decisions.
127
+
128
+ `src/simulation/vehiclePhysics.js` owns vehicle integration and surface physics.
129
+
130
+ `src/simulation/trackModel.js` owns track construction, procedural track generation, DRS zones, and nearest-track queries. Browser mounts generate a fresh procedural seed when `trackSeed` is omitted; explicit procedural track seeds are cached so repeated mounts with the same seed do not regenerate or rebuild the same model.
131
+
132
+ `src/simulation/units.js` owns conversion between simulator units and public meter/km/h display values. Physics stays in simulator units; snapshots expose calibrated display fields such as `speedKph`, `distanceMeters`, and `gapMeters`.
133
+
134
+ `src/rendering/renderSnapshot.js` owns interpolation for rendering.
135
+
136
+ The app runtime pauses its PixiJS ticker when the race canvas is outside the viewport or the document is hidden, then resets the frame clock before resuming. This prevents host pages with several simulator embeds from running every race while only one is visible, and avoids a large simulation catch-up step when the canvas re-enters view. Camera safe-area layout measurements are cached and invalidated by resize observation so the 60 FPS render path does not force repeated DOM geometry reads. Long project-radio delays are also treated as stale schedule state instead of replaying every missed lower-third transition.
137
+
138
+ ## Data Layer
139
+
140
+ `src/data/driverData.js` converts driver rating sheets into constructor arguments.
141
+
142
+ `src/data/vehicleData.js` converts vehicle rating sheets into physical setup values.
143
+
144
+ `src/data/championship.js` pairs drivers with entries and generates timing codes, numbers, team metadata, and converted constructor data.
145
+
146
+ `src/data/normalizeDrivers.js` validates host driver data and invokes championship pairing.
147
+
148
+ `src/data/demoDrivers.js` is demo/portfolio-flavored sample data. It should not become the only supported data path.
149
+
150
+ ## UI Shell
151
+
152
+ `src/ui/componentTemplates.js` owns the generated markup for individual UI surfaces:
153
+
154
+ - Race controls.
155
+ - Camera controls.
156
+ - Safety-car control.
157
+ - Timing tower.
158
+ - Race canvas.
159
+ - Race-data panel.
160
+ - Telemetry panel.
161
+ - Car/driver overview panel.
162
+
163
+ `src/ui/shellTemplate.js` composes those component templates into the default all-in-one simulator DOM and package-owned layout presets such as `left-tower-overlay`. The telemetry panel can embed the car/driver overview, but the overview is also a separately mountable package-owned component. The left-tower overlay preset is responsible for internal component placement and package-owned proportions; the project/radio lower-third remains owned by the race canvas so it can either use the race space beside the timing tower in `auto` sizing mode or overlap the tower when space is constrained. Composable hosts can also ask the race-canvas template to embed the timing tower directly with `includeTimingTower`, using the same expand-vs-scroll vertical fit contract. `F1SimulatorApp` measures the resulting timing-tower gutter when framing the PixiJS camera, whether the tower comes from the prebuilt shell or from an embedded composable race canvas. Hosts should not provide the internal simulator markup or tune preset internals with raw sizing options.
164
+
165
+ `src/config/defaultOptions.js` owns preset resolution and the public theme contract. Presets are merged before host options; theme fields are applied as package CSS variables by the all-in-one app and composable controller. This keeps sizing/color customization explicit without turning internal layout ratios into public API.
166
+
167
+ Race completion is owned by `src/simulation/raceSimulation.js`. The app layer reads `raceControl.finished`, `winner`, and `classification` from snapshots, renders the winner banner, and emits lifecycle callbacks. UI code must not infer finish state from lap text or timing rows.
168
+
169
+ Composable hosts may choose where each package-owned component root is placed, but they still receive package-generated markup through the public mount functions. The controller marks each mounted root as an `f1-sim-component` styling scope so standalone pieces receive the same package variables as the all-in-one shell.
170
+
171
+ `src/styles.css` styles the generated shell, imports package fonts, caps timing-tower width for readability, and owns the fixed-height timing-list scroll behavior. Timing rows are fixed grid rows stacked from the top, so rank positions do not stretch or redistribute when the number of entries changes. The timing tower owns its runtime interval-vs-leader-gap switch, while `F1SimulatorApp` reads both timing values from the race snapshot. Component templates include lightweight package-owned loading overlays; `F1SimulatorApp` removes them after startup initialization has completed.
172
+
173
+ The host page should only provide:
174
+
175
+ ```html
176
+ <div id="f1-simulator-root"></div>
177
+ ```
178
+
179
+ ## Assets
180
+
181
+ `src/config/defaultAssets.js` imports bundled assets from `assets/` and exposes default asset resolution.
182
+
183
+ Assets are bundled by the host build tool when the package is imported.
184
+
185
+ ## Tests
186
+
187
+ Tests live in `src/__tests__/`.
188
+
189
+ Current coverage includes:
190
+
191
+ - Championship metadata.
192
+ - Component API and callback behavior.
193
+ - Driver controller behavior.
194
+ - Procedural track rendering helpers.
195
+ - Race simulation rules.
196
+ - Render interpolation.
197
+ - Track model behavior.
198
+
199
+ Run:
200
+
201
+ ```bash
202
+ npm test
203
+ ```
204
+
205
+ ## Design Rule
206
+
207
+ Keep reusable simulator behavior in PaddockJS. Keep website-specific data and routing in the host.
208
+
209
+ If a change adds portfolio-only behavior to package internals, move it to the host integration layer instead.