Compare commits

...

1172 Commits

Author SHA1 Message Date
Claude b1848d2982 refactor(settings): unify empty states, move entity forms to modals, add sidebar search
The settings UI had accumulated three inconsistencies. This addresses all
three without renaming any group or breaking `?settings=` deep links.

Empty states
- Add `Panel.EmptyState` / `Panel.TableEmpty` (title + description + action),
  promoting the treatment previously local to the spreadsheet PreviewTable.
- Add missing empty states to Custom fields, Projects, Rundowns and both
  Client tables, which previously rendered bare column headers.
- Convert the remaining `TableEmpty` call sites off the generic "No data yet".
- Rename `.empty` to `.loaderBox` in ProjectPanel, where it meant a loader box
  rather than an empty state.

Entity forms in modals
- Add `useEntityModal`, replacing six different open/close mechanisms.
- Move the multi-field create/edit forms into `Modal`: automations (wide),
  triggers, URL presets, custom fields, custom views, new rundown and project
  merge. Single-field row renames stay inline.
- Create and edit now share one surface instead of rendering above the table
  and in-place respectively, and the list is no longer interactive underneath.
- Remove the duplicate project create form; the "New" button now routes to the
  existing QuickStart modal.

Sidebar
- Add a search box with per-section keywords, so sections are reachable by the
  term users have in mind (osc, alias, google sheet, pin...).
- Fix duplicated `manage__sheets` id, which highlighted two entries at once.

Also
- Close button no longer floats over content; drop the 300px padding hack.
- Tables scroll with the panel instead of owning a nested scroll area.
- Derive divider/table/list padding from a card padding custom property.
- Fix an unreachable branch that stopped duplicate custom field labels from
  being rejected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UP4knuna8kLe79NAuNDhUJ
2026-07-26 06:25:20 +00:00
Carlos Valente 5cf36f049a refactor(modal): allow wider modals 2026-07-25 14:28:45 +02:00
Carlos Valente c6248b0c72 refactor(import): improve preview UI 2026-07-25 14:28:45 +02:00
Alex Christoffer Rasmussen 2a890cf2b3 fix: issue where a countToEnd would lead to incorrect expected times (#2149)
* fix: issue where a count-to-end would lead to incorrect expected times

* fix: include add time in overtime when countToEnd

* fix: ui and server use same calculation for expected end
2026-07-25 11:22:24 +02:00
Carlos Valente a8c611911d refactor(rundown): new entries are appended to rundown 2026-07-22 07:17:39 +02:00
Carlos Valente 8363f06a5c chore(rundown): improve documentation of insert logic 2026-07-22 07:17:39 +02:00
will@t1d.dev 5f040092cb fix: timer-legacy view not served in desktop distributions
The server serves html/timer-legacy.html and html/login.html from disk
at runtime, relative to the bundled server. The Docker image copies
these files but the electron packaging did not, so the view 404ed in
all desktop distributions.

Additionally, AppImages mount at /tmp/.mount_*, a hidden directory.
Express sendFile refuses paths containing dot-segments by default
(returns 404 without touching disk), so the view failed on Linux even
with the file packaged. Allow dotfiles for this route; the request
path is fixed so no user input is affected.
2026-07-19 10:02:45 +02:00
Carlos Valente 347c748dd9 feat(rundown): add shortcut to jump to current element 2026-07-18 17:03:02 +02:00
Carlos Valente f234a1f892 fix(rundown): prevent skipped events from influencing day calculations 2026-07-18 16:52:36 +02:00
Carlos Valente 0c5e87b7e7 fix(ui): prevent warning indicator from overflowing container 2026-07-18 16:52:36 +02:00
Carlos Valente 64082ceac0 refactor(rundown): improve visibility of count to end 2026-07-18 16:45:55 +02:00
Carlos Valente f598e8dab1 refactor(time control): improve handling of overtime and count to end 2026-07-18 16:45:55 +02:00
Carlos Valente 0c84a3ff9e fix: delays account for midnight 2026-07-18 16:45:55 +02:00
Carlos Valente c8760b5e9c bump version to 4.11.0 2026-07-18 15:24:25 +02:00
Claude 34360a5b8f feat(import): add merge strategy and new-rundown destination to spreadsheet import
Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2026-07-17 22:26:19 +02:00
Claude 5e6debcce0 fix(views): center timer digits with flexbox for embedded browser compatibility
The Stage Timer and PiP Timer views relied on `align-content: center` to
vertically center the timer digits inside `.timer-container`, which is a
plain block element. Aligning children of a block container via
`align-content` only works in Chromium 123+ (Firefox 125+, Safari 17.4+).

Embedded browsers such as vMix Browser Input (CEF V115) and other older
CEF/Chromium-based production tools ignore the property, so the digits
fall back to the top of the container while every other element renders
correctly.

Make `.timer-container` a real flex column and center with
`justify-content: center`, which is universally supported and matches the
centering approach used elsewhere in the codebase. The removed
`justify-self`/`align-self` were no-ops on a full-width flex item.

Fixes #2126

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014T6ENZ3r6JXZb1fpYw2oNY
2026-07-17 20:59:49 +02:00
Carlos Valente c191786e50 refactor(navigation): clear params from editor drawer 2026-07-17 15:50:42 +02:00
Carlos Valente 6911e37c18 refactor(automation): improve automation UX 2026-07-17 10:53:45 +02:00
Claude 86b2132cdc feat(automation): expose group title and allow setting secondary text
Surface {{groupNow.*}} template variables (title, note, colour, times,
custom fields) in the automation template autocomplete so events inside a
group can reference their group. The runtime store already carries
groupNow, so substitution and filters worked already; this makes it
discoverable.

Extend the message-secondary action with an optional text field so an
automation can set the secondary message content, not just its source.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZYPZVdZLWowU7DyzGkWyy
2026-07-17 10:53:45 +02:00
Carlos Valente 25f0dba83a chore: add codegraph to gitignore 2026-07-16 10:46:10 +02:00
Carlos Valente daa032e92c ux(finder): add navigation shortcuts 2026-07-14 12:04:44 +02:00
Carlos Valente ae65ec97d6 fix(shortcut): correct shortcut to delete 2026-07-14 12:04:44 +02:00
Carlos Valente e7e4302100 refactor(shortcuts): improve readability of info element 2026-07-14 12:04:44 +02:00
Carlos Valente 7e7d35980a refactor(op): improve status recognition 2026-07-14 11:58:59 +02:00
Carlos Valente 5001dda284 feat(op): add group visual relation 2026-07-14 11:58:59 +02:00
Carlos Valente a75d35bdc0 refactor(op): improve delay indicator styles 2026-07-14 11:58:59 +02:00
Claude 2e631194e1 fix(ui): improve progress bar with local interpolation
Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2026-07-14 11:25:22 +02:00
Carlos Valente f39892dc6e feat(navigation): persist view params customisation 2026-07-13 18:44:38 +02:00
Carlos Valente 6bcfd87851 refactor(rundown): show locked duration group indicator 2026-07-13 17:34:11 +02:00
Claude 4addb50ca4 feat(countdown): allow counting down to a group 2026-07-13 17:29:56 +02:00
Carlos Valente 161ab3fe13 refactor(timer): elapsed is active time from start 2026-07-13 15:34:31 +02:00
Carlos Valente 4f4626a163 refactor: allow larger settings panels 2026-07-13 15:33:23 +02:00
Claude 82d5f02ea0 refactor(mcp): guide agents through creating clean rundowns 2026-07-13 15:33:23 +02:00
Claude 05aaeddf22 resolver: drop dead ignoreDeprecations from tsconfig.dts.json
Confirmed empirically that it now suppresses nothing under TS 7.0.2:
compiling tsconfig.dts.json directly with this flag set to "6.0",
"7.0", or removed entirely all produce identical (clean) output. It
was a leftover suppression the previous tsdown mode never actually
needed once nothing in the config triggers a deprecation warning.
2026-07-13 14:27:31 +02:00
Claude 949ab2b7dc Upgrade TypeScript to 7.0.2 (native Go compiler)
TypeScript 7 ships as a native Go binary and drastically trims the
package's public JS API surface (no more `main`, and `exports['.']`
now only resolves to lib/version.cjs) since the programmatic compiler
API isn't stable again until 7.1. This broke apps/resolver's tsdown
build, which used rolldown-plugin-dts' tsc-program mode (import ts
from 'typescript'; ts.createProgram(...)) for declaration generation.

Fix by switching that build to isolatedDeclarations, which lets
rolldown-plugin-dts generate .d.ts via its oxc-based path instead of
the TS compiler API entirely. That surfaced two isolatedDeclarations
violations in ontime-types (array/Object.keys initializers lacking
explicit annotations), fixed with explicit `string[]` types matching
their existing inferred type. Also bumped tsdown to pick up its latest
rolldown-plugin-dts, and disabled allowJs in tsconfig.dts.json since
it's incompatible with isolatedDeclarations.

Full workspace typecheck, lint, build, and test suite (1186 tests)
pass under TypeScript 7.0.2.
2026-07-13 14:27:31 +02:00
Alex Christoffer Rasmussen f12d3ca59c Small cleanup (#2114)
* refactor: propper memo for `ScheduleContext` value

* chore: removed unused file `cuesheet.utils.ts`

* fix: memoize event in ScheduleProvider
2026-07-11 22:20:32 +02:00
Carlos Valente 42147d4d5b allow editing cuesheet link permissions 2026-07-01 13:24:47 +02:00
Carlos Valente 7850358039 bump ontime to version 4.10.0 2026-07-01 13:20:04 +02:00
Claude 52cae1e22f feat(timeline): add group reference
Co-authored-by: Carlos Valente <carlosvalente@pm.me>
2026-07-01 12:48:58 +02:00
Carlos Valente 48f9fb2609 expose mcp server in ontime 2026-07-01 12:48:40 +02:00
Carlos Valente ce8a4d230b refactor(translations): allow resetting to default values 2026-07-01 09:02:13 +02:00
Carlos Valente 1553573b94 feat(url-presets): allow presets to appear as nav menu items (#2110) 2026-06-28 18:07:02 +02:00
Alex Christoffer Rasmussen 41a09bf5c3 fix: send refetch on all rundown edits (#2098)
* fix(server): send refetch on all rundown edits

* fix(test): cuesheet tabing

* chore: remove unneeded async

* refactor: small cleanups

* chore: spelling

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* chore: remove unneded async/await

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2026-06-20 12:21:11 +02:00
Claude 5dbe7afae8 feat(client): add rundown default to inherit parent group colour 2026-06-20 10:46:34 +02:00
Carlos Valente 6633cfc869 chore: minor server package upgrades 2026-06-20 09:15:23 +02:00
Carlos Valente d127367df9 chore: minor client package upgrades 2026-06-20 09:15:23 +02:00
Carlos Valente b21ec41ffd chore: upgrade base-ui 2026-06-20 09:15:23 +02:00
Carlos Valente 66292bc0cf chore: upgrade react packages 2026-06-20 09:15:23 +02:00
Alex Christoffer Rasmussen b946b245c2 feat: create the first event from the cuesheet (#2105)
* feat: create the first event from the cuesheet

* chore: cleanup css

* test: cuesheet beckground edit from empty state

* feat: only show +event button when user is in edit mode and have full write perms
2026-06-17 22:31:13 +02:00
Carlos Valente c238147b75 chore: bump node version 2026-06-11 18:58:00 +02:00
Alex Christoffer Rasmussen 5f33c993f6 bump: v4.9.0 (#2095) 2026-06-07 13:53:25 +02:00
Carlos Valente 0e6d6cd416 refactor: allow secondary rundowns (#2086)
* refactor: allow secondary rundowns

* ui: move dropdown

* feat: follow loaded

* ui: background edit warning

ui: fix disable radio button

* feat(ui): add loaded sufix in the rundown list

* feat(ui): add direct link to background edit from rundown manager

* fix: better fallback

* fix: default to is isCurrentRundown for nav bar colour

* chore: rename navigate to cuesheet

---------

Co-authored-by: alex-arc <ac@omnivox.dk>
2026-06-07 13:33:38 +02:00
Carlos Valente 199dea5df6 fix(cuesheet): prevent timer cells from submitting on tab-out or escape (#2083) 2026-06-07 12:59:18 +02:00
Alex Christoffer Rasmussen 5a3ea9f7d0 fix: playwright ci test (#2094) 2026-05-28 09:50:20 +02:00
Alex Christoffer Rasmussen 9ae49a7175 chore: pnpm 11 + dependency cleanup (#2089)
* core: upgrade to pnpm 11 and nodejs to 22.22.2

* chore: allow builds

* chore: remove unused

* chore: update actions

* chore: add stub for vitest

* reafactor: small optimization
2026-05-17 21:01:16 +02:00
Carlos Valente d30d9cfd4e refactor: account for tick size on translate 2026-05-13 20:26:15 +02:00
Carlos Valente a0e7b9b665 refactor: simplify spacing between help menu items 2026-05-13 20:26:15 +02:00
Carlos Valente 1dcb7fb08d refactor: fix Info text flow 2026-05-13 20:26:15 +02:00
Carlos Valente a7e84b9903 refactor: fixed css editor size 2026-05-13 20:26:15 +02:00
Carlos Valente 3bebbf9ce9 refactor: invalidate changed rundown 2026-05-11 21:25:15 +02:00
Carlos Valente ab8da07518 refactor: improve memoisation on table rows 2026-05-11 21:25:15 +02:00
Carlos Valente c8bae76121 refactor: add empty state for autoamtion form 2026-05-11 20:59:50 +02:00
Carlos Valente 2873698959 bump version to 4.8.0 2026-05-11 19:55:33 +02:00
Cameron Slipp 741ebef90e refactor: improve automation UX 2026-05-11 19:55:01 +02:00
Claude 0741f46aca feat: add self-contained legacy timer viewer for old browsers
Adds timer-legacy.html — a zero-dependency static page that connects
to the Ontime WebSocket and displays the live countdown for browsers
that cannot parse the Vite-built React bundle (Safari < 14, Chrome < 85).

Also adds a synchronous pre-React guard in index.html that redirects
/timer visitors to the legacy page when Promise.any is unavailable,
matching the Vite build target floor of Safari 14 / Chrome 85.

https://claude.ai/code/session_013MfdwdjdDUnr3akpGSGXa4
2026-05-09 22:33:34 +02:00
Carlos Valente 5a33ffad92 refactor: improve background colour inheritance in safari 2026-05-09 22:32:04 +02:00
Claude 562f00dc7e fix: add legacy plugin to support iOS 14 2026-05-09 22:31:30 +02:00
Carlos Valente e50ab0150e docs: add link to youtube 2026-05-09 22:25:32 +02:00
Joel Wetzell fe44b33da2 Merge pull request #2078 from cpvalente/macos-icon
refactor: icon compatibility with macos tahoe
2026-05-09 08:15:08 -05:00
Joel Wetzell 22e392ba9d change macOS runner version in build workflow 2026-05-08 09:22:31 -05:00
Joel Wetzell 4997602e9b refactor: icon compatibility with macos tahoe 2026-05-07 20:23:26 -05:00
Joel Wetzell bdb8d4d89a Merge pull request #2077 from cpvalente/update-electron-builder
update electron-builder to v26.9.1
2026-05-07 16:24:39 -05:00
Joel Wetzell b1d5ee12e4 update electron-builder to v26.9.1 2026-05-07 07:50:49 -05:00
Alex Christoffer Rasmussen a5e733246d fix: format clock from preset (#2066)
* fix: format clock from preset

* fix: add to all views

* chore: format

* refactor: return flat value from common.options.ts
2026-05-03 18:43:58 +02:00
Alex Christoffer Rasmussen 1a08b39b8b feat: auto cue re-numbering (#2016)
* feat: update auto cue numbering

* feat: renumber from ui

* refactor: patchEntries is not used

* chore: format

* fix: correct cue at top of group

* fix: handle precision

* refactor dialog

* bump limit for performance time test

* extract type

* add class name to lable

* fix rebase

* refator: extract renumering logic

* chore: comments for getIntegerAndFraction function

* chore: add the for renumber mutation

* fix: fraction match precision

* refactor: small cleanup

* refactor: use more narrow validator

---------

Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
2026-05-03 18:39:00 +02:00
Carlos Valente ba1c3235b3 refactor: improve gsheet import UX 2026-05-03 18:17:17 +02:00
Alex Christoffer Rasmussen 7c1d2f4554 refactor: small cleanups (#2059)
* refactor: remove unneeded async

* chore: add express Router type to all routes

* refactor: don't use index in react key

* refactor: correctly get error message in excel route

* refactor: avoid exporting muteable values
2026-04-26 17:32:50 +02:00
Alex Christoffer Rasmussen 23a44b2f04 refactor: improve the amount of GET requests for translation assets (#2058)
* refactor: replace traslation hook refetch time with stale time

* refactor: make it the service responsebillety to send refetch keys for asset changes
2026-04-14 09:30:27 +02:00
Carlos Valente 2d3c8e7eb2 chore: update CI actions 2026-04-09 21:30:27 +02:00
Carlos Valente 2f138acad7 fix: extracted rundown entry editor shows correct colour 2026-04-09 06:51:18 +02:00
Carlos Valente 176e53dd7f refactor: scope electron build job 2026-04-06 21:49:12 +02:00
Carlos Valente 6a31d06c02 bump version to 4.7.0 2026-04-06 21:19:34 +02:00
Carlos Valente f739ba122b refactor: improve sheet flow management 2026-04-06 21:19:23 +02:00
Carlos Valente f53cb687bf refactor: improve sheet import UX 2026-04-06 21:19:23 +02:00
Carlos Valente 42dcf35f45 refactor: extract metadata from sheet 2026-04-06 21:19:23 +02:00
Carlos Valente 37dc9a620d feat: keep clock ticking on server drops 2026-04-06 20:17:05 +02:00
Brian Rodgers 9e771a2f24 refactor: format times in editor based on project time format 2026-04-06 19:50:41 +02:00
alex-arc b19e9d23ba feat: show current rundown in project info page
feat: swap project description for rundown title in editor and cuesheet overview
2026-04-06 19:50:08 +02:00
Carlos Valente 7e3a9d634b refactor: vertical titles for small timeline area 2026-04-06 19:49:30 +02:00
Carlos Valente 8c14c330f5 refactor: cache is rundown aware 2026-04-04 14:58:58 +02:00
Alex Christoffer Rasmussen 3022ec2acd feat: wide modal for cuesheet share and css editor (#2051) 2026-04-02 17:16:52 +02:00
Carlos Valente cb9528d894 refactor: create wide modal variant 2026-03-30 22:22:24 +02:00
Carlos Valente 97cb41dc5a refactor: composable info element 2026-03-30 22:22:24 +02:00
Carlos Valente 8985c8604f feat: create autocomplete component 2026-03-30 22:22:24 +02:00
Carlos Valente 2f95aa0c4f chore: update base-ui 2026-03-30 22:22:24 +02:00
Carlos Valente 5d0e96163f chore: update react hook form 2026-03-30 22:22:24 +02:00
Carlos Valente 5e98fdb2e9 fix: sanitise project data on load 2026-03-29 20:22:24 +02:00
Carlos Valente 23298bbe85 fix: resolve path to public assets in cloud 2026-03-28 17:15:46 +01:00
Carlos Valente cc48ac8fd3 fix: start does not reflow when playback starts 2026-03-27 21:27:49 +01:00
Carlos Valente 2d9909577e refactor: add confirmation dialogs for destructive actions 2026-03-27 19:40:24 +01:00
Carlos Valente 3838bdc54d fix: prevent hide index column from leaking between cuesheet and editor 2026-03-27 19:37:57 +01:00
Alex Christoffer Rasmussen c7d8b137ca chore: tooling (#2040)
* chore: only run release action if we have a tag

* chore: typescript 6 is out of beta

* chore: update flaky test

* chore: update pnpm

* chore: update playwright and node types

* chore: migrate tsup to tsdown

* chore: update node to node 22.22.2(LTS)

* chore: fix lockfile

---------

Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
2026-03-27 19:31:41 +01:00
Carlos Valente 3a0df0b491 refactor: introduce codemirror for CSS editor (#2039)
* refactor: introduce codemirror for CSS editor

* chore: fix formatting
2026-03-26 15:25:32 +01:00
Carlos Valente 2331205c93 chore: demo parallel rundowns 2026-03-25 18:42:23 +01:00
Carlos Valente 224d3600b2 fix: prevent race conditions on build (#2036)
* fix: prevent race conditions on build

* refactor: upload artifacts and keep for 1 day

* chore: remove deprecated settings

* Revert "refactor: icon compatibility with macos tahoe"

This reverts commit a7a78702b1.
2026-03-25 11:03:41 +01:00
Carlos Valente 7d469038bf fix: enable cjs interop while we migrate packages (#2034) 2026-03-25 10:49:43 +01:00
Carlos Valente 1c78397f18 chore: bump version to 4.6.0 (#2023) 2026-03-24 11:13:44 +01:00
Alex Christoffer Rasmussen 73eff20d08 chore: format (#2030)
* chore: format

* chore: add formatter in CI check

---------

Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
2026-03-24 09:49:22 +01:00
Carlos Valente 7e3aaa8c30 feat: UI access for custom views (#2020)
* style: consistent casing on menu

* refactor: bundle demo from code

* feat: allow uploading custom views
2026-03-24 09:19:45 +01:00
Carlos Valente 95536902f5 feat: Improve automation flow and add ontime playback actions (#2025) (#2024)
* fix: prevent add filter from submitting form

* refactor: event clarifies whether automations exist but are disabled

* refactor: improve readability of automation form

* refactor: add affordance for field warnings

* refactor: improve visibility of automation off state

* refactor: apply warning styles to other feature toggles

* Adding playback actions to automations (#2024)

* adding intial automation actions

* cleaning up

* fixing formatting

* ran oxfmt

* switching action names to playback- to match the dropdown strings

* fixing flicker that was caused by scroll arrows gettting unmounted

---------

Co-authored-by: Cameron Slipp <cdslipp@gmail.com>
2026-03-24 09:13:37 +01:00
Alex Christoffer Rasmussen cf206e6220 feat: Vite 8 (#2018)
* chore: upgrade vite

* fix: inconsistentCjsInterop for ReactQR

* chore: e2e tests should use relativve path and get host from playwright config

* refactor: drop qr code dependency

---------

Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
2026-03-24 09:11:57 +01:00
Alex Christoffer Rasmussen 4662cc8a26 feat: improve smart time entry (#2029)
Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
2026-03-23 21:23:36 +01:00
Cameron Slipp 8c52556cc0 chore: oxc info to DEVELOPMENT.md (#2027)
* Adding info on formatting and linting

* fixing md links
2026-03-23 15:57:14 +01:00
Carlos Valente b1da877dc7 refactor: consistent style for warning 2026-03-22 20:47:00 +01:00
Carlos Valente d8cef11a25 fix: server port settings has lifecycle 2026-03-22 20:47:00 +01:00
Carlos Valente ef5c0a371e refactor: improve app quit in windows 2026-03-21 18:50:17 +01:00
Carlos Valente b2b7855114 refactor: improve css editor loading suspense 2026-03-21 09:38:02 +01:00
Carlos Valente 129efc33b9 chore: upgrade prism dependencies 2026-03-21 09:38:02 +01:00
Joel Wetzell 6a40c66a7b upgrade vite and related dependencies (#2003) 2026-03-20 20:04:39 +01:00
Carlos Valente 2806177c72 fix: prevent wrapping in time inputs 2026-03-17 21:21:34 +01:00
Carlos Valente 4408bdb0d3 refactor: improve shutdown cleanup 2026-03-17 20:11:45 +01:00
Alex Christoffer Rasmussen 871a6b46c8 feat: automatic refetch css override (#1588)
* feat: send refect on css change

* feat: revamp ViewLoader

---------

Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
2026-03-17 20:08:33 +01:00
Carlos Valente fd39cab845 fix: prevent sheet name too long in excel export 2026-03-16 19:05:32 +01:00
Carlos Valente 1cee679397 fix: pending roll sets group expected times as due 2026-03-14 08:43:22 +01:00
Carlos Valente a7a78702b1 refactor: icon compatibility with macos tahoe 2026-03-13 08:10:48 +01:00
Carlos Valente 26d887c1a6 fix: prevent issues with midnight and day recognition on dst kick in 2026-03-11 07:20:42 +01:00
Alex Christoffer Rasmussen ceb490c60a fix: edit custom fields with type image (#1997) 2026-03-08 19:31:00 +01:00
Carlos Valente 1849b4d39f Deps migration (#1988)
* chore: migrate eslint to oxlint

* chore: migrate prettier to oxfmt

* chore: migrate typescript

* chore: toThrow should have a expected value

* chore: cast test value as Day

* chore: small title fix

* chore: mocks should be hoisted

* chore: incorrect async useage

* chore: test should be inside description

* chore: test sohuld include an expeced

* chore: oxfmt

---------

Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
2026-03-08 16:22:12 +01:00
alex-Arc 9516ac21bb fix: utf-8 from project file 2026-03-07 08:25:34 +01:00
alex-Arc 0e173137e8 add build-in translations 2026-03-07 08:25:23 +01:00
Joel Wetzell 1202ef3e80 add translation keys for timer message labels 2026-03-07 08:25:23 +01:00
Carlos Valente 2ef0b81743 refactor: extract midnight cross rules 2026-03-03 20:48:53 +01:00
Carlos Valente 5a5450d7ef refactor: use Day type in client 2026-03-03 20:48:53 +01:00
Carlos Valente 6f1f10ccff fix: backdate rundown start when rolling 2026-03-03 20:48:53 +01:00
Carlos Valente 5ff7861968 fix: use rundown day for calculations 2026-03-03 20:48:53 +01:00
Carlos Valente f1a1c358bc fix: infer day offset from epoch 2026-03-03 20:48:53 +01:00
Carlos Valente efd19353e9 fix: roll resets day 2026-03-03 20:48:53 +01:00
Carlos Valente a29a493678 fix: add current day when ticking over midnight 2026-03-03 20:48:53 +01:00
Carlos Valente 27b179276d fix: skip handles events across midnight 2026-03-03 20:48:53 +01:00
Carlos Valente 155cf48a17 fix: roll loads events that cross midnight 2026-03-03 20:48:53 +01:00
Alex Christoffer Rasmussen 358ad79ae4 Automation should use event store for data (#1962)
* fix: automation should use RuntimeStore for data

* add: aux timer template help
2026-03-02 20:54:46 +01:00
Alex Christoffer Rasmussen c1fcdf7065 Remove serverport from project file (#1957)
* feat: get server port from app satate or env

optional startup port from env will always override

function for parsing port from env

populate default port in app state

add test for migration

* bump version
2026-03-02 06:02:29 -08:00
Carlos Valente 1fe58e21be fix: onStart triggered by pending roll start 2026-02-28 10:32:19 +01:00
Carlos Valente 3c0e7ba4d5 chore: fix import across packages 2026-02-27 20:32:02 +01:00
Carlos Valente e8f159d894 feat: create branded types to determine different handling of time 2026-02-27 20:32:02 +01:00
Carlos Valente 168d7103b1 fix: prevent settings reflow on small width screens 2026-02-23 20:20:07 +01:00
Carlos Valente 63d7250a70 refactor: consistent focus styling 2026-02-23 20:19:49 +01:00
Carlos Valente 88f21e2186 refactor: improve timer styling in playback controls 2026-02-23 20:19:49 +01:00
Carlos Valente d9a18b0c42 fix: path comparison considers core params 2026-02-23 15:23:49 +01:00
Carlos Valente df588b8845 refactor: extract cuesheet permissions 2026-02-23 15:23:49 +01:00
Carlos Valente 5be504ce8f fix: prevent presets losing params 2026-02-23 15:23:49 +01:00
Carlos Valente 6268e327b9 refactor: mask cuesheet presets 2026-02-23 15:23:49 +01:00
Carlos Valente 1c81b3a23c refactor: hide edit button for users with no write permissions 2026-02-23 15:23:49 +01:00
Carlos Valente d28895ce9c refactor: prevent import map collision across stages 2026-02-23 15:22:56 +01:00
Carlos Valente 1a23287863 refactor: osc disabled by default to prevent collisions 2026-02-17 21:23:34 +01:00
Alex Christoffer Rasmussen 0b3ba65df3 feat: allow existing custom fields to keep its type on excel import (#1976) 2026-02-17 07:59:27 +08:00
Carlos Valente c3a7cd6a39 bump version to 4.4.2 2026-02-16 06:58:08 +01:00
Carlos Valente 39e6c15adf fix: hide now and next cards if there is no content 2026-02-16 06:58:08 +01:00
Carlos Valente aec6673b07 refactor: improve connection handling
- only invalidate caches on reconnect
- add jitter to avoid overwhelming server and allow cache to stabilise
- prevent multiple reconnect timers
2026-02-14 12:08:04 +01:00
Carlos Valente 9e8cb1c755 refactor: improve request cancellation and timeout handling 2026-02-14 12:08:04 +01:00
Carlos Valente cdf02f7c3f bump version to 4.4.1 2026-02-14 12:07:45 +01:00
Carlos Valente 77779a4648 refactor: debounce writing to disk 2026-02-14 09:30:24 +01:00
Carlos Valente e16ec3f388 refactor: prevent suspending page on first add 2026-02-14 08:56:16 +01:00
Carlos Valente 63cfc9ec19 refactor: optimistic add elements in UI 2026-02-14 08:56:16 +01:00
Carlos Valente 4827117163 refactor: extract entry creation logic 2026-02-14 08:56:16 +01:00
Carlos Valente 5777ef3532 refactor: extract parent resolution 2026-02-14 08:56:16 +01:00
Carlos Valente 80a3b04493 refactor: export rundown placement logic 2026-02-14 08:56:16 +01:00
Carlos Valente 7c5fdd3b19 refactor: remove unused exports 2026-02-14 08:56:16 +01:00
Carlos Valente 31fd8e5200 fix: prevent infinite render in settings forms 2026-02-13 21:12:29 +01:00
Carlos Valente b6a01a52e2 fix: table re-renders no longer lose cell focus 2026-02-08 17:41:05 +01:00
Carlos Valente f9563fca5f bump to version 4.4.0 2026-02-08 17:40:05 +01:00
Carlos Valente af1da58718 fix: update data in background rundowns 2026-02-08 17:39:53 +01:00
Carlos Valente 0f83ceaf01 fix: maintain lock on param changes 2026-02-08 17:17:25 +01:00
Carlos Valente aecf9575b1 refactor: link to settings section 2026-02-08 11:20:09 +01:00
Carlos Valente 773229ce4c refactor: align inputs with group, refs #1857 2026-02-08 11:20:09 +01:00
Carlos Valente da02ecd113 feat: editor layout 2026-02-08 11:20:09 +01:00
Carlos Valente 967e92e2c8 feat: extend rundown shortcuts 2026-02-08 11:20:09 +01:00
Carlos Valente d7af73d9ed feat: unify rundown features 2026-02-08 11:20:09 +01:00
Carlos Valente c5045ad82e refactor: table mode and context 2026-02-08 11:20:09 +01:00
Carlos Valente 8c22968bc9 fix: tsconfig references for utility tests 2026-02-08 11:20:09 +01:00
Carlos Valente 8aad271ffe refactor: create utility for enums 2026-02-08 11:20:09 +01:00
Carlos Valente b28573455a feat: create reusable scroll area 2026-02-08 11:20:09 +01:00
Carlos Valente e25725ea8b refactor: expose actions through context 2026-02-08 11:20:09 +01:00
Carlos Valente 1f8065143a refactor: improve context menu performance
- extract menu from rundown tree
- lazy create actions
2026-02-08 11:20:09 +01:00
Carlos Valente 8e32314667 refactor: improve performance in rundown
- leverage compiler
- correct subscriptions from zustand
2026-02-08 11:20:09 +01:00
Carlos Valente 0de55e74a8 feat: table mode in editor 2026-02-08 11:20:09 +01:00
Carlos Valente e5c5ec18d3 fix: automation form validation 2026-02-08 11:20:09 +01:00
Carlos Valente f9f6cb74ad chore: update npm link 2026-02-01 18:03:56 +01:00
Alex Christoffer Rasmussen 026e24bf84 Feat: unify view search params (#1947)
* feat: main src in backstage view

* feat: main src in timeline view

* feat: main src in countdown view

* feat: main src in studio view

* feat: hide past events in countdown view

* notes can not be set as main src

* feat: show group title as secondary src

* chore: spelling

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2026-01-27 20:42:07 +01:00
Joel Wetzell ec1890c275 update docker actions and pin to commit hash 2026-01-26 14:10:09 +01:00
Joel Wetzell 046f1176eb add docker metadata action to setup tags 2026-01-26 14:10:09 +01:00
Alex Christoffer Rasmussen 04c5b67475 fix: missing demo logo (#1954) 2026-01-26 13:52:29 +01:00
google-labs-jules[bot] 7b5f797c95 refactor: move welcome modal logic to AppStateService
The logic for deciding whether to show the welcome modal has been moved from `app.ts` to `AppStateService.ts`. The `getShowWelcomeDialog` function now accepts a boolean indicating whether a restore point exists and returns `false` if it does. This keeps the business logic out of the main application file and in the relevant service.
2026-01-24 07:45:56 +01:00
google-labs-jules[bot] 14f9472250 fix: hide welcome modal on restore
The welcome modal is now hidden if the application is started from a restore point.
2026-01-24 07:45:56 +01:00
Alex Christoffer Rasmussen efede83271 Fix: automation (#1943)
* Fix(automation): Handles undefined values in conditions (#1933)

* Fix(automation): Handles undefined values in conditions

Addresses an issue where conditions with the 'not_equals' operator incorrectly evaluated undefined values. This change ensures that empty string comparisons correctly identify missing values in automation rules.

* Fixes automation "not equals" logic

Simplifies the 'not_equals' condition evaluation in automations by reusing the 'equals' condition, improving code readability and consistency.

Fixes #1932

* Test(automation): Expanding filter condition testing

Expanding test cases to cover various data types and edge case for each operators.
Unexpected behavior have been mark with a TO_DO.

* Fix(automation): Handles default filter case

- Ensures that their is a default scenario.
- Fixing Deepsource issue "No default cases in switch statements JS-0047"

* Test(automation): Remove abstraction in test

* move to test.each

* extract a isEquivalent function

* lowercase contains test

---------

Co-authored-by: Philippe Allard-Rousse <philrousse@gmail.com>
2026-01-21 16:07:21 +01:00
Carlos Valente 904db95f59 docs: update linux links 2026-01-16 07:52:56 +01:00
Carlos Valente 525999440a fix: inline elements should not wrap by default 2026-01-14 17:12:03 +01:00
Carlos Valente 6e5805977a refactor: hide logo on error 2026-01-11 17:43:53 +01:00
Carlos Valente eb897a2a23 refactor: set background colour from content 2026-01-11 17:43:53 +01:00
Carlos Valente 05d997a52d bump version to 4.3.1 2026-01-11 17:43:23 +01:00
Carlos Valente f8cde5d627 fix: restore process sets currentDay 2026-01-10 19:23:49 +01:00
Alex Christoffer Rasmussen 1b55254834 fix: Network Interface overflow (#1935) 2026-01-05 21:05:45 +01:00
Alex Christoffer Rasmussen 6436e3fda8 refactor: extract rundown switching to a single function in rundown.service (#1930) 2026-01-05 20:47:04 +01:00
Alex Christoffer Rasmussen 680273770c fix: crash-log not written on windows and add log entries to crash report
* fix: crashlog not written on windows

* feat: dump last 100 log entries to crash report
2026-01-01 14:59:21 +01:00
Carlos Valente 75e77de466 fix: prevent infinite loop in project data 2025-12-26 08:42:53 +01:00
Carlos Valente 75cbcd8e85 bump version to 4.3.0 2025-12-22 16:57:52 +01:00
Carlos Valente 701f24cece fix: automation form validation 2025-12-22 16:57:52 +01:00
Carlos Valente 166160dda9 chore: upgrade client dependencies 2025-12-22 16:57:52 +01:00
Carlos Valente 9c5116f197 refactor: handle prefix on login
- post-login redirect should account for prefix
- scope cookies to each path by prefix
2025-12-22 15:59:38 +01:00
Alex Christoffer Rasmussen deb1f043c7 fix: set not initialised custom field from api (#1926) 2025-12-22 15:27:06 +01:00
Carlos Valente cbba19a9b1 fix: user can quick start project from partial data 2025-12-21 20:25:14 +01:00
Carlos Valente a5daa76616 refactor: improve merge logic 2025-12-21 20:25:14 +01:00
Carlos Valente 08b8e73393 refactor: finish view directory migration 2025-12-21 13:32:12 +01:00
Carlos Valente 2f3cf9825a refactor(css): migrate use of deprecated import in messagecontrol 2025-12-21 13:32:12 +01:00
Carlos Valente b0f02e726d refactor(css): migrate use of deprecated import in cuesheet 2025-12-21 13:32:12 +01:00
Carlos Valente ac40292813 refactor(css): migrate use of deprecated import in operator 2025-12-21 13:32:12 +01:00
Carlos Valente 2ac00ee8cc refactor(css): migrate use of deprecated import in buttons 2025-12-21 13:32:12 +01:00
Carlos Valente 972f9e35fd refactor: create separation between editor wrapper and contents 2025-12-21 13:32:12 +01:00
Carlos Valente 1d7ee163a0 Revert "feat: display offline indicators in all views"
This reverts commit 71b2fa8e21.
2025-12-20 15:25:15 +01:00
Carlos Valente 1054ec0a5e refactor: expose missing types 2025-12-20 14:18:07 +01:00
Carlos Valente fc2e76db0a refactor: stabilise filtering function 2025-12-19 09:54:19 +01:00
Carlos Valente ef3d541eb3 refactor: simplify element lookup 2025-12-19 09:54:19 +01:00
Carlos Valente 8daf3313f2 refactor: simplify flat rundown 2025-12-19 09:54:19 +01:00
Carlos Valente 93c913ec5a refactor: remove placeholderData pattern 2025-12-19 09:54:19 +01:00
Carlos Valente 71b2fa8e21 feat: display offline indicators in all views 2025-12-19 08:51:54 +01:00
Carlos Valente f2b78e0f32 refactor: generate ids for new rundowns 2025-12-18 14:42:01 +01:00
Carlos Valente b70ef80320 refactor: patch does not load rundown 2025-12-18 14:42:01 +01:00
Carlos Valente 426d42ab63 refactor: add guards around trivial index checks 2025-12-18 14:42:01 +01:00
Carlos Valente 242cefd35c tooling: lax typechecking in tests 2025-12-18 14:42:01 +01:00
arc-alex 6beda1575a chore: marke change api call async 2025-12-16 21:56:39 +01:00
arc-alex ba75d52d29 chore: marking a functions as throwing 2025-12-16 21:56:39 +01:00
arc-alex 35a4361ae9 fix: expectedFinish flashing incorrect value when adding timer in overtime 2025-12-16 21:56:39 +01:00
arc-alex 77225383a4 fix: block uploading already existing file name 2025-12-16 21:56:39 +01:00
arc-alex f0e81c0cf0 fix: stop timer on rundown switch 2025-12-16 21:56:39 +01:00
Joel Wetzell 9355d7bf27 feat: add arm architectures to linux target 2025-12-15 20:03:57 +01:00
Carlos Valente cd39227af7 fix: prevent unsupported wakelock 2025-12-09 19:13:36 +01:00
Carlos Valente 14e21214af fix: subdue paused only for running timers 2025-12-09 19:13:36 +01:00
Carlos Valente 03184b0321 fix: prevent accessing undefined index 2025-12-09 19:13:36 +01:00
Carlos Valente cd5af769ab fix: undefined flatRundown 2025-12-09 19:13:36 +01:00
Carlos Valente d116670318 fix: safe copy to clipboard 2025-12-09 19:13:36 +01:00
Carlos Valente 9ba5ebd4bd bump version to 4.2.1 (#1896) 2025-11-28 16:26:53 +01:00
Carlos Valente 3f1f06f7c5 Improve Clone (#1897)
* refactor(rundown): optimise copy-paste performance

* chore: configure opt-in compiler

* refactor(rundown): stabilise frequently accessed data

* feat(clone): allow cloning any element

* remove paste above and cue increment from test

---------

Co-authored-by: arc-alex <ac@omnivox.dk>
2025-11-28 16:20:52 +01:00
arc-alex a78586d2fa allow count up to show negative value 2025-11-28 15:03:28 +01:00
arc-alex 8e93b0af25 fix: countToEnd should not override display options 2025-11-28 15:03:28 +01:00
Carlos Valente 9a5d50d933 fix(wakelock): check for feature support 2025-11-28 11:26:23 +01:00
Carlos Valente 1189a94bff refactor(wakelock): simplify logic into hook 2025-11-28 11:26:23 +01:00
Alex Christoffer Rasmussen f128f7acd2 fix: api change custom fields (#1888)
* fix: validate nested custom path

* chore: add test
2025-11-27 09:15:02 +01:00
Carlos Valente 80ec2d1186 refactor(pip): match timer formatting defaults 2025-11-26 20:10:01 +01:00
Carlos Valente 18e6f0d7c4 refactor(timer): allow multiline secondary text 2025-11-26 20:10:01 +01:00
Alex Christoffer Rasmussen 48f2511764 fix: custom fields not persisted (#1891) 2025-11-26 17:58:00 +01:00
Carlos Valente c20ea88021 docs: add link to subreddit 2025-11-20 18:44:23 +01:00
Carlos Valente ce2b2b9606 bump version to 4.2.0 2025-11-20 18:44:23 +01:00
Alex Christoffer Rasmussen f223766445 feat: pip for timer preview (#1862)
---------

Co-authored-by: Shobhit Nagpal <74096450+Shobhit-Nagpal@users.noreply.github.com>
Co-authored-by: Carlos Valente <carlosvalente@pm.me>
2025-11-20 18:00:16 +01:00
Carlos Valente 3dfb49e63f refactor(overview): prevent vertical scrollbar 2025-11-20 17:58:53 +01:00
Carlos Valente a13d3dad93 refactor(op): style tweaks to tags and subscriptions 2025-11-20 17:58:53 +01:00
Carlos Valente fa9695873a refactor(timeline): style tweaks on status tags 2025-11-20 17:58:53 +01:00
Carlos Valente 70ce1d5553 feat(projects): allow sorting project table 2025-11-19 06:52:58 +01:00
Shobhit Nagpal 9974ce24cd refactor(operator): hide groups where all events have passed (#1878) 2025-11-18 06:12:02 +01:00
Carlos Valente 93e9c19e0d refactor(input): consistent placeholder colours 2025-11-16 07:44:49 +01:00
Carlos Valente 1694cca92c refactor(settings): improve error message on version fetch 2025-11-16 07:44:49 +01:00
Carlos Valente c1d9276dd5 refactor(timeline): style tweaks to small screens 2025-11-16 07:44:49 +01:00
Carlos Valente f5753a0f5a refactor(timeline): default to fixed size timeline 2025-11-16 07:44:49 +01:00
Carlos Valente 843f5a968d refactor(operator): style tweaks for small screens 2025-11-16 07:44:49 +01:00
Carlos Valente e09b6bbc16 refactor: close menu in small screen navigation 2025-11-16 07:44:49 +01:00
Carlos Valente 19d6b40bdb refactor: hide fullscreen button if not feature available 2025-11-16 07:44:49 +01:00
Carlos Valente 1f2706a063 refactor(operator): improvements to editing 2025-11-16 07:44:49 +01:00
Carlos Valente aaeadd13cf chore(operator): clarify edit prompt 2025-11-16 07:44:49 +01:00
Carlos Valente 72e3eb11d7 chore: upgrade mantine dependencies 2025-11-16 07:44:49 +01:00
Alex Christoffer Rasmussen 6f55d8a6c9 use data in <base> componenet to mark ontime cloud status (#1872) 2025-11-12 19:28:29 +08:00
Shobhit Nagpal 70846aefbe refactor: time input labels on hover (#1855) 2025-11-11 06:42:55 +01:00
Carlos Valente 47ba90f191 refactor: ensure preset names are url safe 2025-11-11 06:41:48 +01:00
Alex Christoffer Rasmussen cee3c9c070 Fix integration api (#1866)
* allow change api to set flag and countToEnd

* fix: custom fields can be uppercase

* check that prop exists and add target duration for group
2025-11-09 14:36:13 +01:00
Carlos Valente 15f3b9625c refactor: review overview styles 2025-11-09 06:44:24 +01:00
Carlos Valente d34a1f0fd8 chore: improve documentation on the 12 hour view 2025-11-09 06:44:24 +01:00
Carlos Valente 0e3b3bcf9a refactor: consistent time formatting for 12h mode 2025-11-09 06:44:24 +01:00
Carlos Valente 50c91f976b refactor: show welcome modal on connect 2025-11-02 18:58:13 +01:00
Carlos Valente 50599eccd2 refactor: show countdown if less than 10 min from start 2025-11-02 18:53:03 +01:00
Carlos Valente 9f2db10548 fix: prevent rounding error show 60s 2025-11-02 18:53:03 +01:00
Carlos Valente 5dce1a40bc refactor: prevent ids from being overriden 2025-11-02 13:27:53 +01:00
Carlos Valente b75b69c3b1 chore: cleanup directory structure 2025-11-02 13:23:05 +01:00
Carlos Valente e6070e6efd refactor: add rundown summary to import preview 2025-11-02 13:23:05 +01:00
Carlos Valente 3918664945 fix: ensure IDs on import from excel 2025-11-02 13:23:05 +01:00
Carlos Valente b65b3fa6d2 refactor: get worksheet names from excel upload 2025-11-02 13:23:05 +01:00
Carlos Valente 162693e19e refactor: allow user to review import map 2025-11-02 13:23:05 +01:00
Carlos Valente becd734ea8 refactor: simplify ux for sheet import 2025-11-02 13:23:05 +01:00
Carlos Valente 9dfc8083f0 style: run code formatting 2025-10-31 19:30:34 +01:00
Carlos Valente 7bd98c23a4 bump version to 4.1.0 2025-10-31 19:30:34 +01:00
arc-alex 6f19d78e53 addedTimer always updates the restore point 2025-10-31 19:26:30 +01:00
arc-alex 163baa752f keep restorpoint on container shutdown 2025-10-31 19:26:30 +01:00
mdavies1 6f708df6f6 feat: lock extracted rundown panels 2025-10-31 19:23:08 +01:00
Carlos Valente eb5c62cca1 chore: simplify prettier setup 2025-10-30 17:26:49 +01:00
Carlos Valente 99d2debca8 typo: incorrect title in delete confirmation 2025-10-30 17:26:49 +01:00
Carlos Valente 74e59dccdb feat: allow renaming rundown 2025-10-30 17:26:49 +01:00
Carlos Valente 812b17a221 refactor: create destructive menu type 2025-10-30 17:26:49 +01:00
Carlos Valente 32bf0f53f6 feat: allow user to duplicate a rundown 2025-10-30 17:26:49 +01:00
Carlos Valente 17f80baf48 chore: improve rundown API documentation 2025-10-30 17:26:49 +01:00
Carlos Valente e0377a7244 feat: implement simple healthcheck endpoint 2025-10-30 17:26:18 +01:00
Alex Christoffer Rasmussen b7e5723fb2 Fix multiday (#1850)
* chore: cleanup typeings

* fix: total gap should only accumulate after the curent loaded

* fix: add the current day of the event to start day offset
2025-10-30 14:22:14 +01:00
Alex Christoffer Rasmussen 897210d5e3 add timer type for css override selector (#1845) 2025-10-30 13:15:14 +01:00
Alex Christoffer Rasmussen ee3d38fce8 Fix link to host in local network (#1843)
* descripe testing for cloud

* use base url directly

* lint
2025-10-27 11:19:21 +01:00
Carlos Valente 37abd3fe47 chore(ci): prevent publishing resolver on release 2025-10-22 06:26:35 +02:00
Carlos Valente b509ee5b1f bump to version 4.0.2 2025-10-22 06:26:35 +02:00
Shobhit-Nagpal dd2b59efe5 fix: move blue line to pseudo element 2025-10-21 16:29:59 +02:00
Shobhit-Nagpal 5788785b3e fix: minor ui shift on quick add inline 2025-10-21 16:29:59 +02:00
Carlos Valente e488ad19b3 fix: ensure well formed URLs for IP address destinations 2025-10-18 15:29:16 +02:00
Carlos Valente cbc03c898c fix: correct alignment to end message 2025-10-18 15:28:47 +02:00
jwetzell 155bfbdc42 Merge pull request #1833 from cpvalente/fix/npm-action-access-error 2025-10-18 07:13:05 -05:00
jwetzell 5d3714a4fc Merge pull request #1836 from cpvalente/fix/use-flat-rundown 2025-10-18 07:12:39 -05:00
jwetzell a8a1d210d8 use flat order when building flat rundown 2025-10-17 21:46:13 -05:00
jwetzell 49de8b63a7 add setup-node step back to resolve npm access issues 2025-10-17 08:13:11 -05:00
Carlos Valente 03c9f51b47 chore: update demo project 2025-10-17 12:20:18 +02:00
Carlos Valente 8d00fd3966 bump version to 4.0.1 2025-10-17 12:20:18 +02:00
Carlos Valente 80fd21354e fix: apply styling to overtime timer 2025-10-16 21:13:26 +02:00
Carlos Valente a87b15825a fix: end message scaling 2025-10-16 21:13:26 +02:00
Carlos Valente 6a8f98c469 fix: end message only shows if timer is in overtime 2025-10-16 21:13:26 +02:00
jwetzell 8685298430 Merge pull request #1830 from cpvalente/fix/linux-project-download 2025-10-15 07:25:57 -05:00
jwetzell d43af2db57 pass options to project download to allow folders beginning with a dot 2025-10-14 19:51:15 -05:00
jwetzell ef718c48e4 Merge pull request #1828 from cpvalente/npm-trusted-publisher
update cli and resolver workflows for new NPM auth
2025-10-14 16:18:53 -05:00
Alex Christoffer Rasmussen 45e7373279 Small clean-up (#1827)
* chore: avoid shadow variable name

* chore: map could just be foreach

* clean uneeded template literal

* lint
2025-10-13 20:24:25 +02:00
jwetzell 6af2d6e021 update cli and resolver workflows for new NPM auth 2025-10-12 14:11:52 -05:00
Alex Christoffer Rasmussen 9b36f36f73 fix: next cue (#1821)
* fix next cue

* Update apps/server/src/services/runtime-service/runtime.service.ts

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-10-12 14:42:55 +02:00
Carlos Valente 3a0dbf057e chore: update docs 2025-10-12 07:48:25 +02:00
Carlos Valente dae84ab48a chore: update screenshots 2025-10-12 07:48:25 +02:00
Carlos Valente aa927597f3 fix: product name ontime 2025-10-10 19:03:53 +02:00
Carlos Valente ef7f0fb54a refactor: bump modal sizes 2025-10-10 18:32:29 +02:00
Carlos Valente 324320fdc8 fix: custom field is multiline 2025-10-10 18:32:29 +02:00
Carlos Valente 4b27d080b6 fix: background colour in not found page 2025-10-10 18:13:58 +02:00
Carlos Valente 7918063753 bump version to 4.0.0 2025-10-10 18:13:58 +02:00
arc-alex e2202c2417 fix: dynamic full hight for mobile view param editor 2025-10-10 18:07:39 +02:00
Carlos Valente 9817c8f8c0 fix: selected styling overrides 2025-10-10 17:41:26 +02:00
Carlos Valente 1d8c04d0b4 feat: export excel file
refactor: align id columns between export and import

refactor: match column names with template
2025-10-10 17:41:26 +02:00
Carlos Valente 10762f10bf docs: caveat on proxy not working before project build 2025-10-10 17:41:26 +02:00
Alex Christoffer Rasmussen da7c0d4ff2 Small clean-up (#1815)
* chore: remove old comment

* chore: smal lclean up of a e2e test

* chore: bump to beta.6

* chore: cleanup imports

* fix integration values com in ms

* allow linkStart and timeStrategy from API

* remove local mac dist
2025-10-10 17:37:55 +02:00
Carlos Valente 4a27973de1 refactor: allow locking op settings and navigation 2025-10-10 16:01:41 +02:00
Carlos Valente bdeb087e28 fix: op gets params from preset context 2025-10-10 16:01:41 +02:00
Carlos Valente 3662cd58f5 refactor: extract operator data loading 2025-10-10 16:01:41 +02:00
Carlos Valente 62c52bca07 refactor: larger secondary field in timer 2025-10-09 21:03:22 +02:00
Carlos Valente 1fc4a06133 fix: allow user to reset schedule filter 2025-10-09 21:03:22 +02:00
Alex Christoffer Rasmussen 147862e647 Fix opview with groups (#1814)
* create failing test

* fix: op evet used past state of the group
2025-10-08 21:19:57 +02:00
Alex Christoffer Rasmussen 12bc323e57 Fix build (#1813)
* fix resolver build

* fix docker build

* node version in docker via arg

* add step to setup auth for pnpm

* lint

---------

Co-authored-by: Joel Wetzell <jwetzell@yahoo.com>
2025-10-08 10:44:53 +02:00
Alex Christoffer Rasmussen de8f9ef587 Merge pull request #1535 from cpvalente/v4
v4
2025-10-07 12:51:09 +02:00
arc-alex 217402050d fix merge issue 2025-10-07 12:44:06 +02:00
Alex Christoffer Rasmussen de75d55849 Merge branch 'master' into v4 2025-10-07 12:39:06 +02:00
Alex Christoffer Rasmussen c4e6215b29 Update test time until (#1809)
* test views

test timeline

* timeline show seconds when less than 2m

* fix: countown typings

* refactor: backstage schedule item

* chore: lint
2025-10-07 12:32:12 +02:00
Alex Christoffer Rasmussen 57315595a5 All views follow offset mode (#1808)
* chore: remove unused

* fix: studio view follow offset mode
2025-10-07 12:32:12 +02:00
Alex Christoffer Rasmussen fc7ed6bd20 clean turbo config and cache github actions (#1805)
* refactor: turbo scripts

refactor dev and build scripts

remove cleanup

* fix e2e script

* cache playwright

* cache pnpm

* fix docker env in vite

* fixup! refactor: turbo scripts

* chore: add comment
2025-10-07 12:32:12 +02:00
jwetzell 750927dc8d chore: switch workspaces to use pnpm to setup node with useNodeVersion value 2025-10-07 12:32:12 +02:00
Carlos Valente 00d2ae0a7c refactor: clarify settings section name 2025-10-07 12:32:12 +02:00
Carlos Valente 56c4d68bae refactor: use coffee icon for keep awake feature 2025-10-07 12:32:12 +02:00
Carlos Valente a02cdf71c0 fix: maintain offset on roll takeover 2025-10-07 12:32:12 +02:00
Carlos Valente 25030debe4 refactor: error page sends email 2025-10-07 12:32:12 +02:00
Carlos Valente 27243c4432 chore: upgrade electron packages 2025-10-07 12:32:12 +02:00
Alex Christoffer Rasmussen 22559c59d4 Expected time for views (#1798)
* refactor: use correct metadata in op view

* refactor: use correct metadata in backstage view

* remove uneeded test

* fix: correct offset colour

* refactor: use correct metadata in timline view

* refactor: schedule item dont pass the whole event

* chore: lint
2025-10-07 12:32:12 +02:00
Carlos Valente 700948bfbb fix: use correct order when creating groups 2025-10-07 12:32:12 +02:00
Carlos Valente 960a39fbf4 refactor: cleanup dragging cursor indicator 2025-10-07 12:32:12 +02:00
Carlos Valente d986f438d9 refactor: improve delay component 2025-10-07 12:32:12 +02:00
Carlos Valente c2012bd9e3 refactor: improve studio clock responsiveness 2025-10-07 12:32:12 +02:00
Carlos Valente 1f0f92371f fix: show secondary timer only if active 2025-10-07 12:32:12 +02:00
Carlos Valente 4b81725e80 chore: upgrade base-ui 2025-10-07 12:32:12 +02:00
Carlos Valente 846eaf569d refactor: remove project CSV export option 2025-10-07 12:32:12 +02:00
Carlos Valente ccf1ddf2fd fix: conflict between swatch picker and swatch elements 2025-10-07 12:32:12 +02:00
Carlos Valente 81e00993f2 fix: prevent rolling into events of 0 duration 2025-10-07 12:32:12 +02:00
Alex Christoffer Rasmussen e22c7698c3 fix: alpha bg (#1799) 2025-10-07 12:32:12 +02:00
arc-alex ef053fc2f7 bump to v4.0.0-beta.5 2025-10-07 12:32:12 +02:00
Alex Christoffer Rasmussen 3b6d3a4631 feat: Keep screen awake (#1788)
* feat keepAwake hook

* reaqure lock after view change

* refactor: move it to a navigation view setting

* save to search params
2025-09-26 21:34:01 +02:00
Alex Christoffer Rasmussen 31c3e603ed refactor: better type for api aux action (#1796) 2025-09-26 21:34:01 +02:00
Alex Christoffer Rasmussen 1dfe5c980d refactor: collect common tsconfig (#1794)
* refactor: collect common tsconfig settings

* chore: module and target should be lower case
2025-09-26 21:34:01 +02:00
Alex Christoffer Rasmussen 80e4dc0941 Fix: rundown import (#1795)
* fix: patch in the active rundown id and title

* fix: send a ALL refetch on project patch

* fix: load the last used rundown on project patch and not just the first one

* chore: lint

* chore: clean up and let rundown service send the refetch request

* chore: dont destructure rundown
2025-09-26 21:34:01 +02:00
Alex Christoffer Rasmussen bc0b83aa14 remove private field from package (#1793) 2025-09-26 21:34:01 +02:00
Alex Christoffer Rasmussen 971c6c66f0 Chore: Small todo about coerce boolean (#1789)
* fix: coerce boolean types negative number is false

* strict coerce
2025-09-26 21:34:01 +02:00
Carlos Valente 343697dc93 refactor: remove unused dependencies 2025-09-26 21:34:01 +02:00
Carlos Valente 4058231d6b refactor: remove unused code 2025-09-26 21:34:01 +02:00
Carlos Valente c5ff254267 refactor: prevent upload logo errors from being stuck 2025-09-26 21:34:01 +02:00
Carlos Valente ac14cb9624 refactor: increase file size limits 2025-09-26 21:34:01 +02:00
Carlos Valente bcabc50ad1 fix: check automation usage in all rundowns before allowing delete 2025-09-26 21:34:01 +02:00
Carlos Valente cbd7bce106 bump version to 4.0.0-beta.4 2025-09-26 21:34:01 +02:00
Carlos Valente 00aa8a09fa refactor(roll breaking): make starting with roll backtrace planned times 2025-09-21 07:42:47 +02:00
Carlos Valente d04f958c11 fix: initialise group time 2025-09-21 07:42:47 +02:00
Alex Christoffer Rasmussen 5716fc31cf feat: export package to help api consumers (#1624) 2025-09-21 07:42:47 +02:00
Carlos Valente a5d628af0c fix: prevent params drawer from being larger than screen 2025-09-21 07:42:47 +02:00
Carlos Valente 111a1f95ba fix: correct offset direction in expected time calculations 2025-09-21 07:42:47 +02:00
Carlos Valente 4bf74d86b3 refactor: set start metadata when starting from roll 2025-09-21 07:42:47 +02:00
arc-alex d2638d64e4 fix: group end elements should only send timertype data nothing else 2025-09-21 07:42:47 +02:00
arc-alex 02cddeec4d refactor: add a try grourd to the cell data generator to better pinpoint errors 2025-09-21 07:42:47 +02:00
Carlos Valente a8fe8b5f1e refactor: allow filtering schedule 2025-09-21 07:42:47 +02:00
Carlos Valente 8d2db7ffcd Relative mode (#1764)
* feat: add group start time to state

* refactor: calculate expected times when offset mode changes

* feat: show relative data in ui

* fixup! feat: add group start time to state

* fixup! refactor: calculate expected times when offset mode changes

* fixup! feat: show relative data in ui

* try to handle multiday

* show planed/expected rundown end day offset values

* refactor: style tweaks to timers

* refactor: cleanup data use

* day offset

* update tests

* test getExpectedStart multiday

* write start epoch to restore file

* current day in relative mode

* remove todo

* move find day offset to utils

---------

Co-authored-by: arc-alex <ac@omnivox.dk>
2025-09-21 07:42:47 +02:00
Alex Christoffer Rasmussen 313590905f fix docker ignore (#1780) 2025-09-21 07:42:47 +02:00
Carlos Valente 7bbdab7dd9 fix(cuesheet): infinite render loop on resizing columns 2025-09-21 07:42:47 +02:00
Carlos Valente 304832ecde fix: maintain multiline in cuesheet cells 2025-09-21 07:42:47 +02:00
Carlos Valente 0ab4abfcfe docs: add contribution guidelines 2025-09-21 07:42:47 +02:00
Carlos Valente 1847822f31 fix: skip nested events 2025-09-21 07:42:47 +02:00
Carlos Valente 4499495212 feat: allow finding milestones 2025-09-21 07:42:47 +02:00
Carlos Valente 0035070db8 refactor: improve automated following 2025-09-21 07:42:47 +02:00
Carlos Valente 890a97d071 fix: allow moving a group after another 2025-09-21 07:42:47 +02:00
Carlos Valente 5441e5b07f refactor: improve pin styling 2025-09-21 07:42:47 +02:00
Carlos Valente ab4b94f2d8 fix: prevent layout reflow on different entry types 2025-09-21 07:42:47 +02:00
Carlos Valente fd7ccb4a5b refactor: add default value to secondary source 2025-09-21 07:42:47 +02:00
Carlos Valente 2e6bf234d5 fix: phase style overrides in timer 2025-09-21 07:42:47 +02:00
Alex Christoffer Rasmussen c934b0ecdd cleanup (#1776)
* chore: cleanup leftover console log

* chore: prevent error in client tsconfig
2025-09-21 07:42:47 +02:00
Alex Christoffer Rasmussen cd6d6f24eb fix: sheet default import map (#1775)
* fix: default import map

* fix: update test
2025-09-21 07:42:47 +02:00
arc-alex e632a08557 chore: bump version 2025-09-21 07:42:47 +02:00
Carlos Valente 0057b4a3f1 refactor: tweaks to countdown styles 2025-09-08 06:23:39 +02:00
Carlos Valente ac8c1e333e bump version to 4.0.0-beta.2 2025-09-08 06:09:31 +02:00
Carlos Valente 321297b685 fix: redirect to preset 2025-09-08 06:09:00 +02:00
Alex Christoffer Rasmussen 67e67d6a5d feat: propper timeuntil calc in countdown view (#1765)
* fix error with is linked to loaded metadata

* pass down metadata

* create expected schedule

* expected start in sub info

* for singel subscribtions

* refactor

* add color to single events

* refactor: clean up subscribtion data

* show schedule and secondray data for single events

* improve subscribtion data display
2025-09-07 21:49:45 +02:00
Carlos Valente 4385e0b9c9 refactor: ensure clocks timer and clock are in sync (#1772)
* refactor: ensure clocks timer and clock are in sync

---------

Co-authored-by: arc-alex <ac@omnivox.dk>
2025-09-07 21:49:45 +02:00
Carlos Valente 02243242f1 fix: redirect to preset 2025-09-07 21:49:45 +02:00
arc-alex 1af42b766d fix: insert group-end where needed 2025-09-07 21:49:45 +02:00
arc-alex 9d6ec3758b chore: clarifying comments 2025-09-07 21:49:45 +02:00
Carlos Valente 9e78ff4d1c refactor: export data for all entry types 2025-09-07 21:49:45 +02:00
Carlos Valente 2037aef080 fix: prevent clipping on bold font sizes 2025-09-07 21:49:45 +02:00
Carlos Valente 48b9cf042f refactor: lax validation for settings
with this changes we allow the client and server to share the same data
shape. even if some of it is useless.
we also get an extra version property in the payload, if we want to save
the transfer cost, we would rather remove it from the sent payload
2025-09-07 21:49:45 +02:00
Carlos Valente 1866e5e78d refactor: simplify restore point 2025-09-07 21:49:45 +02:00
Carlos Valente bc1b3800c8 refactor: op clock uses time format string 2025-09-07 21:49:45 +02:00
Carlos Valente a2505c838b refactor: add none option to optional select 2025-09-07 21:49:45 +02:00
Carlos Valente bffa863fb4 fix: multi select infinite loop 2025-09-07 21:49:45 +02:00
Carlos Valente d00d7677bc fix: merge virtuoso and rundown styles 2025-09-07 21:49:45 +02:00
Carlos Valente 3ae73be562 refactor: prevent emitting errors from user pages 2025-09-07 21:49:45 +02:00
Carlos Valente 763080cd4d fix: generate link for companion 2025-09-07 21:49:45 +02:00
Carlos Valente 6ff06b21a8 fix: hide fade out elements by default 2025-09-07 21:49:45 +02:00
Carlos Valente adebde1b43 chore: upgrade client packages 2025-09-07 21:49:45 +02:00
Carlos Valente 89922fe394 revert: fixed size for editor 2025-09-03 17:48:45 +02:00
Alex Christoffer Rasmussen f219ebb8f8 refactor: settings route (#1753)
* refactor: settings route
2025-09-03 17:48:45 +02:00
Carlos Valente eace97ec7a bump version to 4.0.0-alpha.6 2025-09-03 17:48:45 +02:00
Carlos Valente ec1df8b958 fix: bundle translations 2025-09-03 17:48:45 +02:00
Carlos Valente 8d2d8ef979 feat: add user defined translations (#1756)
* feat: add user defined translations

* add refetch key for translation (#1757)

---------

Co-authored-by: Alex Christoffer Rasmussen <ac@omnivox.dk>
2025-09-03 16:06:03 +02:00
Carlos Valente ae15f3cdc5 Alpha 5 (#1741)
* refactor: align header columns

* refactor: improve scrollbar visibility

* refactor: center align table elements

* refactor: make param elements stateful

* fix: issue with collapsed elements not loosing value

* fix: prevent search params containing multiple alias references

* fix: the issue where a file disappears if it is both migrated and recovered in the same load operation (#1744)

* refactor: disable group action for elements in groups

* refactor: move context menu items into the event element (#1747)

* feat: sheet import new features for v4 (#1730)

* import milestone

* fixup! import milestone

* test: milestone import

* add entries to group

stop on new group or on group-end type

* fixup! add entries to group

* cleanup

* add event target duration

* link start if undefined

* add skip import type

* extract some to the excel paresing functions

* tweaks to presentation

* move file

---------

Co-authored-by: Carlos Valente <carlosvalente@pm.me>

* fix: notify runtimeStore of events bieng groupd

* fix: improve authentication and stage detection in demo

* chore: ship logo with project

* fix: client is referenced by name

* fix: prevent reflow in event editor

* fix: stale render on selected event due to ref mismatch

* Create/Load/Delete multiple rundowns (#1696)

* refactor: restore last loaded rundown

* refactor: initialise rundown in ProjectService

* feat: allow switching rundowns

ensure on coordination between the db object and the working object

server provide list of rundowns

implement switch in the UI

implement delete

implement new rundown button

* fix: render order for floating button

* refactor: appropriate names to service

* refactor: rundown endpoints

* refactor: save last loaded rundown ID

* refactor: rundown management UI

* refactor: emit refetch all on project load

---------

Co-authored-by: Carlos Valente <carlosvalente@pm.me>

* feat: recover single event subscription

* fixup! feat: sheet import new features for v4 (#1730)

* fix: prevent dropping a group inside another

* fixup! refactor: move context menu items into the event element (#1747)

* fix: prevent stale references to custom fields

* fix: propagate updates to all rundowns

* refactor: client rundown metadata (#1728)

* generate metadata in the hook

* move test

* ensure there is always a last element

* use for-loop

* update metadata in useEfect

* fully extract metadata generation

* use direct assignment

* cleanup

---------

Co-authored-by: Carlos Valente <carlosvalente@pm.me>

* refactor: small imporvement and tests for coerce functions (#1752)

* refactor: small imporvement and tests for coerce functions

add test `coerceString`

add test `coerceBoolean`

add test `coerceColour`

* remove old todo

* fix: consistent quick add behaviour

* refactor: create flat rundown with metadata

* fix: show add buttons on top

* feat: allow editing milestones

* refactor: style tweaks to rundown elements

refactor: milestones are full width

refactor: cuesheet header alignment

fix: editor styling in cuesheet

* refactor: virtualise table

* refactor: improve overscan (#1758)

* bump version to 4.0.0-alpha.5

---------

Co-authored-by: Alex Christoffer Rasmussen <ac@omnivox.dk>
2025-09-03 15:50:20 +02:00
Carlos Valente 3c41b40c5f refactor: create fast tracks for cookie parsing 2025-08-18 22:00:16 +02:00
arc-alex 3b38756dda fix: use new token with scope 2025-08-18 22:00:16 +02:00
Carlos Valente 5a4494ba85 refactor: improve timers visibility 2025-08-18 22:00:16 +02:00
Carlos Valente 02ad852dd4 fix: external demo connects to hash 2025-08-18 22:00:16 +02:00
Carlos Valente 7ce1097826 fix: prevent reflow between entry editors 2025-08-18 22:00:16 +02:00
Carlos Valente 7ee0de5341 docs: improve development documentation 2025-08-18 22:00:16 +02:00
Carlos Valente 0db249871b bump version to 4.0.0-alpha.4 2025-08-18 22:00:16 +02:00
Carlos Valente 87df656470 docs: demo project is runtime data documentation 2025-08-18 06:16:53 +02:00
Carlos Valente 54f07c4330 refactor: simplify messages 2025-08-18 06:16:53 +02:00
Carlos Valente ae061cb69a bump version 4.0.0-alpha.3 2025-08-18 06:16:53 +02:00
google-labs-jules[bot] ba529def34 fix(client): use fresh state in row observer to prevent stale closures
The IntersectionObserver callback in rowObserver.ts was capturing the `addVisibleRow` and `removeVisibleRow` actions from the Zustand store only once, at the time of the observer's creation.

This created a potential for a stale closure bug, where the observer would call outdated action functions if the store's state or actions were ever re-initialized. This would lead to the application's state not being updated correctly, causing the bug where visible rows would not render their content.

This commit fixes the issue by calling `useVisibleRowsStore.getState()` inside the observer callback. This ensures that the latest, freshest versions of the action functions are always used, preventing the stale state bug.
2025-08-18 06:16:53 +02:00
Carlos Valente 210a85c463 fix: bug with default field url 2025-08-18 06:16:53 +02:00
Carlos Valente 3ad3ad088f ux: improve warning message in partial merge 2025-08-18 06:16:53 +02:00
Carlos Valente 617da68ab5 fix: storage key conflict across stages 2025-08-18 06:16:53 +02:00
Carlos Valente efceb930e5 refactor: improve navigation and settings locks 2025-08-18 06:16:53 +02:00
Carlos Valente 836fa52757 fix: small style tweaks and bug fixes
fix: remove interactive styles from display elements

fix: automation form reset on submit

fix: prevent empty image src
2025-08-18 06:16:53 +02:00
Carlos Valente 902edf4a85 fix: ensure filename sanitation in all routes 2025-08-18 06:16:53 +02:00
Alex Christoffer Rasmussen 0e770655dc Fix: still counting to un-flaged event (#1726)
* create failing test

* ensuere all values a reset before calculations
2025-08-18 06:16:53 +02:00
arc-alex db922a67e2 chore: cleanup 2025-08-18 06:16:53 +02:00
Alex Christoffer Rasmussen 048a9c96ea Refactor: split runtime state (#1725)
* refactor: runtime types

* refactor: runtimeState functions to use the split type

* refactor: RuntimeService to use the new split data

* refactor: use the split data in the UI

* update comments

* refactor: rename runtime to offset

* fix utils test
2025-08-18 06:16:53 +02:00
Alex Christoffer Rasmussen ed93cf313a fix: move playing event into group (#1723)
* fix: moving paying event into group

* add comment
2025-08-18 06:16:53 +02:00
Alex Christoffer Rasmussen 97a0e17192 feat: abort partial load if there is a version missmatch (#1724)
* abort partial load on version missmatch

* reword comment

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-08-18 06:16:53 +02:00
Alex Christoffer Rasmussen 022a58e142 refactor: runtime service (#1722)
* refactor: trim type

* refactor: update runtime service data push logic

* keep using just deep

* reword didDependencyUpdate

* remove dulicate
2025-08-18 06:16:53 +02:00
Alex Christoffer Rasmussen e0149c1cbf Invert overtime (#1715)
* refactor: invert offset calculation

* chore: update tests

* chore: flip offset in UI

* fix seconds display on group target duration

* update comment

* fix rebase
2025-08-18 06:16:53 +02:00
Alex Christoffer Rasmussen 4665f9c2f7 fix: partial load (#1720) 2025-08-18 06:16:53 +02:00
Carlos Valente 9ab6b30aea refactor: small code improvements 2025-08-18 06:16:53 +02:00
Carlos Valente e5d2457717 chore: rename blocks to groups 2025-08-18 06:16:53 +02:00
Alex Christoffer Rasmussen 486d89ecf4 refactor: remove finishedAt from runtime data (#1716) 2025-08-18 06:16:53 +02:00
Carlos Valente 94b1f63c7c refactor: remove onAir 2025-08-18 06:16:53 +02:00
Alex Christoffer Rasmussen 0299463f62 fix: ensure the restore point is the first thing loaded (#1713) 2025-08-18 06:16:53 +02:00
Carlos Valente f7ccb6bb51 chore: upgrade packages 2025-08-18 06:16:53 +02:00
Carlos Valente 6c6f5c2c0f feat: cuesheet sharing
feat: locked presets

refactor: simplify locked param

refactor: create share links
2025-08-18 06:16:53 +02:00
Carlos Valente 1695b4dc68 refactor: add presets to refetch list 2025-08-18 06:16:53 +02:00
Carlos Valente 1ed08df198 refactor: extract project info data to hook 2025-08-18 06:16:53 +02:00
Carlos Valente c300fc18e1 refactor: extract timeline data to hook 2025-08-18 06:16:53 +02:00
Carlos Valente bea7e2bb2b refactor: extract studio data to hook 2025-08-18 06:16:53 +02:00
Carlos Valente ad7f658f15 refactor: extract backstage data to hook 2025-08-18 06:16:53 +02:00
Carlos Valente f23adfa45c refactor: extract countdown data to hook 2025-08-18 06:16:53 +02:00
Carlos Valente 20ee391525 refactor: extract timer data to hook 2025-08-18 06:16:53 +02:00
Carlos Valente 78184b23b7 refactor: simplify routing 2025-08-18 06:16:53 +02:00
Carlos Valente e204f671be refactor: small code improvements
refactor: remove unused code

refactor: extract loader component

refactor: prevent keyword collision

refactor: cleanup hook pending states
2025-08-18 06:16:53 +02:00
Carlos Valente 8c90f5043f chore: upgrade react router 2025-08-18 06:16:53 +02:00
Carlos Valente 7a322406da chore: directory restructure 2025-08-18 06:16:53 +02:00
Carlos Valente 307f0ffd34 feat: expose presets in view options 2025-08-18 06:16:53 +02:00
Carlos Valente 5f8a5c4995 refactor: small style tweaks 2025-08-18 06:16:53 +02:00
Carlos Valente 3747dbc1a8 refactor: style and ux tweaks 2025-08-18 06:16:53 +02:00
Carlos Valente 20859abda9 chore: add demo images to github 2025-08-09 07:01:01 +02:00
Alex Christoffer Rasmussen 9ebb3decdb V3 project migration (#1652)
* migrate settings

* migrate viewsettings

* migrate url preset

* migrate project data

* migrate custom fields

* migrate automations

* migrate rundown

* lint

* return custom field translation table

* check regex

* new url type

* refactor

* migrate old OSC subscriptions

* migrate old http subscriptions

* update comments

* migrate the whole db

* remove automation logging unlis there is an error

* return a new object

* refactor: copy currup is only used in one place

* make a copy of original migrated file

* refactor: ensure image flder is part of project service init

* add entrys to groups

* small cleanup

* just drop incorrect custom fields in the default data parser
2025-08-07 15:48:58 +02:00
Alex Christoffer Rasmussen 35347e8d63 Feat: Time until group and flag (#1708)
* refactor: split event data and state data

* refactor: calculate the start time insted of until

* cleanup

* send next flag expected start from server

* refactor: render

* fix test

* use same icon

* refactor: group duration

* Optimize (#1711)

* refactor: consolidate block, flag and end loading and expected times

* work on test

* cal end value

* lint

* fix test

* remove todo
2025-08-06 21:55:28 +02:00
Carlos Valente e37d2ce50f refactor: tweaks to timeline styles 2025-08-06 21:40:19 +02:00
Carlos Valente 49e923026e feat: expose planned start in operator 2025-08-06 21:40:19 +02:00
Carlos Valente f1a8db9649 feat: timer overview shows waiting timer 2025-08-06 21:40:19 +02:00
Carlos Valente dddeefd544 chore: rename projected > expected 2025-08-06 21:40:19 +02:00
Carlos Valente b4aaa8966f fix: insert into group 2025-08-06 21:40:19 +02:00
Carlos Valente ad9db4edd3 chore: add demo project 2025-08-06 21:40:19 +02:00
Carlos Valente fef9ed4fcc chore: update icon assets 2025-08-06 21:40:19 +02:00
Carlos Valente 9ec47eda6e feat: expose block target duration in header 2025-08-06 21:40:19 +02:00
Alex Christoffer Rasmussen 32edf3404c Fix tag ws api (#1709)
* fix: ws api should also reten tag and not type

* fix: when starting by cue try finding the next first then check before
2025-08-06 21:40:19 +02:00
Alex Christoffer Rasmussen bcf80f9d77 chore: rename offset to offsetAbs and offsetRel (#1706) 2025-08-06 21:40:19 +02:00
Carlos Valente df137402cf bump version to 4.0.0-alpha.2 2025-08-06 21:40:19 +02:00
Carlos Valente daddb7f7b7 fix: open group when selecting from finder 2025-08-06 21:40:19 +02:00
Carlos Valente 82e8b9d66a fix: cleanup behaviour for rundown add buttons 2025-08-06 21:40:19 +02:00
Carlos Valente 765d5d97cf refactor: readonly inputs in cuesheet 2025-08-06 21:40:19 +02:00
Carlos Valente ed4d3ba265 refactor: remove toggle link context menu action 2025-08-06 21:40:19 +02:00
Carlos Valente a358ec0f17 refactor: small style tweaks
refactor: consistent font size on inputs

refactor: style tweaks for block spacing

refactor: improve skipped styles

fix: improve responsiveness in extracted rundown

refactor: style tweaks to overview

refactor: simplify group target duration
2025-08-06 21:40:19 +02:00
Carlos Valente 524721002a fix: prevent nesting groups 2025-08-06 21:40:19 +02:00
Carlos Valente 9ab54edbf5 refactor: small code cleanups
- remove compiler directives
2025-08-06 21:40:19 +02:00
Carlos Valente 8a7f8c8ebc refactor: small style tweaks
- cursor styles on dnd
2025-08-06 21:40:19 +02:00
Carlos Valente 3ce539af2a fix: rendering if countdown in safari 2025-08-06 21:40:19 +02:00
Carlos Valente 3158311aa1 docs: align readme with documentation 2025-07-26 10:51:00 +02:00
Carlos Valente 6b4d96dddd feat: extra info in backstage 2025-07-26 10:51:00 +02:00
Carlos Valente dc3a32d2bc fix: custom project data 2025-07-26 10:51:00 +02:00
Carlos Valente bda01c886e fix: mirroring 2025-07-26 10:51:00 +02:00
Carlos Valente 93916653be refactor: tweaks to styles
- cuesheet action is ghosted
- mute index in cuesheet milestones
- clarify mirrored state in navigation menu
- align overview contents
2025-07-26 10:51:00 +02:00
Carlos Valente 40f8976350 fix: custom fields are last in order 2025-07-26 10:51:00 +02:00
Carlos Valente 78e905a735 fix: flag cannot be the current event 2025-07-26 10:51:00 +02:00
Carlos Valente 126ff61738 refactor: milestone actions in context menu 2025-07-26 10:51:00 +02:00
Carlos Valente 1e63238cac fix: allow empty string as a cue value 2025-07-26 10:51:00 +02:00
Carlos Valente 6ffe34a642 bump version to 4.0.0-alpha.1 2025-07-26 10:51:00 +02:00
Carlos Valente 69f276765b fix: delay indicator styling 2025-07-26 10:51:00 +02:00
Carlos Valente 75554071a7 refactor: improve params performance 2025-07-26 10:51:00 +02:00
Carlos Valente a1fab10bfd feat: improve preset building 2025-07-26 10:51:00 +02:00
Carlos Valente 5bc286145d refactor: extract regex utilities 2025-07-26 10:51:00 +02:00
Carlos Valente 44508ce8b4 refactor: block writing to base fields in run mode 2025-07-26 10:51:00 +02:00
Carlos Valente 90105a199a refactor: empty state for cuesheet 2025-07-26 10:51:00 +02:00
Carlos Valente 9618bec847 refactor: prevent reflow on visible rows 2025-07-26 10:51:00 +02:00
Carlos Valente 66a4e27fdd refactor: remove unused data 2025-07-26 10:51:00 +02:00
Carlos Valente ef906f43d7 docs: improve feature description 2025-07-26 10:51:00 +02:00
Carlos Valente 93b2a1aece refactor: index test alignment 2025-07-26 10:51:00 +02:00
Carlos Valente 050629d281 fix: stale column data 2025-07-26 10:51:00 +02:00
Carlos Valente abe9012f20 fix: keyboard shortcuts in rundown 2025-07-26 10:51:00 +02:00
Carlos Valente ad2ef3b53f refactor: small fixes and style tweaks
fix: prevent reflow on editing images

fix: schedule overview

fix: report on all events

refactor: over-under time colours

fix: prevent edit on delete
2025-07-14 16:08:40 +02:00
Carlos Valente 1e1f547ce1 breaking: convert offset > over-under
change offset direction and relabel to over-under
2025-07-14 11:39:16 +02:00
Carlos Valente 92a34cf135 fix: group expected end 2025-07-14 11:39:16 +02:00
Carlos Valente 1e5081f4a2 fix: parse nested events in rundown 2025-07-14 11:39:16 +02:00
Carlos Valente 6f9e13948d refactor: timeline design review 2025-07-14 11:39:16 +02:00
Carlos Valente 55e34b5f9a fix: guard corrupted parent on delete 2025-07-14 11:39:16 +02:00
Carlos Valente dd47d2b7e7 refactor: always reload on changes 2025-07-14 11:39:16 +02:00
Carlos Valente 445cc9e845 feat: time to next flag 2025-07-14 11:39:16 +02:00
Carlos Valente 637454f73d feat: add flag property 2025-07-14 11:38:27 +02:00
Alex Christoffer Rasmussen e9dda817e5 block end calculation (#1683) 2025-07-14 11:38:27 +02:00
Carlos Valente e811a82be2 refactor: make minimal and clock as presets 2025-07-14 11:38:27 +02:00
Carlos Valente fd9bf0120c refactor: disable react compiler 2025-07-14 11:38:27 +02:00
Carlos Valente e5f8a99bff refactor: extract sentry 2025-07-13 12:33:50 +02:00
Carlos Valente 7305edc398 refactor: review timer styles 2025-07-13 12:33:50 +02:00
Carlos Valente a69c5f354c react 19 type migration 2025-07-13 12:33:50 +02:00
Carlos Valente 0eaec1f88f refactor: timeline style tweaks 2025-07-13 12:33:50 +02:00
google-labs-jules[bot] cdd021e76d Docs: Update DEVELOPMENT.MD with explicit turbo run commands
Updated DEVELOPMENT.MD to use `turbo run <task>` instead of `turbo <task>` for clarity and consistency when referring to Turborepo tasks that don't have a direct pnpm script alias.

Also, added `dependsOn: ["build"]` to `dist-*` tasks in `turbo.json` to ensure correct build order before packaging.
2025-07-12 22:12:17 +02:00
Carlos Valente 05d01e0863 refactor: timer design review 2025-07-12 18:41:17 +02:00
Carlos Valente 2aac4cb06d refactor: remove deprecated views 2025-07-12 18:41:17 +02:00
Carlos Valente 1c6adbd2fd refactor: upgrade react and add compiler 2025-07-12 18:41:17 +02:00
Carlos Valente d24d5c1365 refactor: freeze is view option 2025-07-12 18:41:17 +02:00
Carlos Valente 138c6523d2 feat: implement custom project data image 2025-07-12 18:41:17 +02:00
Carlos Valente d8c1c38123 refactor: fixes to custom field 2025-07-12 18:41:17 +02:00
Carlos Valente 4d419c0877 refactor: style tweaks and bug fixes
- italic and bold for rundown metadata entries
- allow deleting groups
- make groups in order
- correct style for milestones
- open playing groups
- style tweaks
2025-07-12 18:41:17 +02:00
Carlos Valente 50b603f668 create release 4.0.0-alpha.0 2025-07-11 19:46:25 +02:00
Carlos Valente e1e8410ba4 refactor: migrate UI components and remove chakra
migrate pin page
migrate copy tag
migrate pin input
migrate dropdown menus
migrate radio buttons
migrate switch
migrate select
migrate textarea
migrate colour picker
migrate select
migrate context menu
migrate viewparams
remove chakra
2025-07-11 08:43:38 +02:00
Alex Christoffer Rasmussen 4d359445a7 Fix ontime aux actions (#1684)
* switch to base ui

* ensure all action types is part of the valitation

* update test

* atempt to do it in the base-ui way

* fix fixing

* allow undefide visible on set timer message

* fix set time not showing

* correct time data for aux set automation

---------

Co-authored-by: Carlos Valente <carlosvalente@pm.me>
2025-07-09 13:14:23 +02:00
Carlos Valente 8d68a4554d refactor: deprecate languages 2025-07-08 20:14:49 +02:00
Carlos Valente 6f34e1006b refactor: migrate project data 2025-07-08 20:14:49 +02:00
Carlos Valente 1fc6f9b3ea refactor: inline quick add 2025-07-08 16:34:23 +02:00
Carlos Valente a1ad1d47a4 refactor: review action menu 2025-07-08 16:34:23 +02:00
Carlos Valente 9281c0b51d refactor: update app menu links 2025-07-08 16:34:23 +02:00
Carlos Valente 4365c19498 refactor: review event entry 2025-07-08 16:34:23 +02:00
Carlos Valente 8728c1450b refactor: rename block > group in UI elements 2025-07-08 16:34:23 +02:00
Carlos Valente a98c14406d refactor: remove isNextDay block property 2025-07-08 16:34:23 +02:00
Alex Christoffer Rasmussen ab8bfbcd59 handle rearange in block (#1682) 2025-07-07 23:15:37 +02:00
Carlos Valente f2913971db feat: milestones 2025-07-07 18:22:10 +02:00
Carlos Valente a7ae8598db feat: add group metadata to overview 2025-07-07 18:22:10 +02:00
Carlos Valente 17a7d035bf refactor: style cleanups and migrations
fix: close modal with button
refactor: small type improvements
refactor: migrate time inputs
refactor: migrate tooltips
refactor: prevent component resizing
2025-07-07 18:22:10 +02:00
Carlos Valente 6facd6a666 refactor: improve editor and cuesheet headers
- consistent behaviour for app mode
- improve navigation
2025-07-07 18:22:10 +02:00
Carlos Valente 79bcc179ba refactor: style tweaks and ux improvements 2025-07-07 18:22:10 +02:00
Carlos Valente 3790681bec fix: reordering out of a block 2025-07-07 18:22:10 +02:00
Carlos Valente c32203c759 fix: search params editor conflicts with alias 2025-07-07 18:22:10 +02:00
Carlos Valente ce449408f0 Chore/remove got dependency (#1667)
* Refactor: Remove GOT dependency, use simplified fetch wrapper

Replaced GOT with a lightweight, functional, fetch-based utility (`appClient.ts`) for handling JSON POST requests in `SheetService.ts`.

- Created `appClient.ts` to encapsulate fetch logic. It mimics GOT's behavior for sending JSON payloads and parsing JSON responses.
- Error handling in `appClient.ts` uses standard `Error` objects with descriptive messages, including status and error body details, instead of custom error types.
- Updated `SheetService.ts` to use the new `appClient.ts`.
- Removed `got` from server dependencies.

* cleanup

* dont throw when we know the error

* rearange logging

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: arc-alex <ac@omnivox.dk>
2025-07-07 15:54:26 +02:00
Carlos Valente 5600235ba1 refactor: restructure settings
refactor: migrate react components
2025-07-04 15:32:33 +02:00
Carlos Valente a8ea1080f3 refactor: migrate modal components 2025-07-04 15:24:14 +02:00
Carlos Valente 36223ff49f fix: resizing columns in cuesheet 2025-07-04 08:55:46 +02:00
Carlos Valente 3ce5b42e05 refactor: prepare rundown loading process 2025-07-04 08:55:46 +02:00
Carlos Valente c6c1e4a5d7 feat: share link from cuesheet 2025-07-04 08:55:46 +02:00
Carlos Valente 84b73fc02a refactor: migrate related components 2025-07-04 08:55:46 +02:00
google-labs-jules[bot] cd8e014f08 refactor: optimize cuesheet IntersectionObserver
Decouples the IntersectionObserver from the React component tree to
prevent
unnecessary re-renders of the entire Cuesheet table when row visibility
changes. Restores rootMargin for better UX and memoizes EventRow.

- Introduced `rowObserver.ts` to manage a global IntersectionObserver.
  - Observer callback directly updates `useVisibleRowsStore`.
  - Configured with `rootMargin: '400px 0px'` and `threshold: 0.01`
    to pre-load and retain rows near the viewport, improving UX.
- `EventRow` components use `observeRow`/`unobserveRow` from
`rowObserver.ts`.
- `EventRow` is now wrapped in `React.memo` to prevent re-renders if
props
  haven't changed when its parent re-renders.
- Removed observer creation and prop-drilling from `CuesheetBody.tsx`.

This ensures only individual rows re-render content based on visibility
and optimizes row component rendering, improving performance and UX.
2025-07-03 08:46:21 +02:00
Carlos Valente 2e9ee698e9 fix: rundown shortcuts 2025-07-03 08:46:21 +02:00
Carlos Valente d06b5af538 refactor: upgrade base-ui 2025-07-03 08:46:21 +02:00
Carlos Valente 24a3823d3b refactor: entry actions in cuesheet
fix: insert entry before
fix: avoid double submit on enter
fix: move events in the rundown
fix: clone groups
2025-07-03 08:46:21 +02:00
Carlos Valente 8ad260d28a refactor: cuesheet design review
fix: parsing of custom fields for blocks
refactor: extract cuesheet settings
refactor: cuesheet actions
refactor: improve cuesheet performance on resizing
2025-07-03 08:46:21 +02:00
Carlos Valente 0726c04f20 refactor: align property names between block and event 2025-07-03 08:46:21 +02:00
Carlos Valente b66eac3c9f style: cleanup to excel import 2025-06-28 08:17:08 +02:00
Carlos Valente 310dde4465 refactor: remove compiler in favour of linter 2025-06-28 08:17:08 +02:00
Carlos Valente aefa4cbf44 refactor: simple migrations and tweaks to cuesheet 2025-06-28 08:17:08 +02:00
Shobhit Nagpal c8c2a05724 refactor: cuesheet time format (#1634) 2025-06-28 08:17:08 +02:00
Carlos Valente fb9fdf6a38 tooling: react compiler 2025-06-27 20:51:04 +02:00
Carlos Valente 9473880d2c feat: notify clients to refetch custom fields 2025-06-27 20:50:48 +02:00
Carlos Valente 473f50b493 feat: edit groups 2025-06-27 20:50:48 +02:00
Carlos Valente 4c08482258 refactor: extract button styles 2025-06-27 20:50:48 +02:00
Alex Christoffer Rasmussen c603f29197 fix logo upload (#1666) 2025-06-26 21:17:57 +02:00
Alex Christoffer Rasmussen 063ae69409 adding block now/next data (#1661)
* adding block now/next data

* add to ui

* add blockNext

* indicate in UI

* fix test

* restore block start at value

* one loop

* stricter null comparison

* test fails

* dev throw if id cant be found

* fix spelling

* revert client stuff

* rename

* add comment
2025-06-26 21:06:20 +02:00
Alex Christoffer Rasmussen 6711cdaf4f fix event next (#1664) 2025-06-25 18:17:05 +02:00
Carlos Valente bf8ed8d017 feat: implement multiple aux timers 2025-06-25 06:06:00 +02:00
Carlos Valente d1c58712ae refactor: extract button styles 2025-06-25 06:06:00 +02:00
Carlos Valente e74b2daee7 refactor: review rundown header spacing 2025-06-25 06:06:00 +02:00
Alex Christoffer Rasmussen 4872cd0a48 Colorised spreadsheet (#1643)
* feat: colorise spreadSheet with rundown color

* feat: add colorisation in spreadsheet for block

* feat: border color

* fix: cleanup accessible color

* fix: review before merge (https://github.com/cpvalente/ontime/pull/1608)

* fix: usgae of map instead foreach (https://github.com/cpvalente/ontime/pull/1608)

* Update apps/server/src/services/sheet-service/SheetService.ts

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* fix: comment condition

* fix: Remove Color plugin (but don't can get color like 'red' or 'black')

* fix: isLight for more clean code

* fix: remove map on isLight function

* fix: remove debug logger

* refactor colour

* lint

---------

Co-authored-by: Gwenitora <gwenitora.gardu74@gmail.com>
Co-authored-by: Gwenitora <61121870+Gwenitora@users.noreply.github.com>
Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-06-24 22:38:03 +02:00
Alex Christoffer Rasmussen 1025347c08 refactor: add version to session endpoint (#1659) 2025-06-23 20:42:53 +02:00
Carlos Valente 43988ea12b fix: stale ids in cuesheet (#1657) 2025-06-23 19:56:53 +02:00
Carlos Valente 984dcb0181 refactor: expose editor panels in small device 2025-06-23 15:37:27 +02:00
Carlos Valente b991627cb7 refactor: editor responsive navigation 2025-06-23 15:37:27 +02:00
Carlos Valente cff73a65a5 refactor: extract axios utility 2025-06-23 15:37:27 +02:00
Carlos Valente 269091986a refactor: extract navigation elements 2025-06-23 15:37:27 +02:00
Carlos Valente 7fdda1c969 feat: reusable dialog component 2025-06-23 15:37:27 +02:00
Carlos Valente 97691eba05 feat: reusable input component 2025-06-23 15:37:27 +02:00
Carlos Valente 3f3cef7d6f refactor: reusable dialog management 2025-06-23 15:37:27 +02:00
Carlos Valente e0f2830072 refactor: review studio design 2025-06-22 11:20:53 +02:00
Carlos Valente 3cd6cd2f55 refactor: rename external message to secondary message 2025-06-22 11:20:53 +02:00
Carlos Valente 0aeec12ccd refactor: extract common utility 2025-06-22 11:20:53 +02:00
Carlos Valente 56775f621e fix: implicit minutes parsing 2025-06-22 11:06:03 +02:00
Alex Christoffer Rasmussen 14a99ce27b refactor: use runtime metadate in runtimeState (#1653)
* change load to use metadata

* update roll to use metadata

* remove filterTimedEvents

* add comment

* atempth to refactor rollUtils

* refactor util functions
2025-06-22 10:32:48 +02:00
Carlos Valente e090bde8bb refactor: migrate edit to custom component 2025-06-21 13:35:47 +02:00
Carlos Valente 629204810f refactor: operator design review 2025-06-21 13:35:47 +02:00
Carlos Valente fdd81354cc refactor: migrate params editor drawer to custom component 2025-06-21 13:35:47 +02:00
Carlos Valente 4827b295fa refactor: cleanup display options layout 2025-06-21 13:35:47 +02:00
Carlos Valente a21fdc8481 refactor: multi-line notes in rundown events 2025-06-21 13:35:47 +02:00
Carlos Valente c1a4f04d37 refactor: prevent stale data in collapsed groups 2025-06-21 13:35:47 +02:00
Carlos Valente 48fe6a980c refactor: improvements to navigation 2025-06-21 13:35:47 +02:00
Alex Christoffer Rasmussen 31d47ed1e5 refactor: WS (#1622)
* refactor: WS

* refactor refetch

extract refetch keys to package/types

auto invalidata all keys

use refetch keys for project data

no need for constant update of info, just fetch when looking at it

split refetch and query keys

rename types

* refactor viewSettings

* fixup! refactor: WS

* rearange files and make types for api calls

* cleanup viewsettings

* switch exhaustiveCheck

* combine send socket function

* exhaustive check

* rename type to tag

* fixup! fixup! refactor: WS

* remove custom etag solution

* get errors from socket

* lint

* small change

---------

Co-authored-by: Carlos Valente <carlosvalente@pm.me>
2025-06-21 12:18:59 +02:00
Alex Christoffer Rasmussen c8fe0bbcba disable: view navigation e2e test (#1651)
* fix: view navigation e2e test

* escape welcome dialog

* giveup
2025-06-20 19:46:39 +02:00
Carlos Valente c72fff6bb6 refactor: check for no custom project fields 2025-06-20 16:27:36 +02:00
Carlos Valente 36062458fe refactor: memoise paramseditor 2025-06-20 16:27:36 +02:00
Carlos Valente 5c86914cda refactor: improve handling of multi-fields 2025-06-20 16:27:36 +02:00
Carlos Valente b2b115c329 refactor: countdown view redesign
- allow selecting multiple events
- follow selected
- remove past events
2025-06-20 16:27:36 +02:00
Carlos Valente 93977ebc48 refactor: update flat rundown 2025-06-20 16:27:36 +02:00
Carlos Valente 5ad69b458d refactor: inactivity checks keyboard and has initial state 2025-06-20 16:27:36 +02:00
Carlos Valente 4320ad26a4 refactor: small behaviour tweaks 2025-06-20 16:27:36 +02:00
Carlos Valente bab50ea7db refactor: action-blue is from blue scale 2025-06-20 16:27:36 +02:00
Carlos Valente 5d4fe8e795 refactor: style tweaks to view styles 2025-06-20 16:27:36 +02:00
Carlos Valente 7c93f6232d refactor: use ontime button in view 2025-06-20 16:27:36 +02:00
Carlos Valente 97e3e8f435 refactor: create reusable buttons 2025-06-20 16:27:36 +02:00
Carlos Valente 44d5859844 refactor: add version to session endpoint 2025-06-20 16:27:05 +02:00
Alex Christoffer Rasmussen 722e045b20 Revert "Session version" (#1649)
* Revert "refactor: add version to session endpoint"

This reverts commit cd40a6e55e.

* Revert "Remove public event feature (#1645)"

This reverts commit 08d9e24871.

* Revert "Fix: rearrange playing event (#1640)"

This reverts commit 0649678dca.

* Revert "refactor: style tweaks to edit css modal"

This reverts commit a00ec2d02a.

* Revert "refactor: remove usages of framer-motion"

This reverts commit 54a74ccc2a.

* Revert "Upgrade expressjs (#1633)"

This reverts commit 6f3ab274bd.

* Revert "Refactor: require trigger in all events objects (#1636)"

This reverts commit 90870ecfb6.

* Revert "Fix: Correct boundary condition in applyDelay"

This reverts commit b640e0e181.

* Revert "let vite be the proxy to the dev server (#1630)"

This reverts commit c41fe824cf.

* Revert "refactor: migrate custom fields to transactions"

This reverts commit 62c8319d70.

* Revert "refactor: simplify validations"

This reverts commit b1d23467a2.

* Revert "refactor: create transaction system and apply to adding entry (#1620)"

This reverts commit 9a62daf047.

* Revert "Refactor: better rounding (#1594)"

This reverts commit b9ab1c6fd7.

* Revert "refactor: improve reorder logic"

This reverts commit c1054711b0.

* Revert "chore: simplify URLs"

This reverts commit a4d4f29a37.

* Revert "refactor: order is single source of truth"

This reverts commit 2793aadea0.

* Revert "refactor: refetch targets is enum"

This reverts commit e7cfb7d9d9.

* Revert "refactor: small ux improvements"

This reverts commit 256a851c9b.

* Revert "refactor: remove trivially inferred numEvents"

This reverts commit 4ab9c81cb8.

* Revert "feat: duplicate groups"

This reverts commit 2f13d6c89e.

* Revert "feat: create group from entry selection"

This reverts commit f1f7bad25e.

* Revert "feat: create block from rundown empty"

This reverts commit a8b52a48f7.

* Revert "fix: collapsed blocks dont render children"

This reverts commit cd0999b2ab.

* Revert "refactor: type cleanup and test improvements"

This reverts commit b4c60f3f04.

* Revert "feat: allow dissolving a block"

This reverts commit 5cefad3666.

* Revert "fix: uncontrolled prop on controlled component"

This reverts commit 7a6ecd8c34.

* Revert "refactor: improve return of reorder"

This reverts commit ba96ecfd91.

* Revert "refactor: mutations on batch elements must have IDs"

This reverts commit 7bed3757f2.

* Revert "chore: upgrade dependencies"

This reverts commit e2e755b1d2.

* Revert "refactor: extract utility to merge two arrays"

This reverts commit a77d23109d.

* Revert "refactor: change network mode defaults"

This reverts commit 0eb3b8d382.

* Revert "fix: delete nested events"

This reverts commit 0021185288.

* Revert "fix: add event at end of block"

This reverts commit 94c72ff4f6.

* Revert "refactor: make finder available in exported rundown"

This reverts commit e4c08dc9b2.

* Revert "assert non null and update test (#1604)"

This reverts commit ec74af0d62.

* Revert "Fix project renumber (#1597)"

This reverts commit b6d72dd082.

* Revert "Refactor: WebSocket from flush queue to one patch (#1595)"

This reverts commit 31c311daf0.

* Revert "Refactor: ms for api calls (#1593)"

This reverts commit e9b3cc6090.

* Revert "fix test (#1601)"

This reverts commit 543b04a097.

* Revert "fix: rebase master"

This reverts commit d39b85b6e6.

* Revert "chore: correct test path"

This reverts commit bbe107bb2b.

* Revert "refactor: extract rundown parsing"

This reverts commit c616240db1.

* Revert "chore: improve convention entry <> event"

This reverts commit 166be66ce3.

* Revert "refactor: maintain flat orders"

This reverts commit 4180d0a337.

* Revert "refactor: implement operations on nested events"

This reverts commit 78108e316c.

* Revert "refactor: process events in rundown"

This reverts commit 3bb8b70915.

* Revert "chore: improve convention entry <> event"

This reverts commit 1c4f13a0ed.

* Revert "chore: rename currentBlock > parent"

This reverts commit 3ca0abad53.

* Revert "refactor: fix delay positioning in gaps"

This reverts commit 030c8f897f.

* Revert "refactor(e2e): skip flaky test"

This reverts commit 68175cfa3b.

* Revert "refactor: improve project loading"

This reverts commit fd8f757851.

* Revert "refactor: gather group metadata"

This reverts commit 876d111c61.

* Revert "refactor: swap maintains schedule"

This reverts commit 730cb95c04.

* Revert "chore: rename files"

This reverts commit 3c388d4fb5.

* Revert "refactor: restructure model to contain an object of rundowns"

This reverts commit 2e23718d73.

* Revert "refactor: clearer relationship on rundown elements"

This reverts commit 89ea8c470b.

* Revert "refactor: use strict typing"

This reverts commit 178640bfc4.

* Revert "refactor: remove stop as a possible end action"

This reverts commit 4ed38340e0.

* Revert "refactor: restructure model to contain an object of rundowns"

This reverts commit 69eb9a5eff.

* Revert "chore: remove IDE files"

This reverts commit 351425127a.

* Revert "refactor: remove unused and legacy code"

This reverts commit 95f2ba37cc.
2025-06-20 12:28:20 +02:00
Carlos Valente ad6804019b bump version to 3.16.0 2025-06-20 07:41:39 +02:00
Carlos Valente cd40a6e55e refactor: add version to session endpoint 2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen 08d9e24871 Remove public event feature (#1645) 2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen 0649678dca Fix: rearrange playing event (#1640)
* creat failing test

* find and pass index of eventNow in case it moved

* fix cache invalidate edits should also recalculate timer

* use coluor as test indicator

* fixed empty cue guard

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-06-19 18:53:28 +02:00
google-labs-jules[bot] a00ec2d02a refactor: style tweaks to edit css modal 2025-06-19 18:53:28 +02:00
google-labs-jules[bot] 54a74ccc2a refactor: remove usages of framer-motion 2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen 6f3ab274bd Upgrade expressjs (#1633)
* upgrade expressjs

* migration

* reenable test

* extend timeout on download test

* fixup! migration

* move empty body test from controller to validator

* enusre not empty

* extract validation function

* fixup! reenable test

* remove thin controllers

* disable e2e test of project file download
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen 90870ecfb6 Refactor: require trigger in all events objects (#1636)
* reqire trigger in all events

* use structuredClone when cloning an event
2025-06-19 18:53:28 +02:00
google-labs-jules[bot] b640e0e181 Fix: Correct boundary condition in applyDelay
The `applyDelay` function had a condition that incorrectly used `rundown.order.length` instead of `rundownMetadata.flatEntryOrder.length` to check if a delay entry was the last in the sequence. `rundown.order` only contains top-level entries, while `flatEntryOrder` contains all entries, including those within blocks, which is the relevant list for this check.

This commit corrects the condition to use `rundownMetadata.flatEntryOrder.length`.

Existing tests in `rundown.dao.test.ts` (specifically the test `removes delays in last position without applying changes`) already cover this scenario and pass with the correction, ensuring the fix behaves as expected.
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen c41fe824cf let vite be the proxy to the dev server (#1630) 2025-06-19 18:53:28 +02:00
Carlos Valente 62c8319d70 refactor: migrate custom fields to transactions
refactor: extract functions to api domain

refactor: strict custom field parsing

refactor: remove rundown cache utilities

refactor: directory restructure
2025-06-19 18:53:28 +02:00
Carlos Valente b1d23467a2 refactor: simplify validations 2025-06-19 18:53:28 +02:00
Carlos Valente 9a62daf047 refactor: create transaction system and apply to adding entry (#1620)
* refactor: create transaction system and apply to adding entry

* refactor: migrate edit mutations to transaction

* refactor: migrate delete mutation to transaction

* refactor: migrate reorder mutation to transaction

* refactor: migrate apply delay to transaction

* refactor: migrate swapEvents to transaction

* refactor: migrate clone to transaction

* refactor: migrate group/ungroup transactions

* refactor: simplify mutations

* refactor: migrate getters

* chore: add tests to processRundown()
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen b9ab1c6fd7 Refactor: better rounding (#1594) 2025-06-19 18:53:28 +02:00
Carlos Valente c1054711b0 refactor: improve reorder logic 2025-06-19 18:53:28 +02:00
Carlos Valente a4d4f29a37 chore: simplify URLs 2025-06-19 18:53:28 +02:00
Carlos Valente 2793aadea0 refactor: order is single source of truth 2025-06-19 18:53:28 +02:00
Carlos Valente e7cfb7d9d9 refactor: refetch targets is enum 2025-06-19 18:53:28 +02:00
Carlos Valente 256a851c9b refactor: small ux improvements
- rename dissolve > ungroup
- prevent ondrag when clicking
- add untitled as block title fallback
- move block action to context menu
2025-06-19 18:53:28 +02:00
Carlos Valente 4ab9c81cb8 refactor: remove trivially inferred numEvents 2025-06-19 18:53:28 +02:00
Carlos Valente 2f13d6c89e feat: duplicate groups 2025-06-19 18:53:28 +02:00
Carlos Valente f1f7bad25e feat: create group from entry selection 2025-06-19 18:53:28 +02:00
Carlos Valente a8b52a48f7 feat: create block from rundown empty 2025-06-19 18:53:28 +02:00
Carlos Valente cd0999b2ab fix: collapsed blocks dont render children 2025-06-19 18:53:28 +02:00
Carlos Valente b4c60f3f04 refactor: type cleanup and test improvements 2025-06-19 18:53:28 +02:00
Carlos Valente 5cefad3666 feat: allow dissolving a block 2025-06-19 18:53:28 +02:00
Carlos Valente 7a6ecd8c34 fix: uncontrolled prop on controlled component 2025-06-19 18:53:28 +02:00
Carlos Valente ba96ecfd91 refactor: improve return of reorder 2025-06-19 18:53:28 +02:00
Carlos Valente 7bed3757f2 refactor: mutations on batch elements must have IDs 2025-06-19 18:53:28 +02:00
Carlos Valente e2e755b1d2 chore: upgrade dependencies 2025-06-19 18:53:28 +02:00
Carlos Valente a77d23109d refactor: extract utility to merge two arrays 2025-06-19 18:53:28 +02:00
Carlos Valente 0eb3b8d382 refactor: change network mode defaults 2025-06-19 18:53:28 +02:00
Carlos Valente 0021185288 fix: delete nested events 2025-06-19 18:53:28 +02:00
Carlos Valente 94c72ff4f6 fix: add event at end of block 2025-06-19 18:53:28 +02:00
Carlos Valente e4c08dc9b2 refactor: make finder available in exported rundown 2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen ec74af0d62 assert non null and update test (#1604) 2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen b6d72dd082 Fix project renumber (#1597)
* fix: generateUniqueFileName

* loadProject should not generate new names

* update comments

* extract and test getProjectNumber

* spell

* finish jsdoc

* use getProjectNumber

* cleanup loadProject

* create a `incrementProjectNumber` function

* spelling

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen 31c311daf0 Refactor: WebSocket from flush queue to one patch (#1595)
* change flush to one patch

* remove unused types

* create batch

* merge patch into eventStore

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen e9b3cc6090 Refactor: ms for api calls (#1593)
* use ms for api calls

* change it in UI

* update comment

* rename variables

* refactor: addtime api logic
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen 543b04a097 fix test (#1601)
* fix test

* add comment
2025-06-19 18:53:28 +02:00
Carlos Valente d39b85b6e6 fix: rebase master 2025-06-19 18:53:28 +02:00
Carlos Valente bbe107bb2b chore: correct test path 2025-06-19 18:53:28 +02:00
Carlos Valente c616240db1 refactor: extract rundown parsing
refactor: implement groups in editor
2025-06-19 18:53:28 +02:00
Carlos Valente 166be66ce3 chore: improve convention entry <> event 2025-06-19 18:53:28 +02:00
Carlos Valente 4180d0a337 refactor: maintain flat orders 2025-06-19 18:53:28 +02:00
Carlos Valente 78108e316c refactor: implement operations on nested events 2025-06-19 18:53:28 +02:00
Carlos Valente 3bb8b70915 refactor: process events in rundown 2025-06-19 18:53:28 +02:00
Carlos Valente 1c4f13a0ed chore: improve convention entry <> event 2025-06-19 18:53:28 +02:00
Carlos Valente 3ca0abad53 chore: rename currentBlock > parent 2025-06-19 18:53:28 +02:00
Carlos Valente 030c8f897f refactor: fix delay positioning in gaps 2025-06-19 18:53:28 +02:00
Carlos Valente 68175cfa3b refactor(e2e): skip flaky test 2025-06-19 18:53:28 +02:00
Carlos Valente fd8f757851 refactor: improve project loading 2025-06-19 18:53:28 +02:00
Carlos Valente 876d111c61 refactor: gather group metadata 2025-06-19 18:53:28 +02:00
Carlos Valente 730cb95c04 refactor: swap maintains schedule 2025-06-19 18:53:28 +02:00
Carlos Valente 3c388d4fb5 chore: rename files 2025-06-19 18:53:28 +02:00
Carlos Valente 2e23718d73 refactor: restructure model to contain an object of rundowns 2025-06-19 18:53:28 +02:00
Carlos Valente 89ea8c470b refactor: clearer relationship on rundown elements 2025-06-19 18:53:28 +02:00
Carlos Valente 178640bfc4 refactor: use strict typing 2025-06-19 18:53:28 +02:00
Carlos Valente 4ed38340e0 refactor: remove stop as a possible end action 2025-06-19 18:53:28 +02:00
Carlos Valente 69eb9a5eff refactor: restructure model to contain an object of rundowns 2025-06-19 18:53:28 +02:00
Carlos Valente 351425127a chore: remove IDE files 2025-06-19 18:53:28 +02:00
Carlos Valente 95f2ba37cc refactor: remove unused and legacy code
- remove legacy migrations
- remove unused server code
- remove unused UI code
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen c522860d28 Remove public event feature (#1645) 2025-06-19 18:07:42 +02:00
Alex Christoffer Rasmussen 4e561cf01f Fix: rearrange playing event (#1640)
* creat failing test

* find and pass index of eventNow in case it moved

* fix cache invalidate edits should also recalculate timer

* use coluor as test indicator

* fixed empty cue guard

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-06-17 15:59:23 +02:00
google-labs-jules[bot] fb93063b47 refactor: style tweaks to edit css modal 2025-06-13 10:36:48 +02:00
google-labs-jules[bot] 24d3bf05ca refactor: remove usages of framer-motion 2025-06-13 10:36:26 +02:00
Alex Christoffer Rasmussen a4b15970de Upgrade expressjs (#1633)
* upgrade expressjs

* migration

* reenable test

* extend timeout on download test

* fixup! migration

* move empty body test from controller to validator

* enusre not empty

* extract validation function

* fixup! reenable test

* remove thin controllers

* disable e2e test of project file download
2025-06-12 15:02:35 +02:00
Alex Christoffer Rasmussen 2a5b053d97 Refactor: require trigger in all events objects (#1636)
* reqire trigger in all events

* use structuredClone when cloning an event
2025-06-12 09:54:57 +02:00
google-labs-jules[bot] 02c30c9681 Fix: Correct boundary condition in applyDelay
The `applyDelay` function had a condition that incorrectly used `rundown.order.length` instead of `rundownMetadata.flatEntryOrder.length` to check if a delay entry was the last in the sequence. `rundown.order` only contains top-level entries, while `flatEntryOrder` contains all entries, including those within blocks, which is the relevant list for this check.

This commit corrects the condition to use `rundownMetadata.flatEntryOrder.length`.

Existing tests in `rundown.dao.test.ts` (specifically the test `removes delays in last position without applying changes`) already cover this scenario and pass with the correction, ensuring the fix behaves as expected.
2025-06-10 21:37:40 +02:00
Alex Christoffer Rasmussen 2de7221653 let vite be the proxy to the dev server (#1630) 2025-06-09 14:12:59 +02:00
Carlos Valente 2498e59156 refactor: migrate custom fields to transactions
refactor: extract functions to api domain

refactor: strict custom field parsing

refactor: remove rundown cache utilities

refactor: directory restructure
2025-06-09 14:12:59 +02:00
Carlos Valente f3b4ea0155 refactor: simplify validations 2025-06-09 14:12:58 +02:00
Carlos Valente aebf949883 refactor: create transaction system and apply to adding entry (#1620)
* refactor: create transaction system and apply to adding entry

* refactor: migrate edit mutations to transaction

* refactor: migrate delete mutation to transaction

* refactor: migrate reorder mutation to transaction

* refactor: migrate apply delay to transaction

* refactor: migrate swapEvents to transaction

* refactor: migrate clone to transaction

* refactor: migrate group/ungroup transactions

* refactor: simplify mutations

* refactor: migrate getters

* chore: add tests to processRundown()
2025-06-09 14:12:58 +02:00
Alex Christoffer Rasmussen 33ac05ebee Refactor: better rounding (#1594) 2025-06-09 14:12:58 +02:00
Carlos Valente 421183fe55 refactor: improve reorder logic 2025-06-09 14:12:58 +02:00
Carlos Valente b66c19769d chore: simplify URLs 2025-06-09 14:12:57 +02:00
Carlos Valente 7b8445597f refactor: order is single source of truth 2025-06-09 14:12:57 +02:00
Carlos Valente 0b719c485f refactor: refetch targets is enum 2025-06-09 14:12:57 +02:00
Carlos Valente 17deeddb87 refactor: small ux improvements
- rename dissolve > ungroup
- prevent ondrag when clicking
- add untitled as block title fallback
- move block action to context menu
2025-06-09 14:12:57 +02:00
Carlos Valente 2546159a94 refactor: remove trivially inferred numEvents 2025-06-09 14:12:56 +02:00
Carlos Valente 2f26f20db5 feat: duplicate groups 2025-06-09 14:12:56 +02:00
Carlos Valente 11e0215530 feat: create group from entry selection 2025-06-09 14:12:56 +02:00
Carlos Valente ea54953298 feat: create block from rundown empty 2025-06-09 14:12:56 +02:00
Carlos Valente d21f132674 fix: collapsed blocks dont render children 2025-06-09 14:12:56 +02:00
Carlos Valente c7b2f0e89c refactor: type cleanup and test improvements 2025-06-09 14:12:56 +02:00
Carlos Valente a1505606cf feat: allow dissolving a block 2025-06-09 14:12:56 +02:00
Carlos Valente 9edff55bd5 fix: uncontrolled prop on controlled component 2025-06-09 14:12:56 +02:00
Carlos Valente e287215179 refactor: improve return of reorder 2025-06-09 14:12:56 +02:00
Carlos Valente 776649c997 refactor: mutations on batch elements must have IDs 2025-06-09 14:12:56 +02:00
Carlos Valente 9de5e0ac48 chore: upgrade dependencies 2025-06-09 14:12:55 +02:00
Carlos Valente c7d99073f6 refactor: extract utility to merge two arrays 2025-06-09 14:12:55 +02:00
Carlos Valente 5c1694d802 refactor: change network mode defaults 2025-06-09 14:12:55 +02:00
Carlos Valente 7cd319a6b3 fix: delete nested events 2025-06-09 14:12:55 +02:00
Carlos Valente 2c9b6918e6 fix: add event at end of block 2025-06-09 14:12:55 +02:00
Carlos Valente a50cfad7ef refactor: make finder available in exported rundown 2025-06-09 14:12:55 +02:00
Alex Christoffer Rasmussen 66cd1decde assert non null and update test (#1604) 2025-06-09 14:12:55 +02:00
Alex Christoffer Rasmussen c9bf6d9812 Fix project renumber (#1597)
* fix: generateUniqueFileName

* loadProject should not generate new names

* update comments

* extract and test getProjectNumber

* spell

* finish jsdoc

* use getProjectNumber

* cleanup loadProject

* create a `incrementProjectNumber` function

* spelling

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-06-09 14:12:55 +02:00
Alex Christoffer Rasmussen 8557f64382 Refactor: WebSocket from flush queue to one patch (#1595)
* change flush to one patch

* remove unused types

* create batch

* merge patch into eventStore

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-09 14:12:55 +02:00
Alex Christoffer Rasmussen 8e994fb0df Refactor: ms for api calls (#1593)
* use ms for api calls

* change it in UI

* update comment

* rename variables

* refactor: addtime api logic
2025-06-09 14:12:55 +02:00
Alex Christoffer Rasmussen 5c47a834b5 fix test (#1601)
* fix test

* add comment
2025-06-09 14:12:55 +02:00
Carlos Valente f356afef43 fix: rebase master 2025-06-09 14:12:55 +02:00
Carlos Valente eba5252e40 chore: correct test path 2025-06-09 14:12:54 +02:00
Carlos Valente 72c4ad789a refactor: extract rundown parsing
refactor: implement groups in editor
2025-06-09 14:12:54 +02:00
Carlos Valente 3118c20719 chore: improve convention entry <> event 2025-06-09 14:12:54 +02:00
Carlos Valente 3754c374c3 refactor: maintain flat orders 2025-06-09 14:12:54 +02:00
Carlos Valente dd6d74121c refactor: implement operations on nested events 2025-06-09 14:12:54 +02:00
Carlos Valente 6fb42b989e refactor: process events in rundown 2025-06-09 14:12:54 +02:00
Carlos Valente 82dc1bc56b chore: improve convention entry <> event 2025-06-09 14:12:54 +02:00
Carlos Valente d2c7b34142 chore: rename currentBlock > parent 2025-06-09 14:12:53 +02:00
Carlos Valente 918f8d1b08 refactor: fix delay positioning in gaps 2025-06-09 14:12:53 +02:00
Carlos Valente fcbcf70e01 refactor(e2e): skip flaky test 2025-06-09 14:12:53 +02:00
Carlos Valente 5c6b06a0d4 refactor: improve project loading 2025-06-09 14:12:53 +02:00
Carlos Valente fc4c1bf22e refactor: gather group metadata 2025-06-09 14:12:53 +02:00
Carlos Valente 84fac739f0 refactor: swap maintains schedule 2025-06-09 14:12:53 +02:00
Carlos Valente ef73c87e2f chore: rename files 2025-06-09 14:12:53 +02:00
Carlos Valente 26ee5768a5 refactor: restructure model to contain an object of rundowns 2025-06-09 14:12:53 +02:00
Carlos Valente 27dcf06c73 refactor: clearer relationship on rundown elements 2025-06-09 14:12:52 +02:00
Carlos Valente d48b2ae506 refactor: use strict typing 2025-06-09 14:12:52 +02:00
Carlos Valente 14a7c04d3b refactor: remove stop as a possible end action 2025-06-09 14:12:52 +02:00
Carlos Valente abd9b127db refactor: restructure model to contain an object of rundowns 2025-06-09 14:12:51 +02:00
Carlos Valente 0a80d6db31 chore: remove IDE files 2025-06-09 14:12:51 +02:00
Carlos Valente 977c99e587 refactor: remove unused and legacy code
- remove legacy migrations
- remove unused server code
- remove unused UI code
2025-06-09 14:12:51 +02:00
jwetzell fe8bbd5003 add electron and esbuild to allow list for postinstall builds 2025-06-06 20:25:46 +02:00
jwetzell c6e38a2b4a upgrade vite to v6 (#1626) 2025-06-05 06:27:59 +02:00
jwetzell 56f18fe71c update node and pnpm versions in README 2025-06-05 06:13:04 +02:00
jwetzell 0b9306de82 update pnpm to latest v10 2025-06-05 06:13:04 +02:00
jwetzell 3b1e6cf33d update node version in workflows 2025-06-05 06:13:04 +02:00
jwetzell dda219ebbe update NodeJS to v22 2025-06-05 06:13:04 +02:00
jwetzell 8bb771e5c6 upgrade electron and electron-builder 2025-06-05 06:13:04 +02:00
Shobhit Nagpal 8723bcbd33 refactor: block event xlsx import (#1623) 2025-06-02 06:56:46 +02:00
Shobhit Nagpal 0dcea4f2d7 fix: pass empty string fallback when parsing fields for export (#1616) 2025-05-25 19:58:41 +02:00
Shobhit Nagpal 696c016c90 feat: custom data for projects (#1571) 2025-05-23 09:04:57 +02:00
Shobhit Nagpal eed6373dbf refactor: change offset classes depending on playback state (#1611)
* refactor: assign offsetClasses based on playback state
2025-05-23 09:04:57 +02:00
Shobhit-Nagpal ac61f257c0 fix: import preview enable after error 2025-05-13 21:24:14 +02:00
Carlos Valente 8d8625aa77 bump version to 3.15.2 2025-05-13 21:24:14 +02:00
Alex Christoffer Rasmussen 1e1aa4bca5 Fix: sheet export (#1606)
* add util to get keys from labels

* look for existing keys first then create new

* save ontime key to rundown metadata

* update test
2025-05-12 13:56:25 +02:00
Alex Christoffer Rasmussen 1f0cde6d0d fix: relative offset with count to end (#1602)
* refactor merge absolute and relative offset calculation

* update test
2025-05-07 22:34:25 +02:00
Carlos Valente c4b66a9ed3 bump version to 3.15.1 2025-04-30 20:28:42 +02:00
Carlos Valente 133b3aa819 refactor: create hash of table state 2025-04-30 20:23:49 +02:00
Carlos Valente 9566fadbbb chore: upgrade table dependencies 2025-04-30 20:23:49 +02:00
Carlos Valente 62570899e8 fix: issue when user hides all columns 2025-04-30 20:23:49 +02:00
Carlos Valente c1f25f8dba bump version to 3.15.0 2025-04-20 13:52:43 +02:00
Alex Christoffer Rasmussen 37a91026d8 FIX: editing event data clears delay value (#1591)
* leave data in place in case it is not regenerated

* update comments
2025-04-20 13:51:25 +02:00
Bastian Widmer c62ce213f5 fix - fix studio clock sizing when timer seconds are hidden !1585 2025-04-20 13:28:22 +02:00
Alex Christoffer Rasmussen 529751578d refactor: lower third (#1507)
* refactor

* separate in and out time

* keep element on screen

* animate on play

* spelling

* explain animation decisions

* guard numbers from url params

* spelling

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* rename safeParseNumber

* use view data

* refactor getOptionsFromParams

* rebalance animation timing

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-04-20 12:50:16 +02:00
Alex Christoffer Rasmussen dce87c3a81 Relative run mode (#1512)
* inti relative mode

* send imediat update on mode change

* add relative to test

* don't persist offset mode

* add test relative and update calc function

* pass data from ssocket

* add dev guard

* spelling and comments

* show relative in offset overview

* remove comments

* refactor: move totalDelay to _rundown

* update  clear naming and comments

* remove old testing values
2025-04-19 21:36:54 +02:00
Alex Christoffer Rasmussen a94b10cb0f Event trigger (#1557)
* crude UI

* add server functionalety

* better event trigger list

* fix default value

* remove log

* add trigger to recalculate whitelist

* use DTO type

* fix rebase

* one callback for delete and submit

* refactor

* add invalid description

* clean up commits

* cleanup

* refactor: find event triggers in `triggerAutomations`

* refactor: switch to flat array for event triggers

* prevent deleting a automation that is in use

* refactor

* refactor: extract EventEditorCustom

* move trigger options to separate file

* move trigger edit down a file level

* cleanup

* spelling

* refactor: ui review proposal

* change default value

* fix delete filter

---------

Co-authored-by: Carlos Valente <carlosvalente@pm.me>
2025-04-19 21:27:07 +02:00
Shobhit Nagpal 6817263123 feat: css editor (#1542)
* feat: allow editing CSS from settings

---------

Co-authored-by: Carlos Valente <carlosvalente@pm.me>

refactor: style tweaks
2025-04-19 20:40:18 +02:00
Alex Christoffer Rasmussen ddd629d7db in cloud force port 4001 (#1575)
* force 4001

* only log if there is anything to change

* skip port retry in cloud
2025-04-15 21:36:59 +02:00
Carlos Valente 9d2a3d5a12 bump version to 3.14.5 2025-04-15 21:25:46 +02:00
Carlos Valente 4bc271208e refactor: improve synchronisation of external text 2025-04-15 21:25:33 +02:00
Carlos Valente dbd535dd6c refactor: timeout for offline status 2025-04-15 21:25:33 +02:00
Carlos Valente 57b7fe214a refactor: avoid old language features 2025-04-15 21:25:33 +02:00
cv b136e2856a refactor: simplify logic 2025-04-15 21:25:33 +02:00
Carlos Valente 03e68c03a9 refactor: allow cloud in iframe 2025-04-10 21:28:43 +02:00
Carlos Valente 44048a889d bump version to 3.14.4 2025-04-10 21:28:27 +02:00
arc-alex f72db62e08 add comment 2025-04-10 21:21:33 +02:00
arc-alex 333f2623a5 Fix: timeUntil past coutToEnd events 2025-04-10 21:21:33 +02:00
Alex Christoffer Rasmussen 3ebb3451e4 better browser compatibility for view params (#1566) 2025-04-09 12:39:59 +02:00
Carlos Valente 73afbacdd7 refactor: swap does not copy time data 2025-03-28 19:42:43 +01:00
Carlos Valente d08452d613 refactor: clone events fully copies data 2025-03-28 19:42:43 +01:00
Carlos Valente 4ebe6ede59 refactor: consistent overview layout 2025-03-28 19:42:43 +01:00
Carlos Valente 09a32e7612 bump version to 3.14.3 2025-03-28 19:31:31 +01:00
asharonbaltazar d81f5fcfe1 chore(ui): update icons package (#1556)
* chore: add new icons package and remove the old one

* chore: update and consolidate imports

* chore: missed import
2025-03-25 16:36:47 -04:00
Carlos Valente 0a6542df76 fix: link generation should add prefix 2025-03-18 21:56:10 +01:00
Carlos Valente b2eb140ee2 fix: allow copying link 2025-03-18 21:56:10 +01:00
Carlos Valente e28f692d3d fix: cloud should not give IP links 2025-03-18 21:56:10 +01:00
Carlos Valente dc73e7e637 bump version to 3.14.2 2025-03-18 21:48:22 +01:00
Carlos Valente b81e3a4ba5 fix: link to variables 2025-03-17 21:48:40 +01:00
Carlos Valente 6fc7b15d23 refactor: compact event block size 2025-03-17 19:48:04 +01:00
Alex Christoffer Rasmussen 99dd4b4d5c Extract client timeuntil logic (#1521)
* extract

* add to timeline

* translate Due

* fix timeline page

* begin adding some tests

* spelling

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* move file

* refactor

* create hook

* refactor

* update test

* spelling

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* remove translation

* explain isLinkedToLoaded

* add delay to calculation

* add delay value to test

* spelling

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-03-10 22:47:02 +01:00
Carlos Valente b60b1beeec bump version to 3.14.1 2025-03-09 22:06:40 +01:00
Alex Christoffer Rasmussen eab916f410 fix clock drift (#1531) 2025-03-08 18:13:02 +01:00
Carlos Valente 069a003787 refactor: do not send sentry errors if offline 2025-03-08 08:58:04 +01:00
Carlos Valente 9b08080b7a fix: pending card 2025-03-08 08:57:57 +01:00
Carlos Valente 8e7ab54d39 refactor: defer table while loading 2025-03-04 22:01:21 +01:00
arc-alex 4a16378786 refactor: pad block row with extra columns 2025-03-04 22:01:21 +01:00
Shobhit Nagpal e5b30770ce feat: add table navigation to cuesheet table (#1483)
* refactor: cuesheet table body
* feat: add table navigation
2025-03-04 22:01:21 +01:00
Carlos Valente 8465b04c89 bump version to 3.14.0 2025-03-04 21:54:29 +01:00
Carlos Valente 718a6ad2b7 refactor: improve editor overflow 2025-03-04 21:54:29 +01:00
Carlos Valente 6ca4bfc363 refactor: allow overriding styles of cuesheet 2025-03-03 15:58:52 +01:00
Carlos Valente 4859026989 refactor: op cannot subscribe to image fields 2025-03-03 15:58:52 +01:00
Carlos Valente 9167a50eda refactor: project info design review 2025-02-28 19:28:55 +01:00
Carlos Valente 8adca908b3 refactor: improve links inside editor 2025-02-28 19:21:48 +01:00
Carlos Valente bf99646ad5 refactor: restructure directories 2025-02-28 19:21:48 +01:00
Carlos Valente 308285ce3e chore: add missing variables 2025-02-28 19:21:48 +01:00
Carlos Valente dd781a32f6 bump version to 3.13.1 2025-02-27 08:28:21 +01:00
Carlos Valente c2a6de6195 fix: memoisation of row should allow changes 2025-02-27 08:28:21 +01:00
Carlos Valente 0ac2ce8e84 bump version to 3.13.0 2025-02-26 21:43:24 +01:00
Carlos Valente 3bdd8424eb chore: remove console statement 2025-02-26 21:43:24 +01:00
Carlos Valente b3a099255f fix: missing backstage prop 2025-02-26 21:43:24 +01:00
Carlos Valente 570ff59cfc feat: add custom field type of image 2025-02-26 21:42:24 +01:00
Alex Christoffer Rasmussen d0b4c6a279 import id's from sheet (#1516)
* import id's from sheet

* fix test

* fix test in utils

* remove comment

* ensure uri safe ids
2025-02-26 21:12:36 +01:00
Carlos Valente e90161aa33 refactor: consistent logo sizes 2025-02-25 21:25:28 +01:00
Carlos Valente e37254107e style: reword prompt 2025-02-25 21:25:28 +01:00
Carlos Valente 9d6bc0a137 style: clarify resource 2025-02-25 21:25:28 +01:00
Carlos Valente 4cd2dcbab0 refactor: move logic to service 2025-02-25 21:25:28 +01:00
Carlos Valente 8c593b3487 refactor: use memoised selector 2025-02-23 21:50:01 +01:00
Carlos Valente ba74622569 refactor: use native button 2025-02-23 21:50:01 +01:00
Carlos Valente f76c0d1a6d refactor: improve composition performance 2025-02-23 21:50:01 +01:00
Carlos Valente 8e8d3a7824 refactor: send client list on patch 2025-02-23 21:49:53 +01:00
Carlos Valente 6c3ae1f914 refactor: show schedule in smaller screens 2025-02-23 21:49:53 +01:00
Alex Christoffer Rasmussen b6507c27a6 Report (#1469)
* create report service

* write report from runtimeState

* use in UI

* clear report

* update types

* clear all from settings menu

* rearence rightclik menu

* refactor styling

* also report roll events

* ontime/under time is same colour

* refactor reporter

* add target to ontime-refetch

* remove menu

* fectch only on message from server

* memo useGetEventReport

* refactor

* use staleTime

* dont add to menu yet

* implement review

* clear all from menu

* fix merge

* start end show

* combine test for the go button text and action

* add report to menu

* extract csv utility

* report management

* unneeded async

* small refacort of triggerReportEntry

---------

Co-authored-by: Carlos Valente <carlosvalente@pm.me>
2025-02-23 17:02:09 +01:00
Carlos Valente 21877e4bff refactor: allow redirecting to view 2025-02-22 16:15:24 +01:00
Carlos Valente b8d7324be0 feat: allow redirecting to preset 2025-02-22 16:15:24 +01:00
Carlos Valente 88b8e83494 Time until (#1497)
* Timeuntil in UI (#1461)

* cherry pick EventBlockChip

* pull data down throug components

* add comment

* cleanup css

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* fix: account for running day

* refactor: improve composition

* add partial test

---------

Co-authored-by: Alex Christoffer Rasmussen <ac@omnivox.dk>
2025-02-19 13:17:53 +01:00
Carlos Valente abce0c498c bump version to 3.12.0 2025-02-18 21:43:16 +01:00
Carlos Valente 6b2e45e7de refactor: improve template input 2025-02-18 21:43:03 +01:00
Carlos Valente 5fb30c75c4 feat: ontime actions in automation 2025-02-18 21:43:03 +01:00
Carlos Valente 636f78e21e fix: unique reference for filter items 2025-02-18 21:43:03 +01:00
Carlos Valente 1636745fed refactor: improve automation flow logging 2025-02-18 21:43:03 +01:00
Carlos Valente e01ab0c88b fix: update navigation references 2025-02-18 21:43:03 +01:00
Carlos Valente 0ede753652 fix: missing reference on template input 2025-02-18 21:43:03 +01:00
Alex Christoffer Rasmussen 1f6f3df0f2 ensure extension during rename and delete (#1508) 2025-02-18 21:25:36 +01:00
Carlos Valente a5afb32b8a refactor: show projected time 2025-02-18 14:55:24 +01:00
Carlos Valente 9c41f8ff6b refactor: public design review 2025-02-18 14:55:24 +01:00
Carlos Valente 1edde03bb4 chore: directory restructure 2025-02-18 14:55:24 +01:00
Carlos Valente 946b6717d2 refactor: backstage design review
refactor: improve empty state

refactor: review schedule design

- fit as many elements as possible
- expose cycle interval as an option
- stabilise rundown reference
- style tweaks
2025-02-18 14:55:24 +01:00
Carlos Valente f99ad83049 chore: folder structure 2025-02-18 14:55:24 +01:00
Carlos Valente 665496907e refactor: improve overflow style 2025-02-18 14:14:14 +01:00
Carlos Valente 2867c8301d refactor: stabilise table options 2025-02-18 14:14:14 +01:00
Carlos Valente 84a5b4fe5b refactor: use a single menu for table 2025-02-18 14:14:14 +01:00
Carlos Valente 86bb739d90 chore: upgrade zustand 2025-02-18 14:14:14 +01:00
Carlos Valente 2c8b6180d7 refactor: show menu only if enabled 2025-02-18 14:14:14 +01:00
Carlos Valente 4cc138e3c0 chore: directory structure 2025-02-18 14:14:14 +01:00
Carlos Valente d21e1f996f refactor: simplify typing 2025-02-17 21:36:18 +01:00
Carlos Valente 11c1b472a2 refactor: do not test automations without outputs 2025-02-17 21:36:18 +01:00
Carlos Valente 891bda6661 refactor: improve async handling 2025-02-17 21:36:18 +01:00
Carlos Valente c65761448d refactor: simplify import paths 2025-02-17 21:36:18 +01:00
Carlos Valente 70558c5104 refactor: remove unused template literals 2025-02-17 21:36:18 +01:00
Carlos Valente c8bf74a719 refactor: migrate chakra alert to custom component 2025-02-17 14:45:23 +01:00
Carlos Valente 10bf4b93ee doc: add osc disabled warning for cloud users 2025-02-17 14:45:23 +01:00
Carlos Valente f6449a4326 refactor: tweak reusable info component 2025-02-17 14:45:23 +01:00
Carlos Valente 8d38f52b69 bump version to 3.11.1 2025-02-17 12:59:10 +01:00
Carlos Valente 3d40e31730 fix: error on reading malformed package 2025-02-17 12:59:10 +01:00
Carlos Valente 8ac5c0a9a2 Revert "Timeuntil in UI (#1461)"
This reverts commit 221ff7b3af.
2025-02-12 22:19:18 +01:00
Carlos Valente 649f0af2cf refactor: reword error 2025-02-12 22:19:18 +01:00
Carlos Valente fd9b944ecf bump version to 3.11.0 2025-02-12 22:19:18 +01:00
Alex Christoffer Rasmussen 0b36608134 Catch xlsx error (#1487)
* catch potential xlsx errors

* clear worksheet errors when starting new connections
2025-02-12 13:38:18 +01:00
Carlos Valente bac11adeb1 feat: add autocomplete to osc arguments 2025-02-10 20:01:36 +01:00
Carlos Valente 31799f8fab fix: project created without extension 2025-02-10 07:39:05 +01:00
Carlos Valente af177060ff refactor: add next to source fields 2025-02-06 21:20:25 +01:00
Carlos Valente d26a3cd393 refactor: change order of operations 2025-02-06 21:20:25 +01:00
Carlos Valente 38a3165a79 refactor: larger settings panels 2025-02-04 22:35:17 +01:00
Carlos Valente 1715c19747 refactor: clarify empty text 2025-02-04 22:35:17 +01:00
Carlos Valente 6be6e5fd8d refactor: handle overflow in menulist 2025-02-04 22:35:17 +01:00
Carlos Valente 44b0cfd928 refactor: allow locking cuesheet and operator 2025-02-04 22:35:00 +01:00
Carlos Valente 3d50b8ce49 refactor: simplify navigation lock 2025-02-04 22:35:00 +01:00
Carlos Valente c3d66f5bd1 fix: maintain locked and token params 2025-02-01 21:34:29 +01:00
Carlos Valente d08787a6ba refactor: allow presets for op and cuesheet 2025-02-01 21:34:29 +01:00
Carlos Valente f0e6231338 refactor: simplify url preset logic 2025-02-01 21:34:29 +01:00
Carlos Valente 430bfb38f2 chore: test is typescript 2025-02-01 21:34:29 +01:00
Carlos Valente 60354f8ea3 fix: prevent non alphanumeric in pincode 2025-02-01 11:38:58 +01:00
Carlos Valente bc12531729 refactor: show multiline text in custom fields 2025-02-01 11:38:58 +01:00
Carlos Valente 0b8a694e4f refactor: allow overriding operator styles 2025-02-01 11:38:58 +01:00
Carlos Valente 73ada99596 fix: update time in block (#1474)
* fix: update time in block

---------

Co-authored-by: arc-alex <ac@omnivox.dk>
2025-01-31 17:20:02 +01:00
Alex Christoffer Rasmussen f509655d31 update types/node and osc-min (#1475) 2025-01-30 22:25:25 +01:00
Carlos Valente fb1bbd6d5c refactor: fix roll next day 2025-01-27 19:24:11 +01:00
Alex Christoffer Rasmussen 221ff7b3af Timeuntil in UI (#1461)
* cherry pick EventBlockChip

* pull data down throug components

* add comment

* cleanup css

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-01-27 16:39:32 +01:00
Carlos Valente 5d5b48aca6 bump version to 3.11.0-beta.1 2025-01-26 20:22:01 +01:00
Carlos Valente 0f57689750 feat: generate authenticated url (#1471)
* refactor: extract info component

* feat: generate authenticated url

* refactor: add companion select

* refactor: ensure behaviour across environments
2025-01-26 16:51:28 +01:00
Alex Christoffer Rasmussen d34280c03d More batch (#1468)
* batch updates in client

* rearange updating logic

* utilety type

* guard undefinde in affectsLoaded

* fix: pause state not saved to restore point
2025-01-26 13:14:24 +01:00
Carlos Valente bb1e9072fd refactor: apply param structure 2025-01-25 18:54:25 +01:00
Carlos Valente 4503d1e411 refactor: param sections are collapsible 2025-01-25 18:54:25 +01:00
Carlos Valente b4caec0644 refactor: use type imports 2025-01-24 17:06:50 +01:00
Carlos Valente 960f0322ad refactor: improve Esc handling in forms 2025-01-24 16:48:42 +01:00
Carlos Valente a12694b186 refactor: use inline elements 2025-01-24 16:48:42 +01:00
Carlos Valente a1d34c8f6c refactor: remove > and < comparisons 2025-01-24 13:53:17 +01:00
Carlos Valente 7f8dbbfa87 refactor: simplify string comparisons 2025-01-24 13:53:17 +01:00
Carlos Valente 39e012452b refactor: improve form ux 2025-01-24 13:53:17 +01:00
Carlos Valente c333117347 refactor: start with small runtimedata subset 2025-01-24 13:53:17 +01:00
Carlos Valente eb0e5e6420 refactor: handle boolean comparisons 2025-01-24 13:53:17 +01:00
Carlos Valente 8ba65fa2bd refactor: handle non numeric comparisons 2025-01-24 13:53:17 +01:00
Carlos Valente efe5ac16f2 chore: rename feature elements
rename automations > triggers
rename blueprints > automations

chore: add link to documentation
2025-01-24 13:53:17 +01:00
arc-alex f4f266dbd4 refactor: migrate to OSC min 2025-01-24 13:53:17 +01:00
Carlos Valente bc9345199b feat: filtering logic 2025-01-24 13:53:17 +01:00
Carlos Valente 2cc434b0e9 refactor: remove legacy service 2025-01-24 13:53:17 +01:00
Carlos Valente 1f71d4578c refactor: migrate triggers to new service 2025-01-24 13:53:17 +01:00
Carlos Valente 6c4d80158c feat: send osc 2025-01-24 13:53:17 +01:00
Carlos Valente ab4dd300ca feat: send http 2025-01-24 13:53:17 +01:00
Carlos Valente 8b84963e17 feat: automation UI
merge with automation service
2025-01-24 13:53:17 +01:00
Carlos Valente 05e61e7bf8 feat: automation service 2025-01-24 13:53:17 +01:00
Carlos Valente a8fe4c8e68 refactor: allow both http and https 2025-01-24 13:53:17 +01:00
Alex Christoffer Rasmussen d175c788c5 Batch client updates (#1448) 2025-01-24 13:52:58 +01:00
Carlos Valente f1f628a48e bump version to 3.10.5 2025-01-24 13:31:18 +01:00
Carlos Valente d58335d1a1 refactor: allow overriding timer type in view 2025-01-24 13:13:53 +01:00
Carlos Valente 78c2bd79b5 refactor: card content on preload 2025-01-24 13:13:53 +01:00
Carlos Valente 28452d1e38 refactor: allow overriding progress bar phases 2025-01-24 13:13:53 +01:00
Carlos Valente 46790441e0 refactor: review view styles 2025-01-24 13:13:53 +01:00
Carlos Valente 3f35b5e297 refactor: extract and simplify logic 2025-01-24 13:13:53 +01:00
Carlos Valente 49cae647d6 refactor: larger progress bar 2025-01-24 13:13:53 +01:00
Carlos Valente 90700c85e8 refactor: subscript clock 2025-01-24 13:13:53 +01:00
Carlos Valente 8fac23c078 refactor: make function private 2025-01-24 13:13:53 +01:00
Carlos Valente b5cf6c4bd1 chore: directory restructure 2025-01-24 13:13:53 +01:00
Carlos Valente 0ea00ff6be chore: remove unused code 2025-01-24 12:41:42 +01:00
Carlos Valente 30e290c320 refactor: remove unnecessary handler 2025-01-22 20:51:12 +01:00
Carlos Valente ac52b3f0c1 chore: use smaller dependency 2025-01-22 20:51:12 +01:00
Carlos Valente 8e3db6c559 refactor: remove dom dependencies 2025-01-22 20:51:12 +01:00
Carlos Valente 1d2013ad41 fix: show correct state for welcome option 2025-01-19 22:18:03 +01:00
Carlos Valente 9c910c079d bump version to 3.10.4 2025-01-19 20:04:52 +01:00
Carlos Valente 420cef8b0d chore: add html files to docker 2025-01-19 19:57:10 +01:00
Carlos Valente b4c7b84f92 refactor: handle trailing slash in URL preset 2025-01-19 19:57:10 +01:00
Carlos Valente 0450eb9169 refactor: set token from query params 2025-01-19 19:57:10 +01:00
Carlos Valente 7fe5223af9 feat: add auth to server requests 2025-01-19 19:57:10 +01:00
arc-alex ce8d534953 feat: optional welcome modal
welcome modal is persisted in app state

created endpoints to set the visibility

added UI to support feature

Co-authored-by: Carlos Valente
<34649812+cpvalente@users.noreply.github.com>
2025-01-18 20:24:46 +01:00
Carlos Valente 4a6fbc2a92 refactor: add timezone data to session 2025-01-17 20:05:56 +01:00
Carlos Valente 50ebcf4e14 refactor: review welcome modal design 2025-01-17 20:04:00 +01:00
Carlos Valente f0f7d1cede docs: add homebrew links 2025-01-17 20:02:44 +01:00
Carlos Valente d2bdb1ea03 docs: add links to sponsor 2025-01-17 20:02:44 +01:00
Carlos Valente 9be6febcda chore: upgrade playwright 2025-01-17 19:57:01 +01:00
Carlos Valente aa929fd0dc refactor: create utility for nested templates 2025-01-12 10:20:24 +01:00
Carlos Valente 7baa6a4ab9 refactor: create mock data utilities 2025-01-12 10:20:24 +01:00
Carlos Valente 082fe8b8d7 chore: write feature spec 2025-01-12 10:14:44 +01:00
Alex Christoffer Rasmussen e9a7cd0400 More rundown metadata (#1429)
* add dayOffset to OntimeEvent

* refactor: isNewLatest

* calculate totalDays

* refactor: getTimeFromPrevious

* add gap as dataset in OntimeEvent

* use in ui

* format overlap is just a simple text formatting, big test is not needed

* add new fields where needed

* update apply delay

* show nex day eaven if there is no gap

* create test

* use buildin day offset in timeline

* remove todo

* consistent naming

* make a calculateDayOffset util for rundownCache

* refactor: checkIsNextDay to use dayOffset

* spelling

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* remove unneeded test

* remove todo

* update test db

* use data-testid

* spelling

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* update comments

* remove null option

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-01-09 13:35:11 +01:00
Carlos Valente 9ba40c35a1 refactor: avoid matching file versions 2025-01-06 22:58:06 +01:00
Carlos Valente f94db06812 refactor: wider sheet id length 2025-01-06 22:58:06 +01:00
Carlos Valente 35424cfff2 bump version to 3.10.3 2025-01-06 20:33:39 +01:00
Alex Christoffer Rasmussen 13eadeb0af fix download (#1434)
* fix download

* update test db

* create download test
2025-01-06 20:26:06 +01:00
Carlos Valente 9cfed5a150 fix: issue with persistent script 2025-01-06 20:11:15 +01:00
Alex Christoffer Rasmussen a22c2d380d bump: v3.10.2 (#1431) 2025-01-06 14:08:36 +01:00
Alex Christoffer Rasmussen cd913fc144 small strict improvements (#1428)
* small `strict` improvements

* fix type

* use isObject
2025-01-06 14:07:36 +01:00
Alex Christoffer Rasmussen 743d7fa782 fix: validateLinkStart (#1430)
* fix validateLinkStart

* update comment

* use fallback

* cast to MaybeString

* update comment

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-01-06 13:12:03 +01:00
Carlos Valente 891c43ba77 refactor: countToEnd does not cause rundown recalculation 2025-01-05 15:18:28 +01:00
Carlos Valente fba20b889e refactor: only allow testing saved URLs 2025-01-05 15:17:53 +01:00
Carlos Valente bc4d913ba7 refactor: always show client path 2025-01-05 15:17:53 +01:00
Alex Christoffer Rasmussen 533f9798e8 convert to and from database type (#1410)
* create db event type

* dont use it yet

* convert to and from database type

* type needed in one place in client

* use flatMap to filter out unknow stuff

* leave revision in save file

* move to utils

* make test

* destructur entry

* simple test

* throw in rundownToDatabaseRundown

* clerify comment

* Refactor: avoid shadowing the rundown variable in rundownCache (#1409)


---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* rename to DAO

* add simple edit test

* only the generate function clears the state state

* strict guard for custom fields

* fix small strict issues

* ensure deep copy

* safely set isStale

* todo is done

* small type error

* these functions can work on DAO entries

* only reat rundown as DAO on initial load and the cast immediately and esure all fields are present

* eslint comment

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* fix spelling

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* fix spelling

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* comment not needed

* setIsStale

* remove DAO

* clean up commits

* remove unneeded async/await

* revert test

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-01-05 14:35:58 +01:00
Carlos Valente 1b62ed5f2d bump version to 3.10.1 2025-01-04 23:12:57 +01:00
Carlos Valente bd0c951da8 declare env variables 2025-01-04 23:12:57 +01:00
Carlos Valente 2d5b86808f refactor: cleanup pin styles 2025-01-04 16:39:47 +01:00
Carlos Valente c69bce3e89 refactor: cleanup picker implementations 2025-01-03 13:02:26 +01:00
Carlos Valente d62e314d19 bump version to 3.10.0 2025-01-03 12:56:37 +01:00
Shobhit-Nagpal dbd56522f6 refactor: swatch picker and inline colour picker 2025-01-02 21:30:41 +01:00
Shobhit-Nagpal 1ba48c6a6f refactor: inline colour picker 2025-01-02 21:30:41 +01:00
Carlos Valente f4f24da47b refactor: stop port change in dev 2025-01-02 14:18:19 +01:00
Carlos Valente c3609b2aed chore: extract middleware config 2025-01-02 14:18:19 +01:00
Carlos Valente 260d326989 chore: add noindex to app entry 2024-12-31 23:20:28 +01:00
Carlos Valente a21e32a097 refactor: expose indent style 2024-12-31 15:01:34 +01:00
Carlos Valente 617e4b579b refactor: introduce empty placeholder 2024-12-31 15:01:34 +01:00
Carlos Valente 44f65b2fcc refactor: extract utility 2024-12-31 13:04:00 +01:00
Carlos Valente 760de608e3 refactor: simplify logic 2024-12-31 13:01:22 +01:00
Carlos Valente 0dfef732bf refactor: prevent race conditions on init 2024-12-29 22:53:41 +01:00
Carlos Valente f65ec0da3c chore: upgrade dependencies 2024-12-29 20:02:56 +01:00
Shobhit Nagpal 6bbf8ce795 refactor: event editor color picker (#1400)
* refactor: event editor color picker

* refactor: throttle and debounce function signature
2024-12-27 10:57:48 +01:00
Carlos Valente 7f39d035fd refactor: show delayed times 2024-12-22 21:43:37 +01:00
Carlos Valente bdd2a62cee feat: allow editing times in cuesheet 2024-12-22 21:43:37 +01:00
Carlos Valente b38a7e5c92 refactor: allow submitting same value 2024-12-22 21:43:37 +01:00
Carlos Valente b173b5a8e8 chore: update manifest 2024-12-22 20:06:35 +01:00
Alex Christoffer Rasmussen 29243e3f6c detect EADDRINUSE on server start (#1348)
* detect `EADDRINUSE` on server start

* @ts-expect-error

* send portError to electron

* use escalateErrorFn

* dont move in docker

* allow errors to be sendt to electron UI

* Log after the new port is found

* Update comment

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* update comment

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* update comment

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* rename to escalate

* extract `serverTryDesiredPort`

* type check server.address()

* request shutdown on `unrecoverable` `escalateError`

* combine network utils

* reject promise

* not unrecoverable by default

* unneeded assignment

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2024-12-22 16:23:47 +01:00
Carlos Valente 1af394cd49 chore: upgrade turbo 2024-12-21 22:46:07 +01:00
Carlos Valente 1c85265623 fix: section can only be selected if parent is 2024-12-21 13:56:23 +01:00
Carlos Valente 6199d0ad10 refactor: improve section style and composition 2024-12-20 21:24:34 +01:00
Carlos Valente 2f2c26ed00 chore: rename time-to-end to count-to-end 2024-12-20 21:24:34 +01:00
Carlos Valente c9ef2465f8 refactor: visibility options 2024-12-20 21:15:26 +01:00
Carlos Valente 430b72c1d0 fix: missing return when data is handled 2024-12-20 21:15:26 +01:00
Carlos Valente 88b114020b feat: edit duration in cuesheet 2024-12-20 21:15:26 +01:00
Carlos Valente c445c42f8a refactor: extract edit element in table 2024-12-20 21:15:26 +01:00
Carlos Valente 2fc4e6da16 refactor: extract utility type 2024-12-20 21:15:26 +01:00
Carlos Valente 4cbe25a845 refactor: simplify update functions 2024-12-20 21:15:26 +01:00
Carlos Valente ab6765b332 refactor: tweak table element styles 2024-12-20 21:15:26 +01:00
Carlos Valente fb38d82855 refactor: show cuesheet as long as we have data 2024-12-20 21:15:26 +01:00
Carlos Valente 1061ed2a0e refactor: improve fallback timer consistency 2024-12-20 15:27:57 +01:00
Carlos Valente 3d37f42fe0 refactor: tweak styling 2024-12-20 09:03:02 +01:00
Carlos Valente 1bdea8e436 refactor: edit cue field 2024-12-20 09:03:02 +01:00
Carlos Valente 2c25910863 refactor: add table actions 2024-12-20 09:03:02 +01:00
Carlos Valente 8f30c0a7df refactor: restructure element composition 2024-12-20 09:03:02 +01:00
Carlos Valente 67fa747aae refactor: extract cuesheet dnd 2024-12-20 09:03:02 +01:00
Carlos Valente 8116b169b2 refactor: generalise event editor 2024-12-20 09:03:02 +01:00
Carlos Valente ec823e0095 refactor: improve cuesheet composition
refactor: simplify placing events in rundown

m
2024-12-20 09:03:02 +01:00
Carlos Valente c7faab00a3 feat: create offline indicator 2024-12-19 22:15:48 +01:00
Alex Christoffer Rasmussen cfcf6a5684 extract isPlaybackActive to a global util function (#1393) 2024-12-18 12:44:48 +01:00
Carlos Valente df1cf7a96b feat: parse timeToEnd from excel 2024-12-16 21:59:38 +01:00
Carlos Valente 0f3feca7ab refactor: extract time-to-end 2024-12-16 21:59:38 +01:00
Carlos Valente 41fe213ebb refactor: settings in params 2024-12-16 15:13:49 +01:00
Carlos Valente e91d5f5cd9 refactor: always show settings 2024-12-16 15:13:49 +01:00
Carlos Valente bedab221d9 chore: dedupe lock file 2024-12-16 15:13:49 +01:00
Carlos Valente 97208c052a chore: upgrade dependencies 2024-12-16 15:13:49 +01:00
Alex Christoffer Rasmussen b048d0f88d Edit in cuesheet (#1372)
* pass on all event edits

* MakePublic

* trim value in cell

* edit notes

* title

* add key check

* don't log error

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

* checkbox

* refactor

* remove log

* fix test

* use row number to test value

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2024-12-16 14:27:25 +01:00
asharonbaltazar d8f7d4bba6 feat(ui): add warning in view params when styles are overriden (#1384)
* feat: add warning in view params when styles are overriden

* refactor: style tweaks

---------

Co-authored-by: Carlos Valente <carlosvalente@pm.me>
2024-12-15 10:40:27 -05:00
Carlos Valente ed65a4db67 refactor: smaller settings 2024-12-15 11:04:15 +01:00
Carlos Valente 4a70bcfe64 chore: remove unused 2024-12-15 11:04:15 +01:00
Carlos Valente 89e68c3b0e fix: correct aria label 2024-12-15 11:04:15 +01:00
Carlos Valente 976dc9cbde refactor: tweak header style 2024-12-15 11:04:15 +01:00
Carlos Valente 3701ba5174 refactor: handle missing data 2024-12-15 11:04:15 +01:00
Carlos Valente acc6ba63ba refactor: keep drawer open on submit 2024-12-15 11:04:15 +01:00
Carlos Valente 6c5afcb8d9 refactor: improve empty state for pages 2024-12-15 09:55:52 +01:00
Carlos Valente d46dfcf82e refactor: improve redirect 2024-12-14 08:25:53 +01:00
Carlos Valente 4c6acc4013 refactor: improve active styles for navigation buttons 2024-12-13 11:24:03 +01:00
Carlos Valente 26a24449de refactor: prevent modal close on click outside 2024-12-13 10:40:16 +01:00
Carlos Valente a1fb642441 bump version to 3.9.5 2024-12-13 10:40:16 +01:00
Carlos Valente b64b154330 refactor: disable shutdown 2024-12-13 10:39:58 +01:00
Carlos Valente 5032cbf65a refactor: prevent instantiating unavailable services 2024-12-13 10:39:58 +01:00
Carlos Valente 466360f9d1 chore: note service limitations 2024-12-13 10:39:58 +01:00
Carlos Valente ef2ea9a1da fix: recover edit menu and shortcuts 2024-12-13 10:39:45 +01:00
Carlos Valente fb83f48752 refactor: maintain an isOnline flag in client 2024-12-13 10:39:30 +01:00
Carlos Valente c1d53b0e55 refactor: improve empty state for project info 2024-12-13 10:12:06 +01:00
Alex Christoffer Rasmussen 7915cc822d Dockersafe rename (#1370)
* add dockerSafeRename function

* replace all rename functions

* add explanation to function
2024-12-10 23:27:51 +01:00
Carlos Valente 7dbf64d100 chore: restructure directory 2024-12-10 15:53:53 +01:00
Carlos Valente 6ffbf2af9d chore: move files to new structure 2024-12-10 15:53:53 +01:00
Carlos Valente fa7ec623f1 bump version to 3.9.4 2024-12-08 08:52:18 +01:00
Carlos Valente bfb9b51073 fix: secure protocol resolution 2024-12-08 08:52:18 +01:00
Carlos Valente 8bc3f5a56c Fix links (#1366)
* chore: remove unused code

* bump version to 3.9.3

* chore: use constant links

* refactor: build links

* chore: ignore ontime-data
2024-12-06 16:48:35 +01:00
Carlos Valente acf1afb5e9 fix: prevent overflow 2024-12-06 14:54:41 +01:00
Carlos Valente d8e2a8d092 bump version to 3.9.2 2024-12-04 21:41:49 +01:00
Carlos Valente 8e587128a1 refactor: ensure element height 2024-12-04 21:41:49 +01:00
Alex Christoffer Rasmussen 607bc40673 create addTime function in simple timer (#1363)
* create addTime function in simple timer
2024-12-04 20:25:49 +01:00
Carlos Valente e6aa7404f7 chore: correct documentation 2024-12-04 19:49:49 +01:00
Carlos Valente d622a7738f refactor: remove deprecated field 2024-12-04 19:49:49 +01:00
Carlos Valente b9ba416366 refactor: add prefix to relative assets
refactor: add prefix to server entrypoints
refactor: add prefix to express router
refactor: add prefix to router basename
2024-12-04 19:49:49 +01:00
Carlos Valente b3ce247f54 bump version to 3.9.1 2024-12-02 14:45:57 +01:00
Carlos Valente 82a2660fbc fix: router prefix 2024-12-02 14:45:57 +01:00
Carlos Valente 1e8706aa73 feat: allow add time to aux timer 2024-12-02 13:30:22 +01:00
Carlos Valente 7dec32aea7 bump version to 3.9.0 2024-12-02 13:30:11 +01:00
Carlos Valente 7e4cf6ffce refactor: improve time added display 2024-12-01 21:06:33 +01:00
Carlos Valente e6dfe9c363 fix: rolling over midnight wrongly classified as skip 2024-12-01 21:06:33 +01:00
Carlos Valente 29e682ee06 refactor: remove frozen implementation 2024-11-30 22:31:18 +01:00
Carlos Valente 55d349877b fix: prevent stale data in finder 2024-11-30 22:30:19 +01:00
Alex Christoffer Rasmussen 8f2a93d6a3 FIX: OSC TX and RX shutdown was swapped (#1352) 2024-11-30 22:27:36 +01:00
Carlos Valente 6529a05e2a fix: previous eventid is any entry 2024-11-30 21:59:53 +01:00
Alex Christoffer Rasmussen 0f5c47bc82 Small fixes (#1345)
* cleanup: remove console log

* use UI Index
2024-11-30 10:11:36 +01:00
Carlos Valente f561e52bb5 refactor: prevent caching html files 2024-11-29 19:36:39 +01:00
Carlos Valente 35f4472daa chore: define test workspaces 2024-11-29 19:36:39 +01:00
Carlos Valente 272f788c15 refactor: split vendor 2024-11-29 19:36:39 +01:00
Carlos Valente 85b71627a8 refactor: exclude html from compression 2024-11-29 19:36:39 +01:00
Carlos Valente 6e51e5083b refactor: allow route prefix 2024-11-29 19:36:39 +01:00
Ary Neto 1f998fda74 reverted unnecessary change 2024-11-28 22:10:48 +01:00
Ary Neto e7b5baa49a fix: file picker opening on delete 2024-11-28 22:10:48 +01:00
Carlos Valente f3f4cdb19f feat: add support for chinese 2024-11-25 09:55:07 +01:00
Carlos Valente b489214004 refactor: allow copy-paste in interface 2024-11-25 09:54:43 +01:00
Carlos Valente cc55414b57 refactor: show project description 2024-11-25 09:54:43 +01:00
Carlos Valente e694cb7556 refactor: style tweaks to cuesheet 2024-11-25 09:54:43 +01:00
Carlos Valente 988100c1d9 chore: remove extra character 2024-11-25 09:54:04 +01:00
Carlos Valente b4eabfae72 feat: trigger welcome modal from electron 2024-11-25 09:54:04 +01:00
Carlos Valente 22fe65c655 feat: show welcome screen on first login 2024-11-25 09:54:04 +01:00
Carlos Valente 7bf90d669a refactor: expose api for loading demo project 2024-11-25 09:54:04 +01:00
Carlos Valente a518dc4882 refactor: consolidate event editor styles 2024-11-25 09:54:04 +01:00
Carlos Valente dd5ffb2f47 refactor: improve app data 2024-11-25 09:54:04 +01:00
Carlos Valente c5a5eab225 refactor: extract utility hook 2024-11-25 09:54:04 +01:00
Carlos Valente 5d6345717f chore: update ontime assets 2024-11-25 09:54:04 +01:00
asharonbaltazar 4c88f90a15 feat(ui): disable menu buttons with locked param (#1332)
* feat: add lock icon when param `locked=true`

* chore: move `isViewLocked` logic up the component tree

* chore: remove extra container from lock icon

* chore: move fading logic into separate hook

* chore: move locked icon into separate component

* chore: rename variables
2024-11-18 09:49:49 -05:00
Carlos Valente 238eb49b5c bump version to 3.8.0 2024-11-18 14:57:40 +01:00
Carlos Valente 7aec3e0d73 feat: project info view 2024-11-18 14:56:30 +01:00
Carlos Valente 312b294a61 refactor: update test 2024-11-18 14:56:30 +01:00
Carlos Valente 9bfa3e9f8e feat: implement logo in all views 2024-11-18 13:48:05 +01:00
Carlos Valente 9a2cfb59e7 fix: issue with colour string sanitation 2024-11-18 13:12:50 +01:00
Ary a6e1cbbbce Upload corporate logo (#1314)
* feat: upload logo to project data

---------

Co-authored-by: Carlos Valente <carlosvalente@pm.me>
2024-11-17 22:55:20 +01:00
Carlos Valente 8dd448db44 feat: implement naive client ping 2024-11-17 21:41:10 +01:00
Carlos Valente d87542dcf7 refactor: add cloud runtime 2024-11-17 21:41:10 +01:00
Carlos Valente 18575fc558 fix: separator collides with space indicator 2024-11-13 20:23:04 +01:00
Carlos Valente 04e2593939 style: clarify select component 2024-11-13 20:23:04 +01:00
Carlos Valente 73d6b97afa fix: issue with casing in custom fields 2024-11-13 20:23:04 +01:00
Carlos Valente b55724ad0e bump version to 3.7.1 2024-11-12 21:46:31 +01:00
Carlos Valente 3a7c2b8a55 refactor: refetch on load 2024-11-12 21:46:31 +01:00
Carlos Valente 420dfae652 refactor: clarify url preset form 2024-11-09 13:35:43 +01:00
Carlos Valente fb6896de76 refactor: show timer clock in preview 2024-11-07 21:39:04 +01:00
Carlos Valente f574fdbc02 refactor: hide progress bar in timer none 2024-11-07 21:39:04 +01:00
Carlos Valente 597be4e710 refactor: omit modifier in timer type none 2024-11-07 21:39:04 +01:00
Carlos Valente 6e8734c73f fix: prevent stale data on project change 2024-11-07 21:33:20 +01:00
Carlos Valente 9440ad17d2 refactor: show leading zeroes in minimal 2024-11-07 21:33:07 +01:00
Carlos Valente 9ba94288a1 refactor: remove style override 2024-11-01 15:42:32 +01:00
Carlos Valente 6c78d25af1 fix: issue with rem 2024-11-01 15:42:32 +01:00
Carlos Valente 0d8c186c77 feat: add time type none 2024-11-01 15:40:42 +01:00
Carlos Valente 7ccd41eeee fix: validation cycle 2024-10-29 20:23:12 +01:00
Carlos Valente 43b390a079 refactor: register modal in electron 2024-10-29 20:23:12 +01:00
Carlos Valente 80ff3251ea refactor: allow two NIF links in macbook screens 2024-10-29 20:23:12 +01:00
Carlos Valente 3591b93c2d refactor: handle thrown errors 2024-10-29 20:23:12 +01:00
Carlos Valente b637621871 refactor: guarantee minimum width 2024-10-29 20:23:12 +01:00
Carlos Valente ed2efe183b feat: quick project wizard 2024-10-29 20:23:12 +01:00
Carlos Valente 27f99b4c87 refactor: generalise modal footer style 2024-10-29 20:23:12 +01:00
Carlos Valente ed4d0f060b fix: reset rem value 2024-10-29 20:23:12 +01:00
Carlos Valente 7c19c52332 chore: directory restructure
merge with directory
2024-10-29 20:23:12 +01:00
Carlos Valente b1bff22b84 refactor: improve visibility of time-to-end 2024-10-29 20:02:23 +01:00
Carlos Valente 83b9fde45e refactor: improve visibility of extract element 2024-10-29 20:02:23 +01:00
Carlos Valente 37974df5c6 fix: allow changing custom field colour 2024-10-29 09:42:11 +01:00
Carlos Valente 161ec383fd refactor: unify localhost logic 2024-10-29 09:42:01 +01:00
Carlos Valente de38f9d467 refactor: show new app version 2024-10-29 09:42:01 +01:00
Carlos Valente 8ad1f2cb38 refactor: no style overrides in production views 2024-10-29 08:23:44 +01:00
Carlos Valente 143917f11a feat: event finder 2024-10-26 11:57:20 +02:00
Carlos Valente a8756c8afe refactor: clear state on sheet flows 2024-10-25 21:58:37 +02:00
Carlos Valente 5501f57b45 feat: add link to context menu 2024-10-25 19:44:55 +02:00
Carlos Valente 46e9fcb3cb refactor: populate readme on start 2024-10-22 21:18:33 +02:00
Carlos Valente f2d7f4ab82 bump version to 3.6.2 2024-10-22 20:24:22 +02:00
Alex Christoffer Rasmussen a5496434f8 todo note about sanitiseCustomFields (#1281) 2024-10-22 19:13:24 +02:00
Alex Christoffer Rasmussen 42984eed87 Improve xlsx import (#1271)
* use xlsx

* add existing custom fields to import map

* keep colours of existing custom fields

* use unknown

* fix

* revert

* allow space and upper case

* fix test

* define empty sheet
2024-10-22 19:13:07 +02:00
Carlos Valente 59c1c77d48 refactor: timeline has overrideable styles 2024-10-21 22:16:18 +02:00
Carlos Valente dd13eb31d8 refactor: expose public dir 2024-10-20 22:35:26 +02:00
Carlos Valente 82810f1cb1 feat: add sessions stats endpoint 2024-10-20 22:35:26 +02:00
Carlos Valente 71c468d069 refactor: session resource 2024-10-20 22:35:26 +02:00
Carlos Valente cf40001a2a improve style 2024-10-19 20:34:00 +02:00
arc-alex d458928090 fix external path and remove unneeded stuff 2024-10-19 20:34:00 +02:00
Carlos Valente 76e12af116 docs: add initial documentation to external assets 2024-10-19 20:34:00 +02:00
Carlos Valente b577057768 refactor: notes as data source 2024-10-19 20:33:36 +02:00
Carlos Valente dd4a165b08 refactor: add nif to navigation 2024-10-19 20:33:20 +02:00
Carlos Valente ab847a324b refactor: timer strategy is default 2024-10-19 14:31:58 +02:00
Carlos Valente cc5c097ddc refactor: tooltips for time locks 2024-10-19 14:31:58 +02:00
jwetzell ea2330e23b add invisible shortcuts for zooming in 2024-10-18 19:40:21 +02:00
jwetzell c5870452e3 make custom fields case sensitive (#1242)
* remove custom field lowercasing

* don't allow custom field form to submit duplicate field

* remove unused

* update custom field tests

* also keep upper case when editing

* show key in UI

* also kep upper case when creating a new field

* fix test

* allow old forced case keys to stay as is

* allow space

* add extension to import

* supply initial key to CustomFieldForm

* refactor: improve element contrast

* refactor: style and composition

* consistent use of `customFieldLabelToKey`

* fix CopyTag

* remove log

---------

Co-authored-by: arc-alex <ac@omnivox.dk>
Co-authored-by: Carlos Valente <carlosvalente@pm.me>
2024-10-18 18:53:23 +02:00
Alex Christoffer Rasmussen b73b529c00 hide seconds toggle in cuesheet (#1272) 2024-10-18 13:08:34 +02:00
Carlos Valente 8ef807d6cb refactor: items active by default 2024-10-16 16:34:57 +02:00
Carlos Valente 1d4ddea596 chore: extract views directory 2024-10-15 22:28:50 +02:00
Carlos Valente 4cba970cda feat: add loader to views 2024-10-15 22:28:50 +02:00
Carlos Valente bad9dab0a0 refactor: compound timeline 2024-10-15 22:28:50 +02:00
Carlos Valente f11218a757 refactor: control container growth 2024-10-15 22:28:50 +02:00
Carlos Valente 77e87e1d0c refactor: make message service stateless 2024-10-15 16:11:53 +02:00
Carlos Valente a4aa513d1f refactor: share store initial state 2024-10-15 16:11:53 +02:00
Carlos Valente b73448f827 feat: implement colour picker 2024-10-15 16:01:42 +02:00
Carlos Valente 687ad68726 refactor: recover style override 2024-10-15 16:01:42 +02:00
Carlos Valente 089eba3877 refactor: param type colour 2024-10-15 16:01:42 +02:00
Carlos Valente 24b0cfd24e chore: consistent naming in externals 2024-10-15 15:51:01 +02:00
Carlos Valente 5950551da2 refactor: simplify setup of asset paths 2024-10-15 15:51:01 +02:00
Carlos Valente e46948772e refactor: network interfaces can be copied or followed 2024-10-13 20:39:58 +02:00
Carlos Valente 78fa1df99c refactor: blink style 2024-10-12 11:02:26 +02:00
Alex Christoffer Rasmussen 0500368982 Revert http test (#1257)
* Revert "refactor: allow messages not starting with http"

This reverts commit 887e5c448e.

* Revert "refactor: allow messages not starting with http"

This reverts commit 6ce275da7a.
2024-10-12 10:52:14 +02:00
Carlos Valente 2d53323f60 refactor: unify time to start logic 2024-10-11 16:01:44 +02:00
Carlos Valente 08ca3cd3a5 fix: calculate time to start 2024-10-11 16:01:44 +02:00
Carlos Valente 16fd44a441 refactor: allow aux change direction while playing 2024-10-11 16:01:22 +02:00
Carlos Valente 9a6ca7ac67 refactor: show electron dev tools 2024-10-11 13:26:15 +02:00
Carlos Valente 2c63bbff76 refactor: arrow used in new window navigation 2024-10-11 13:26:15 +02:00
Carlos Valente d2c34757f9 refactor: navigate in electron 2024-10-11 13:26:15 +02:00
Carlos Valente aeadc78578 refactor: remove unused db 2024-10-11 13:26:15 +02:00
Carlos Valente bf98f9792a feat: open asset directories 2024-10-11 13:26:15 +02:00
Carlos Valente 6d2b59ef73 refactor: collocate view navigation 2024-10-11 13:26:15 +02:00
Carlos Valente 0a53018930 refactor: improve native menu links 2024-10-11 13:26:15 +02:00
Carlos Valente f1cac8d1c4 feat: allow download of current project 2024-10-11 13:26:15 +02:00
Carlos Valente cfb76153a0 refactor: create project form state from params 2024-10-11 13:26:15 +02:00
Alex Christoffer Rasmussen fee2021ad7 Fix: time-to-end offset (#1252)
* flip the sign correctly for time-to-end offset
2024-10-10 16:20:59 +02:00
Carlos Valente db956f7955 refactor: await init rundown 2024-10-09 20:15:10 +02:00
Carlos Valente 887e5c448e refactor: allow messages not starting with http 2024-10-09 19:19:20 +02:00
Carlos Valente 6ce275da7a refactor: allow messages not starting with http 2024-10-09 19:19:20 +02:00
Carlos Valente 213f516f71 refactor: message does not have negative style 2024-10-09 19:18:47 +02:00
Alex Christoffer Rasmussen 86e6f8b58c bump version to 3.6.1 (#1237) 2024-10-03 14:50:05 +02:00
Carlos Valente 1de062a38e refactor: remove alt shortcut 2024-10-03 08:35:56 +02:00
Carlos Valente eb7a30fcf3 refactor: improve resizing of column 2024-10-02 22:34:19 +02:00
Carlos Valente e2cbe43a2b fix: form element alignment in form 2024-10-02 21:57:52 +02:00
Carlos Valente 7877af571f fix: apply delay 2024-10-02 21:44:03 +02:00
Carlos Valente 086bd5b644 docs: add discord link 2024-10-01 08:18:19 +02:00
Carlos Valente f045cf2292 bump version to 3.6.0 2024-09-28 20:41:21 +02:00
Alex Christoffer Rasmussen 78e2f217a4 handle timer type whitespace (#1225) 2024-09-28 20:34:04 +02:00
Carlos Valente 8a5cef79a8 refactor: simplify secondary styles 2024-09-27 20:14:58 +02:00
Carlos Valente 4d8bea2940 feat: timer preview 2024-09-27 20:14:58 +02:00
Carlos Valente fa709dc9be fix: issue where event next was stale 2024-09-24 22:25:45 +02:00
Carlos Valente 177c9a35ec feat: electron links open in browser 2024-09-24 13:46:09 +02:00
Carlos Valente 7f7691a452 fix: stop integration if output disabled 2024-09-23 19:39:19 +02:00
Carlos Valente 92a16ea33b refactor: migrate fit text logic 2024-09-23 16:56:15 +02:00
Carlos Valente bc6b7c5596 refactor: fit user message in screen 2024-09-23 16:56:15 +02:00
Carlos Valente 66083813f9 refactor: allow local shutdown 2024-09-23 16:55:22 +02:00
Carlos Valente d8cf1abb37 docs: warn of shutdown conditions 2024-09-23 16:55:22 +02:00
Carlos Valente 5b471980f5 refactor: enable timer type and end action default settings 2024-09-09 10:15:55 +02:00
1330 changed files with 84230 additions and 41352 deletions
+5
View File
@@ -0,0 +1,5 @@
# CodeGraph data files — local to each machine, not for committing.
# Ignore everything in .codegraph/ except this file itself, so transient
# files (the database, daemon.pid, sockets, logs) never show up in git.
*
!.gitignore
+3 -3
View File
@@ -1,5 +1,5 @@
# Ignore config files
.prettier*
# Ignore formatter config files
.oxfmt*
# Ignore install stuff
**/yarn.lock
@@ -16,7 +16,7 @@
# Ignore build folders
node_modules
dist
**/node_modules
# Ignore default volumes created by running docker compose up
ontime-db
-1
View File
@@ -1 +0,0 @@
"ONTIME_VERSION.js"
-53
View File
@@ -1,53 +0,0 @@
{
"root": true,
"parserOptions": {
"ecmaVersion": 2020
},
"env": {
"es6": true,
"jest": true
},
"parser": "@typescript-eslint/parser",
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier", "eslint-config-prettier"],
"plugins": ["@typescript-eslint", "prettier"],
"overrides": [
{
"files": ["e2e/**/**.spec.ts", "e2e/**/**.test.ts"],
"extends": ["plugin:playwright/playwright-test"]
}
],
"rules": {
"no-useless-concat": "warn",
"prefer-template": "warn",
"no-throw-literal": "error",
"no-console": [
"warn",
{
"allow": ["warn", "error"]
}
],
"no-restricted-imports": [
"error",
{
"patterns": ["ontime-types/src/*", "ontime-utils/src/*"]
}
],
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_",
"destructuredArrayIgnorePattern": "^_",
"varsIgnorePattern": "^_"
}
],
"prettier/prettier": [
"warn",
{
"endOfLine": "auto"
}
]
}
}
+1 -1
View File
@@ -1,2 +1,2 @@
github: cpvalente
buy_me_a_coffee: cpvalente
buy_me_a_coffee: cpvalente
Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 KiB

After

Width:  |  Height:  |  Size: 149 KiB

-115
View File
@@ -1,115 +0,0 @@
name: Ontime build
on:
push:
tags: [ "*" ]
workflow_dispatch:
jobs:
build_macos:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build project packages
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: pnpm build
- name: Electron - Build app
env:
APPLE_ID: ${{ secrets.APPLEID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLEIDPASS }}
APPLE_TEAM_ID: ${{ secrets.TEAMID }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
CSC_LINK: ${{ secrets.CSC_LINK }}
run: pnpm dist-mac
timeout-minutes: 60
- name: Release
uses: softprops/action-gh-release@v1
with:
files: |
./apps/electron/dist/ontime-macOS-x64.dmg
./apps/electron/dist/ontime-macOS-arm64.dmg
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build_windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build project packages
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: pnpm build
- name: Electron - Build app
run: pnpm dist-win
- name: Release
uses: softprops/action-gh-release@v1
with:
files: './apps/electron/dist/ontime-win64.exe'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build_ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build project packages
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: pnpm build
- name: Electron - Build app
run: pnpm dist-linux
- name: Release
uses: softprops/action-gh-release@v1
with:
files: './apps/electron/dist/ontime-linux.AppImage'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+21 -13
View File
@@ -12,21 +12,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Use Node.js
uses: actions/setup-node@v4
# This step is only needed to setup the permissions to update npm as pnpm will setup the correct node version
- uses: actions/setup-node@v6
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Setup pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v6
- name: Get pnpm store directory
run: echo "PNPM_STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v5
with:
version: 9
path: ${{ env.PNPM_STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
@@ -38,16 +43,19 @@ jobs:
- name: Copy server
run: mkdir -p apps/cli/server && cp apps/server/dist/index.cjs apps/cli/server/index.cjs
- name: Copy client
run: cp -R apps/client/build apps/cli/client
- name: Copy external
run: cp -R apps/server/src/external apps/cli/external
# This will be included in v24 of NodeJS so when the project upgrades to that this can be removed
- name: Install newer version of npm
run: npm install -g npm@11
- name: Publish to NPM
run: pnpm publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true
working-directory: ./apps/cli
+48 -29
View File
@@ -6,43 +6,62 @@ on:
workflow_dispatch:
jobs:
publish_docker:
runs-on: ubuntu-latest
env:
CI: ''
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Docker Login
uses: docker/login-action@v2.1.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup pnpm
uses: pnpm/action-setup@v6
- name: Docker Setup Buildx
uses: docker/setup-buildx-action@v2.5.0
- name: Get pnpm store directory
run: echo "PNPM_STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Build and push stable release
if: github.event.release.prerelease == false
uses: docker/build-push-action@v4.0.0
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
# Push is a shorthand for --output=type=registry
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/ontime:${{ github.event.release.tag_name }} , ${{ secrets.DOCKERHUB_USERNAME }}/ontime:latest
- name: Setup pnpm cache
uses: actions/cache@v5
with:
path: ${{ env.PNPM_STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Build and push pre-release
if: github.event.release.prerelease == true
uses: docker/build-push-action@v4.0.0
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
# Push is a shorthand for --output=type=registry
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/ontime:${{ github.event.release.tag_name }} , ${{ secrets.DOCKERHUB_USERNAME }}/ontime:nightly
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build project packages
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: pnpm build:docker
- name: Docker Login
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker Setup Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
- name: Setup Docker metadata
id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with:
images: ${{ secrets.DOCKERHUB_USERNAME }}/ontime
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}
type=raw,value=nightly,enable=${{ github.event.release.prerelease == true }}
- name: Build and push stable release
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
# Push is a shorthand for --output=type=registry
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+142
View File
@@ -0,0 +1,142 @@
name: Ontime build
on:
push:
tags: ['*']
workflow_dispatch:
jobs:
build_macos:
runs-on: macOS-26
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
- name: Setup pnpm
uses: pnpm/action-setup@v6
- name: Get pnpm store directory
run: echo "PNPM_STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v5
with:
path: ${{ env.PNPM_STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Electron - Build app
env:
APPLE_ID: ${{ secrets.APPLEID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLEIDPASS }}
APPLE_TEAM_ID: ${{ secrets.TEAMID }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
CSC_LINK: ${{ secrets.CSC_LINK }}
run: pnpm dist-mac --env-mode=loose
timeout-minutes: 60
- name: Upload macOS build artifacts
uses: actions/upload-artifact@v6
if: always()
with:
name: ontime-macos-build
path: |
./apps/electron/dist/ontime-macOS-x64.dmg
./apps/electron/dist/ontime-macOS-arm64.dmg
retention-days: 1
- name: Release
uses: softprops/action-gh-release@v2
if: github.ref_type == 'tag'
with:
files: |
./apps/electron/dist/ontime-macOS-x64.dmg
./apps/electron/dist/ontime-macOS-arm64.dmg
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build_windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
- name: Setup pnpm
uses: pnpm/action-setup@v6
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Electron - Build app
run: pnpm dist-win
- name: Upload Windows build artifacts
uses: actions/upload-artifact@v6
if: always()
with:
name: ontime-windows-build
path: |
./apps/electron/dist/ontime-win64.exe
./apps/electron/dist/win-unpacked/
retention-days: 1
- name: Release
uses: softprops/action-gh-release@v2
if: github.ref_type == 'tag'
with:
files: './apps/electron/dist/ontime-win64.exe'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build_ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
- name: Setup pnpm
uses: pnpm/action-setup@v6
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Electron - Build app
run: pnpm dist-linux
- name: Upload Linux build artifacts
uses: actions/upload-artifact@v6
if: always()
with:
name: ontime-linux-build
path: |
./apps/electron/dist/ontime-linux-x86_64.AppImage
./apps/electron/dist/ontime-linux-arm64.AppImage
./apps/electron/dist/ontime-linux-armv7l.AppImage
retention-days: 1
- name: Release
uses: softprops/action-gh-release@v2
if: github.ref_type == 'tag'
with:
files: |
./apps/electron/dist/ontime-linux-x86_64.AppImage
./apps/electron/dist/ontime-linux-arm64.AppImage
./apps/electron/dist/ontime-linux-armv7l.AppImage
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+48
View File
@@ -0,0 +1,48 @@
name: Ontime Resolver build
on:
workflow_dispatch:
jobs:
build_resolver:
permissions:
id-token: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# This step is only needed to setup the permissions to update npm as pnpm will setup the correct node version
- uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
- name: Setup pnpm
uses: pnpm/action-setup@v6
- name: Get pnpm store directory
run: echo "PNPM_STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v5
with:
path: ${{ env.PNPM_STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build project packages
run: pnpm build:resolver
# This will be included in v24 of NodeJS so when the project upgrades to that this can be removed
- name: Install newer version of npm
run: npm install -g npm@11
- name: Publish to NPM
run: pnpm publish --access public --no-git-checks
env:
NPM_CONFIG_PROVENANCE: true
working-directory: ./apps/resolver
+54 -48
View File
@@ -13,93 +13,99 @@ jobs:
CI: ''
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 20
node-version-file: '.nvmrc'
- name: Setup pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v6
- name: Get pnpm store directory
run: echo "PNPM_STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v5
with:
version: 9
path: ${{ env.PNPM_STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
# Run code quality per package
- name: React - Run linter + TypeScript checks
if: always()
run: pnpm lint && tsc --noEmit
working-directory: ./apps/client
# Run code quality
- name: Run formatter
run: pnpm format:check
- name: Server - Run linter + TypeScript checks
if: always()
run: pnpm lint && tsc --noEmit
working-directory: ./apps/server
- name: Utils - Run linter + TypeScript checks
if: always()
run: pnpm lint && tsc --noEmit
working-directory: ./packages/utils
- name: Types - Run linter
if: always()
- name: Run linter
run: pnpm lint
working-directory: ./packages/types
- name: Run TypeScript checks
run: pnpm typecheck
# We choose to run tests separately
- name: React - Run unit tests
- name: Run unit tests
if: always()
run: pnpm test:pipeline
working-directory: ./apps/client
- name: Server - Run unit tests
if: always()
run: pnpm test:pipeline
working-directory: ./apps/server
- name: Utils - Run unit tests
if: always()
run: pnpm test:pipeline
working-directory: ./packages/utils
run: pnpm test
e2e-test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 20
node-version-file: '.nvmrc'
- name: Setup pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v6
- name: Get pnpm store directory
run: echo "PNPM_STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v5
with:
version: 9
path: ${{ env.PNPM_STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build client
run: pnpm build:local
- name: Get installed Playwright version
run: echo "PLAYWRIGHT_VERSION=$(pnpm ls @playwright/test --parseable | cut -s -d '@' -f3 | cut -d '/' -f1)" >> $GITHUB_ENV
- name: Cache playwright binaries
uses: actions/cache@v5
with:
path: |
~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
restore-keys: |
${{ runner.os }}-playwright-
- run: pnpm playwright install --with-deps
if: steps.playwright-cache.outputs.cache-hit != 'true'
- name: Install Playwright Browsers
run: npx playwright install --with-deps
run: pnpm playwright install --with-deps
- name: Run Playwright tests
run: pnpm e2e
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
with:
name: automated-screenshots
path: automated-screenshots/
+13 -4
View File
@@ -28,23 +28,32 @@ test-results
playwright-report
/playwright/.cache/
automated-screenshots
e2e/tests/fixtures/tmp/*
# production
build/
dist/
*.tsbuildinfo
# bundled assets
translations.json
override.css
**/external/demo/index.html
# working stuff
**/TODO.md
**.local.**
# docker utils
ontime-db
ontime-external/
# working database
apps/server/src/preloaded-db/db.json
ontime-data/
# versioning file
**/ONTIME_VERSION.js
# temporary write files
**.tmp
**.tmp
# Claude Code metadata
.claude/
-5
View File
@@ -1,5 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
-51
View File
@@ -1,51 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="CssUnknownProperty" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myCustomPropertiesEnabled" value="true" />
<option name="myIgnoreVendorSpecificProperties" value="false" />
<option name="myCustomPropertiesList">
<value>
<list size="3">
<item index="0" class="java.lang.String" itemvalue="scrollbar-color" />
<item index="1" class="java.lang.String" itemvalue="scrollbar-width" />
<item index="2" class="java.lang.String" itemvalue="app-region" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="HttpUrlsUsage" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredUrls">
<list>
<option value="http://localhost" />
<option value="http://127.0.0.1" />
<option value="http://0.0.0.0" />
<option value="http://www.w3.org/" />
<option value="http://json-schema.org/draft" />
<option value="http://java.sun.com/" />
<option value="http://xmlns.jcp.org/" />
<option value="http://javafx.com/javafx/" />
<option value="http://javafx.com/fxml" />
<option value="http://maven.apache.org/xsd/" />
<option value="http://maven.apache.org/POM/" />
<option value="http://www.springframework.org/schema/" />
<option value="http://www.springframework.org/tags" />
<option value="http://www.springframework.org/security/tags" />
<option value="http://www.thymeleaf.org" />
<option value="http://www.jboss.org/j2ee/schema/" />
<option value="http://www.jboss.com/xml/ns/" />
<option value="http://www.ibm.com/webservices/xsd" />
<option value="http://activemq.apache.org/schema/" />
<option value="http://schema.cloudfoundry.org/spring/" />
<option value="http://schemas.xmlsoap.org/" />
<option value="http://cxf.apache.org/schemas/" />
<option value="http://primefaces.org/ui" />
<option value="http://tiles.apache.org/" />
<option value="http://__IP__" />
</list>
</option>
</inspection_tool>
<inspection_tool class="Stylelint" enabled="true" level="ERROR" enabled_by_default="true" />
</profile>
</component>
+1 -1
View File
@@ -1 +1 @@
v20.15.1
v22.22.3
+25
View File
@@ -0,0 +1,25 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"endOfLine": "lf",
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"jsxSingleQuote": true,
"printWidth": 120,
"sortPackageJson": false,
"sortImports": {},
"ignorePatterns": [
"build",
"coverage",
"dist",
"node_modules",
"playwright-report",
"pnpm-lock.yaml",
"**/*.toml",
"**/*.json",
"!tsconfig.common.json",
"!turbo.json",
"!package.json"
]
}
+37
View File
@@ -0,0 +1,37 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"categories": {
"correctness": "warn",
"suspicious": "off",
"pedantic": "off",
"perf": "warn",
"style": "off",
"restriction": "off",
"nursery": "off"
},
"rules": {
"no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_",
"destructuredArrayIgnorePattern": "^_",
"caughtErrors": "none"
}
],
"no-restricted-imports": [
"error",
{
"patterns": ["ontime-types/src/*", "ontime-utils/src/*"]
}
]
},
"ignorePatterns": [
"**/node_modules/**",
"**/dist/**",
"apps/client/build/**",
"apps/electron/assets/**",
"**/ONTIME_VERSION.js"
]
}
-10
View File
@@ -1,10 +0,0 @@
build
coverage
dist
node_modules
playwright-report
**/*.toml
**/*.yml
**/*.json
-9
View File
@@ -1,9 +0,0 @@
{
"endOfLine": "lf",
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"jsxSingleQuote": true,
"printWidth": 120
}
+44 -30
View File
@@ -2,16 +2,16 @@
Ontime consists of 3 distinct parts
- __client__: A React app for Ontime's UI and web clients
- __electron__: An electron app which facilitates the cross-platform distribution of Ontime
- __server__: A node application which handles the domains services and integrations
- **client**: A React app for Ontime's UI and web clients
- **electron**: An electron app which facilitates the cross-platform distribution of Ontime
- **server**: A node application which handles the domains services and integrations
The steps below will assume you have locally installed the necessary dependencies.
Other dependencies will be installed as part of the setup
- __node__ (~20)
- __pnpm__ (~9)
- __docker__ (only necessary to run and build docker images)
- **node** (~22)
- **pnpm** (~10)
- **docker** (only necessary to run and build docker images)
## LOCAL DEVELOPMENT
@@ -21,18 +21,21 @@ Locally, we would need to run both the React client and the node.js server in de
From the project root, run the following commands
- __Install the project dependencies__ by running `pnpm i`
- __Run dev mode__ by running `turbo dev`
- **Install the project dependencies** by running `pnpm i`
- **Create a local build** by running `pnpm build`, this will populate local dependencies
- **Run dev mode** by running `pnpm dev` or `pnpm dev:electron` to get the electron window
### Debugging backend
To debug backend code in Node.js:
The previous command will start the development servers for both the client, server and electron applications.
Typically in dev mode we prefer to start these in separate terminals to help with error tracking and debugging.
- Open two separate terminals and navigate to the `apps/client` and `apps/server` directories.
- In each terminal, run the command `pnpm dev` to start the development servers for both the client and server
applications.
- If you need to set breakpoints and inspect the code execution, enable Node.js inspect mode by
running `pnpm dev:inspect`.
We do that by creating two terminals an running
- **Run the React UI** by running `pnpm dev --filter=ontime-ui`
- **Run the nodejs server** by running `pnpm dev --filter=ontime-server`
- If you need to set breakpoints and inspect the code execution, enable Node.js inspect mode by running `pnpm dev:inspect --filter=ontime-server`.
## TESTING
@@ -45,7 +48,7 @@ Generally we have 2 types of tests.
Unit tests are contained in mostly all the apps and packages (client, server and utils)
You can run unit tests by running turbo `turbo test:pipeline` from the project root.
You can run unit tests by running `pnpm test:pipeline` from the project root.
This will run all tests and close test runner.
Alternatively you can navigate to an app or project and run `pnpm test` to run those tests in watch mode
@@ -65,7 +68,7 @@ start the webserver with `pnpm dev:server`
Some other useful commands
- `pnpm e2e --ui` open playwright UI
- `pnpm e2e:ui` open playwright UI
- `pnpm e2e --headed` run tests with a visible browser window
## CREATE AN INSTALLABLE FILE (Windows | MacOS | Linux)
@@ -75,15 +78,12 @@ You can generate a distribution for your OS by running the following steps.
From the project root, run the following commands
- __Install the project dependencies__ by running `pnpm i`
- __Build the UI and server__ by running `turbo build:electron`
- __Create the package__ by running `turbo dist-win`, `turbo dist-mac` or `turbo dist-linux`
- **Install the project dependencies** by running `pnpm i`
- **Build the UI and server** by running `pnpm build`
- **Create the package** by running `pnpm dist-win`, `pnpm dist-mac` or `pnpm dist-linux`
The build distribution assets will be at `.apps/electron/dist`
Note: The MacOS build will only work in CI, locally it will fail due to notarisation issues.
Use the `turbo dist-mac:local` command to build a MacOS distribution locally.
## DOCKER
Ontime provides a docker-compose file to aid with building and running docker images.
@@ -91,17 +91,31 @@ While it should allow for a generic setup, it might need to be modified to fit y
From the project root, run the following commands
- __Build docker image from__ by running `docker build -t getontime/ontime`
- __Run docker image from compose__ by running `docker-compose up -d`
- **Build docker image from** by running `docker build -t getontime/ontime .`
- **Run docker image from compose** by running `docker-compose up -d`
Other useful commands
- __List running processes__ by running `docker ps`
- __Kill running process__ by running `docker kill <process-id>`
- **List running processes** by running `docker ps`
- **Kill running process** by running `docker kill <process-id>`
## General Info
## FORMATTING AND LINTING
# APP Building
Ontime uses [oxfmt](https://oxc.rs/docs/guide/usage/formatter.html) for formatting and [oxlint](https://oxc.rs/docs/guide/usage/linter.html) for linting. Look for an [oxc extension](https://oxc.rs/docs/guide/usage/linter/editors) for your editor of choice.
We build the app from app.js for almost all applications. The output file will still be named index.cjs. This is because of Electron.
Building the app from index.ts only applies for applications that don't use electron. index.ts will take over the initialization of the server and UI when electron isn't present.
- **Run formatter** by running `pnpm format`
- **Run linter** by running `pnpm lint`
## CONTRIBUTION GUIDELINES
If you want to propose changes to the codebase, please reach out before opening a Pull Request.
For new PRs, please follow the following checklist:
- [ ] You have updated and ran unit locally and they are passing. Unit tests are generally created for all utility functions and business logic
- [ ] You have ran code formatting and linting in all your changes
- [ ] The branch is clean and the commits are meaningfully separated and contain descriptive messages
- [ ] The PR body contains description and motivation for the changes
After this checklist is complete, you can request a review from one of the maintainers to get feedback and approval on the changes. \
We will review as soon as possible
+30 -35
View File
@@ -1,35 +1,30 @@
FROM node:20-bullseye AS builder
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN npm install -g pnpm@9.5.0
COPY . /app
WORKDIR /app
RUN pnpm --filter=ontime-ui --filter=ontime-server --filter=ontime-utils install --config.dedupe-peer-dependents=false --frozen-lockfile
RUN pnpm --filter=ontime-ui --filter=ontime-server run build:docker
FROM node:20-alpine
# Set environment variables
# Environment Variable to signal that we are running production
ENV NODE_ENV=docker
# Ontime Data path
ENV ONTIME_DATA=/data/
WORKDIR /app/
# Prepare UI
COPY --from=builder /app/apps/client/build ./client/
# Prepare Backend
COPY --from=builder /app/apps/server/dist/ ./server/
COPY --from=builder /app/apps/server/src/external/ ./external/
# Export default ports
EXPOSE 4001/tcp 8888/udp 9999/udp
CMD ["node", "server/docker.cjs"]
# Build and run commands
# !!! Note that this command needs pre-build versions of the UI and server apps
# docker buildx build . -t getontime/ontime
# docker run -p 4001:4001 -p 8888:8888/udp -p 9999:9999/udp -v ./ontime-db:/external/db/ -v ./ontime-styles:/external/styles/ getontime/ontime
ARG NODE_VERSION=22.22.2
FROM node:${NODE_VERSION}-alpine
# Set environment variables
# Environment Variable to signal that we are running production
ENV NODE_ENV=docker
# Ontime Data path
ENV ONTIME_DATA=/data/
RUN mkdir /app
WORKDIR /app/
# Prepare UI
COPY apps/client/build/ ./client/
# Prepare Backend
COPY apps/server/dist/ ./server/
COPY apps/server/src/external/ ./external/
COPY apps/server/src/user/ ./user/
COPY apps/server/src/html/ ./html/
# Export default ports
EXPOSE 4001/tcp 8888/udp 9999/udp
CMD ["node", "server/docker.cjs"]
# Build and run commands
# pnpm build:docker
# docker buildx build . -t getontime/ontime
# docker run -p 4001:4001 -p 8888:8888/udp -p 9999:9999/udp -v ./ontime-db:/data/ getontime/ontime
+40 -82
View File
@@ -2,25 +2,36 @@
![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/cpvalente/ontime/total)
![Docker Pulls](https://img.shields.io/docker/pulls/getontime/ontime)
![NPM Downloads](https://img.shields.io/npm/dy/%40getontime%2Fcli)
![Homebrew Cask Version](https://img.shields.io/homebrew/cask/v/ontime)
[![](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/cpvalente)
[![](https://img.shields.io/static/v1?label=Buy%20me%20a%20coffee&message=%E2%9D%A4&logo=buymeacoffee&color=%23fe8e86)](https://www.buymeacoffee.com/cpvalente)
## Download the latest release
<a href="https://www.buymeacoffee.com/cpvalente" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="32"></a>
- Download for <a href="https://github.com/cpvalente/ontime/releases/latest/download/ontime-win64.exe">Windows</a>
- Download for <a href="https://github.com/cpvalente/ontime/releases/latest/download/ontime-macOS-arm64.dmg">MacOS Arm</a>
- Download for <a href="https://github.com/cpvalente/ontime/releases/latest/download/ontime-macOS-x64.dmg">MacOS Intel</a>
- Download AppImage for <a href="https://github.com/cpvalente/ontime/releases/latest/download/ontime-linux.AppImage">Linux</a>
- Download for <a href="https://github.com/cpvalente/ontime/releases/latest/download/ontime-macOS-arm64.dmg">macOS (Apple Silicon)</a>
- Download for <a href="https://github.com/cpvalente/ontime/releases/latest/download/ontime-macOS-x64.dmg">macOS (Intel)</a>
- Download AppImage for <a href="https://github.com/cpvalente/ontime/releases/latest/download/ontime-linux-x86_64.AppImage">Linux (Intel / AMD 64-bit)</a>
- Download AppImage for <a href="https://github.com/cpvalente/ontime/releases/latest/download/ontime-linux-arm64.AppImage">Linux (ARM 64-bit, Raspberry Pi 4+)</a>
- Download AppImage for <a href="https://github.com/cpvalente/ontime/releases/latest/download/ontime-linux-armv7l.AppImage">Linux (ARM 32-bit, older Raspberry Pi)</a>
... or
- Get from <a href="https://hub.docker.com/r/getontime/ontime">Docker hub</a>
- Install from <a href="https://www.npmjs.com/package/@getontime/cli">NPM</a>
- Install from <a href="https://formulae.brew.sh/cask/ontime">Homebrew</a>
## Need help?
We do our best to have most topics covered by the documentation. However, if your question is not covered, you are welcome to [fill in a bug report in an issue](https://github.com/cpvalente/ontime/issues), [ask a question in GitHub discussions](https://github.com/cpvalente/ontime/discussions) or hop in the [discord server](https://discord.com/invite/eje3CSUEXm) for a chat.
## Using Ontime?
Let us know!
Ontime improves from the collaboration with its users. We would like to understand how you use Ontime and appreciate your feedback.
We would also like to include a testimonials section in our ✨new website✨. It would be great to showcase the diversity of users running Ontime.
# Ontime
Ontime is a browser-based application that manages event rundowns, scheduling, and cueing.
@@ -35,6 +46,17 @@ Ontime is made by entertainment and broadcast engineers and used by
- Theatres and opera houses
- Houses of worship
## Main features
- **Multiplatform**: Available as a Cloud service and for Windows, macOS, Linux, or self-hosted via Docker.
- **In any device**: Ontime is available to any device with a browser, eg: tablets, mobile phones, laptops, signage, media servers...
- **Team Collaboration**: Dedicated views for directors, operators, backstage, and signage.
- **Real-Time Updates**: Manage and communicate runtime delays effortlessly.
- **Automatable**: Ontime can be fully or partially controlled by an operator, or run standalone with the system clock
- **Flexible Integrations**: Use one of the APIs provided (OSC, HTTP, Websocket) or the available [Companion module](https://bitfocus.io/connections/getontime-ontime) to integrate into your workflow (vMix, disguise, Qlab, OBS)
... and a lot more ...
### For live environments
Ontime is designed for use in live environments. \
@@ -62,96 +84,29 @@ Ontime is made by video engineers and entertainment technicians.
## Using Ontime
Ontime can be started by downloading the latest release for your platform. \
Alternatively you can also use the docker image, available at [Docker Hub](https://hub.docker.com/r/getontime/ontime)
### Getting started
Once installed and running, any device in the network has access to Ontime.
The easiest way to start with Ontime is by leveraging our [Cloud service](https://getontime.no). \
This will give you immediate access to running instances of Ontime which are available to share with anyone with an internet connection.
Ontime provides different screens which allow for different types of interactions with the data. These are called
views. \
Each view in Ontime focuses on empowering a specific role or achieving a particular task.
Alternatively, you can run Ontime locally for free by downloading the latest release for your platform or using the docker image, available at [Docker Hub](https://hub.docker.com/r/getontime/ontime)
You can access the different views by reaching the ontime server, in your browser, at (_default port
4001_) `http://localhost:4001` or `http://192.168.1.3:4001`
Once installed and running, any device that shares the same network as Ontime will have access to Ontime.
```
For the backstage views
-------------------------------------------------------------
IP.ADDRESS:4001/timer > Presenter / Stage timer view
IP.ADDRESS:4001/minimal > Simple timer view
IP.ADDRESS:4001/clock > Simple clock view
IP.ADDRESS:4001/backstage > Stage Manager / Backstage view
IP.ADDRESS:4001/countdown > Countdown to anything
IP.ADDRESS:4001/studio > Studio Clock
```
More information is available [in our docs](https://docs.getontime.no)
```
For the public views
-------------------------------------------------------------
IP.ADDRESS:4001/public > Public / Foyer view
IP.ADDRESS:4001/lower > Lower Thirds
```
## Continued development
```
For production views
-------------------------------------------------------------
IP.ADDRESS:4001/editor > the control interface, same as the app
IP.ADDRESS:4001/cuesheet > realtime cuesheets for collaboration
IP.ADDRESS:4001/operator > automated views for operators
```
More documentation is available [in our docs](https://docs.getontime.no)
## Main features
- [x] Distribute data over network and render it in the browser
- [x] Collaborative
- [x] Extendable
- [x] Send messages to different screen types
- [x] Differentiate between backstage and public data
- [x] Workflow for managing delays
- [x] Rich protocol integrations for Control and Feedback
- [x] For servers: use docker to run Ontime in in a server, configure from a browser anywhere
- [x] Multi-platform (available on Windows, MacOS and Linux)
- [x] Companion integration [follow link](https://bitfocus.io/connections/getontime-ontime)
## Roadmap
### Continued development
Ontime is under active development. We continue adding and tweaking features in collaboration with users.
Ontime is under active development. We continue adding and improving features in collaboration with users.
Have an idea? Reach out via [email](mail@getontime.no)
or [open an issue](https://github.com/cpvalente/ontime/issues/new)
### Issues
## Issues
We use Github's issue tracking for bug reporting and feature requests. \
Found a bug? [Open an issue](https://github.com/cpvalente/ontime/issues/new).
#### Unsigned App
When installing the app you would see warning screens from the Operating System like:
in Windows
`Microsoft Defender SmartScreen prevented an unrecognised app from starting. Running this app might put your PC at risk.`
or in Linux
`Could Not Display "ontime-linux.AppImage`
We currently only sign MacOS releases. \
Purchasing the certificates for both Mac and Windows would mean a recurrent expense which we are not able to cover.
You can circumvent this by allowing the execution of the app manually.
- In Windows: click `more` -> `Run Anyway`
- In Linux: right-click the AppImage file: `Properties` -> `Permissions` -> `Allow Executing File as a Program`
If you have tips on how to improve this or would like to sponsor the code signing,
please [open an issue](https://github.com/cpvalente/ontime/issues/new)
## Contributing
Looking to contribute? All types of help are appreciated, from coding to testing and feature specification.
@@ -162,7 +117,10 @@ Information about the project setup can be found in the [development documentati
## Links
See the [Ontime website](https://getontime.no) here and the link to the [documentation](https://docs.getontime.no)
- [Ontime website](https://getontime.no)
- [Documentation](https://docs.getontime.no)
- [Video tutorials](https://www.youtube.com/@ontimeapp)
- [Ontime discord server](https://discord.com/invite/eje3CSUEXm)
## License
+5
View File
@@ -1,19 +1,24 @@
# Terms of sponsorship
Ontime does not exist as a fiscal entity. The sponsorship recipient is the project owner. In this light, contributors are funding the project owner's capacity to work on the project, not the project itself. As a contribution recipient, the code owner reserves the right to redistribute.
## Why sponsor
Ontime is and will remain free, regardless of your decision to contribute.
Regardless, you may choose to provide a monetary contribution:
### Improve user experience
Certain parts of the application user experience are tied to our capacity to pay for services. App signing and Google Sheets integration are good examples of this.
### Cool stuff
We may choose not to implement some features. This could be related to the complexity required, to the skill set, or to the fact that we just feel it doesn't improve the app for a significant percentage of users. In those cases, a feature may be considered "not worth it," regardless of its pertinence.
As a user, you can choose to sponsor a particular feature or donate to the app so that we can create bounties for features.
### Priorities
Monetary contributions are most immediately needed to offset the costs of presenting and distributing Ontime, i.e., hosting, domains, and certificates.
Additional funding will be considered according to the needs at the time.
-16
View File
@@ -1,16 +0,0 @@
{
"parserOptions": {
"sourceType": "module"
},
"env": {
"browser": true,
"node": true
},
"extends": [
"eslint:recommended"
],
"plugins": [],
"rules": {
"@typescript-eslint/no-var-requires": "off"
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"$schema": "../../node_modules/oxlint/configuration_schema.json",
"extends": ["../../.oxlintrc.json"],
"plugins": ["unicorn", "typescript", "oxc", "vitest"]
}
-9
View File
@@ -1,9 +0,0 @@
{
"endOfLine": "lf",
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"jsxSingleQuote": true,
"printWidth": 120
}
+9 -1
View File
@@ -1,6 +1,7 @@
# Ontime CLI
![Ontime](https://github.com/cpvalente/ontime/blob/9e63261b350e5b7b61c7ffb17c9e75a6c5a8c711/.github/aux-images/editor.png)
> Ontime is an application for managing rundown and event timers.
Congratulations! You got this far into Ontime's rabbit hole and want to manage your installation.
@@ -10,33 +11,40 @@ The CLI tool is a fast and lightweight way of installing Ontime and is perfect f
## Getting started
### Prerequisites
You will need to have installed the correct version of node.js
Please check [the app engines declaration](https://github.com/cpvalente/ontime/blob/master/package.json) for the correct version.
### Running Ontime
To get Ontime running, all you need to do is install it in your system using your package manager of choice:
Install globally in your system
Install globally in your system
```bash
npm install -g @getontime/cli
```
... and run using the installed script
```bash
ontime
```
Or install and run ontime (the installation here is temporary for the duration of the session)
```bash
npx install @getontime/cli
```
## Links
- [Ontime's repository](https://github.com/cpvalente/ontime)
- [Ontime's documentation](https://docs.getontime.no/)
- [Ontime's website](https://getontime.no/)
## Sponsoring
You can help the development of this project or say thank you with a one time donation. \
See the [terms of donations](https://github.com/cpvalente/ontime/blob/master/SPONSOR.md)
+21 -26
View File
@@ -1,27 +1,22 @@
{
"name": "@getontime/cli",
"version": "3.5.1",
"author": "Carlos Valente",
"description": "Time keeping for live events",
"repository": "https://github.com/cpvalente/ontime",
"keywords": [
"lighdev",
"ontime",
"timer"
],
"license": "AGPL-3.0-only",
"main": "main.js",
"bin": {
"ontime": "main.js"
},
"files": [
"client",
"external",
"server"
],
"devDependencies": {
"eslint": "catalog:",
"eslint-config-prettier": "catalog:",
"prettier": "catalog:"
}
}
"name": "@getontime/cli",
"version": "4.11.0",
"author": "Carlos Valente",
"description": "Time keeping for live events",
"repository": "https://github.com/cpvalente/ontime",
"keywords": [
"lighdev",
"ontime",
"timer"
],
"license": "AGPL-3.0-only",
"main": "main.js",
"bin": {
"ontime": "main.js"
},
"files": [
"client",
"external",
"server"
]
}
-2
View File
@@ -1,2 +0,0 @@
node_modules
build
-141
View File
@@ -1,141 +0,0 @@
{
"settings": {
"react": {
"version": "detect"
}
},
"env": {
"browser": true,
"node": true
},
"extends": [
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:@tanstack/eslint-plugin-query/recommended"
],
"plugins": [
"react",
"testing-library",
"simple-import-sort",
"@tanstack/query"
],
"rules": {
"react/jsx-no-bind": [
"error",
{
"ignoreRefs": true,
"allowArrowFunctions": true,
"allowFunctions": false,
"allowBind": false,
"ignoreDOMComponents": true
}
],
"react/jsx-boolean-value": [
"error",
"never",
{
"always": []
}
],
"react/jsx-handler-names": [
"off",
{
"eventHandlerPrefix": "handle",
"eventHandlerPropPrefix": "on"
}
],
"react/no-danger": "warn",
"react/no-deprecated": [
"error"
],
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off",
"react/jsx-no-undef": "error",
"react/jsx-closing-tag-location": "warn",
"react/jsx-no-useless-fragment": "warn",
"react/no-unescaped-entities": "error",
"react/jsx-tag-spacing": [
"error",
{
"closingSlash": "never",
"beforeSelfClosing": "always",
"afterOpening": "never",
"beforeClosing": "never"
}
],
"react/jsx-space-before-closing": [
"off",
"always"
],
"react/jsx-curly-brace-presence": [
"error",
{
"props": "never",
"children": "never"
}
],
"react/destructuring-assignment": [
"error",
"always"
],
"react/no-unsafe": "off",
"react/prop-types": [
"error",
{
"ignore": [],
"customValidators": [],
"skipUndeclared": true
}
],
"simple-import-sort/imports": "warn",
"simple-import-sort/exports": "warn"
},
"overrides": [
// override "simple-import-sort" config
{
"files": [
"*.js",
"*.jsx",
"*.ts",
"*.tsx"
],
"rules": {
"simple-import-sort/imports": [
"error",
{
"groups": [
// Packages `react` related packages come first.
[
"^react",
"^@?\\w"
],
// Internal packages.
[
"^(@|components)(/.*|$)"
],
// Side effect imports.
[
"^\\u0000"
],
// Parent imports. Put `..` last.
[
"^\\.\\.(?!/?$)",
"^\\.\\./?$"
],
// Other relative imports. Put same-folder imports and `.` last.
[
"^\\./(?=.*/)(?!/?$)",
"^\\.(?!/?$)",
"^\\./?$"
],
// Style imports.
[
"^.+\\.?(css)$"
]
]
}
]
}
}
]
}
+5
View File
@@ -0,0 +1,5 @@
{
"$schema": "../../node_modules/oxlint/configuration_schema.json",
"extends": ["../../.oxlintrc.json"],
"plugins": ["unicorn", "typescript", "oxc", "react", "vitest"]
}
-9
View File
@@ -1,9 +0,0 @@
{
"endOfLine": "lf",
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"jsxSingleQuote": true,
"printWidth": 120
}
+28 -35
View File
@@ -1,37 +1,30 @@
<!doctype html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<link rel='icon' href='/favicon.ico' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<meta name='theme-color' content='#121212' />
<meta name='ontime' content='ontime - event timer manager' />
<link rel='apple-touch-icon' href='/logo192.png' />
<link
rel='icon'
type='image/png'
sizes='32x32'
href='/favicon-32x32.png'
/>
<link
rel='icon'
type='image/png'
sizes='16x16'
href='/favicon-16x16.png'
/>
<link rel='manifest' href='/site.webmanifest' />
<link rel='manifest' href='/manifest.json' />
<title>ontime</title>
<style>
body,
html {
background-color: rgba(0, 0, 0, 0) !important;
}
</style>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id='root'></div>
<script type='module' src='/src/index.tsx'></script>
</body>
<html lang="en">
<head>
<meta charset="utf-8" />
<base href="/" />
<link rel="icon" href="favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#101010" />
<meta name="ontime" content="ontime - time keeping for live events" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link rel="apple-touch-icon" href="ontime-logo.png" />
<link rel="icon" type="image/png" href="ontime-logo.png" />
<link rel="manifest" href="site.webmanifest" />
<link rel="manifest" href="manifest.json" />
<meta name="robots" content="noindex" />
<title>ontime</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script>
if (/\/timer\/?$/.test(window.location.pathname) && typeof Promise.any !== 'function') {
var b = document.querySelector('base');
var base = b ? (b.getAttribute('href') || '').replace(/\/$/, '') : '';
window.location.replace(base + '/timer-legacy' + window.location.search);
}
</script>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>
+44 -59
View File
@@ -1,57 +1,57 @@
{
"name": "ontime-ui",
"version": "3.5.1",
"version": "4.11.0",
"private": true,
"type": "module",
"dependencies": {
"@chakra-ui/react": "^2.7.0",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@base-ui/react": "1.6.0",
"@codemirror/commands": "^6.0.0",
"@codemirror/lang-css": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@fontsource/open-sans": "^5.0.28",
"@mantine/hooks": "^7.6.2",
"@react-icons/all-files": "^4.1.0",
"@sentry/react": "^8.19.0",
"@tanstack/react-query": "^5.17.9",
"@tanstack/react-query-devtools": "^5.17.9",
"@tanstack/react-table": "^8.11.3",
"@fontsource/open-sans": "^5.2.7",
"@mantine/hooks": "^8.3.7",
"@sentry/react": "^10.59.0",
"@table-nav/react": "^0.0.7",
"@tanstack/react-query": "^5.101.0",
"@tanstack/react-query-devtools": "^5.101.0",
"@tanstack/react-table": "^8.21.3",
"@uiw/codemirror-theme-vscode": "^4.25.10",
"autosize": "^6.0.1",
"axios": "^1.2.0",
"color": "^4.2.3",
"csv-stringify": "^6.4.5",
"framer-motion": "^10.10.0",
"react": "^18.3.1",
"react-colorful": "^5.6.1",
"react-dom": "^18.3.1",
"axios": "^1.18.0",
"csv-stringify": "^6.6.0",
"qrcode": "^1.5.4",
"react": "^19.2.7",
"react-colorful": "^5.7.0",
"react-dom": "^19.2.7",
"react-fast-compare": "^3.2.2",
"react-hook-form": "^7.49.2",
"react-qr-code": "^2.0.12",
"react-router-dom": "^6.3.0",
"web-vitals": "^3.1.1",
"zustand": "^4.5.2"
"react-hook-form": "^7.80.0",
"react-icons": "5.6.0",
"react-router": "^8.0.1",
"react-virtuoso": "^4.18.7",
"zustand": "^5.0.14"
},
"scripts": {
"addversion": "node -p \"'export const ONTIME_VERSION = ' + JSON.stringify(require('../../package.json').version) + ';'\" > src/ONTIME_VERSION.js",
"postinstall": "pnpm addversion",
"dev": "cross-env BROWSER=none vite",
"dev:electron": "pnpm dev",
"lint": "oxlint --quiet --type-aware",
"typecheck": "tsc -p tsconfig.app.json --noEmit",
"build": "vite build",
"build:local": "cross-env NODE_ENV=local vite build",
"build:electron": "cross-env NODE_ENV=local vite build",
"build:docker": "vite build",
"build:localdocker": "cross-env NODE_ENV=local vite build",
"lint": "eslint . --quiet",
"test": "vitest",
"test:pipeline": "vitest run",
"cleanup": "rm -rf .turbo && rm -rf node_modules && rm -rf build",
"analyse": "npx vite-bundle-visualizer"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
"not op_mini all",
"safari >= 14.1"
],
"development": [
"last 1 chrome version",
@@ -60,36 +60,21 @@
]
},
"devDependencies": {
"@sentry/vite-plugin": "^2.16.1",
"@tanstack/eslint-plugin-query": "^5.8.4",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.1.1",
"@testing-library/user-event": "^14.1.1",
"@types/color": "^3.0.3",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"@types/testing-library__jest-dom": "^5.14.5",
"@typescript-eslint/eslint-plugin": "catalog:",
"@typescript-eslint/parser": "catalog:",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "catalog:",
"eslint-config-prettier": "catalog:",
"eslint-plugin-jest": "^28.6.0",
"eslint-plugin-prettier": "catalog:",
"eslint-plugin-react": "^7.32.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^8.0.0",
"eslint-plugin-testing-library": "^5.9.1",
"jsdom": "^21.1.0",
"@sentry/vite-plugin": "5.1.1",
"@types/qrcode": "^1.5.6",
"@types/react": "^19.1.12",
"@types/react-dom": "^19.1.9",
"@vitejs/plugin-legacy": "^8.0.1",
"@vitejs/plugin-react": "5.2.0",
"babel-plugin-react-compiler": "1.0.0",
"happy-dom": "^20.9.0",
"ontime-types": "workspace:*",
"ontime-utils": "workspace:*",
"prettier": "catalog:",
"sass": "^1.57.1",
"typescript": "catalog:",
"vite": "^5.2.11",
"vite-plugin-compression2": "^0.12.0",
"vite-plugin-svgr": "^4.2.0",
"vite-tsconfig-paths": "^4.3.1",
"vitest": "^1.6.0"
"vite": "8.0.1",
"vite-plugin-compression2": "2.5.1",
"vite-plugin-svgr": "4.5.0",
"vitest": "catalog:"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

+5 -12
View File
@@ -4,23 +4,16 @@
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
"src": "ontime-logo.png",
"type": "image/png"
}
],
"scope": "/",
"start_url": "/",
"scope": "./",
"start_url": "./",
"display": "",
"theme_color": "#121212",
"background_color": "#ffffff"
"background_color": "#101010"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+8 -1
View File
@@ -1 +1,8 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
{
"name": "",
"short_name": "",
"icons": [{ "src": "ontime-logo.png", "sizes": "295x295", "type": "image/png" }],
"theme_color": "#121212",
"background_color": "#101010",
"display": "standalone"
}
+15 -11
View File
@@ -1,29 +1,33 @@
import { BrowserRouter } from 'react-router-dom';
import { ChakraProvider } from '@chakra-ui/react';
import { Tooltip } from '@base-ui/react/tooltip';
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { BrowserRouter } from 'react-router';
// apply global axios config defaults
import './common/api/axios.config';
import AppRouter from './AppRouter';
import ErrorBoundary from './common/components/error-boundary/ErrorBoundary';
import IdentifyOverlay from './common/components/identify-overlay/IdentifyOverlay';
import { AppContextProvider } from './common/context/AppContext';
import { ontimeQueryClient } from './common/queryClient';
import { connectSocket } from './common/utils/socket';
import theme from './theme/theme';
import { baseURI } from './externals';
import KeepAwake from './features/keep-awake/KeepAwake';
import { TranslationProvider } from './translation/TranslationProvider';
import AppRouter from './AppRouter';
connectSocket();
function App() {
return (
<ChakraProvider disableGlobalStyle resetCSS theme={theme}>
<QueryClientProvider client={ontimeQueryClient}>
<AppContextProvider>
<BrowserRouter>
<QueryClientProvider client={ontimeQueryClient}>
<AppContextProvider>
<Tooltip.Provider>
<BrowserRouter basename={baseURI}>
<div className='App'>
<ErrorBoundary>
<TranslationProvider>
<IdentifyOverlay />
<KeepAwake />
<AppRouter />
</TranslationProvider>
</ErrorBoundary>
@@ -33,9 +37,9 @@ function App() {
<div id='identify-portal' />
</ErrorBoundary>
</BrowserRouter>
</AppContextProvider>
</QueryClientProvider>
</ChakraProvider>
</Tooltip.Provider>
</AppContextProvider>
</QueryClientProvider>
);
}
+205 -86
View File
@@ -1,99 +1,115 @@
import React from 'react';
import {
createRoutesFromChildren,
matchRoutes,
Navigate,
Route,
Routes,
useLocation,
useNavigationType,
} from 'react-router-dom';
import * as Sentry from '@sentry/react';
import { OntimeView, OntimeViewPresettable, URLPreset } from 'ontime-types';
import { ComponentType, Suspense, lazy, useEffect, useMemo } from 'react';
import { Navigate, Route, useLocation, useNavigate, useParams } from 'react-router';
import ViewNavigationMenu from './common/components/navigation-menu/ViewNavigationMenu';
import { PresetContext } from './common/context/PresetContext';
import useUrlPresets from './common/hooks-query/useUrlPresets';
import { useClientPath } from './common/hooks/useClientPath';
import { getRouteFromPreset } from './common/utils/urlPresets';
import { getIsNavigationLocked, sessionScope } from './externals';
import Log from './features/log/Log';
import withPreset from './features/PresetWrapper';
import withData from './features/viewers/ViewWrapper';
import { ONTIME_VERSION } from './ONTIME_VERSION';
import { sentryDsn, sentryRecommendedIgnore } from './sentry.config';
import { initializeSentry } from './sentry.config';
import Loader from './views/common/loader/Loader';
import NotFound from './views/common/not-found/NotFound';
import ViewLoader from './views/ViewLoader';
const Editor = React.lazy(() => import('./features/editors/ProtectedEditor'));
const Cuesheet = React.lazy(() => import('./features/cuesheet/ProtectedCuesheet'));
const Operator = React.lazy(() => import('./features/operator/OperatorExport'));
const Timer = lazy(() => import('./views/timer/Timer'));
const Countdown = lazy(() => import('./views/countdown/Countdown'));
const Backstage = lazy(() => import('./views/backstage/Backstage'));
const StudioClock = lazy(() => import('./views/studio/Studio'));
const Timeline = lazy(() => import('./views/timeline/TimelinePage'));
const ProjectInfo = lazy(() => import('./views/project-info/ProjectInfo'));
const TimerView = React.lazy(() => import('./features/viewers/timer/Timer'));
const MinimalTimerView = React.lazy(() => import('./features/viewers/minimal-timer/MinimalTimer'));
const ClockView = React.lazy(() => import('./features/viewers/clock/Clock'));
const Countdown = React.lazy(() => import('./features/viewers/countdown/Countdown'));
const Editor = lazy(() => import('./views/editor/ProtectedEditor'));
const Cuesheet = lazy(() => import('./views/cuesheet/ProtectedCuesheet'));
const Operator = lazy(() => import('./features/operator/OperatorExport'));
const Backstage = React.lazy(() => import('./features/viewers/backstage/Backstage'));
const Timeline = React.lazy(() => import('./features/viewers/timeline/TimelinePage'));
const Public = React.lazy(() => import('./features/viewers/public/Public'));
const Lower = React.lazy(() => import('./features/viewers/lower-thirds/LowerThird'));
const StudioClock = React.lazy(() => import('./features/viewers/studio/StudioClock'));
const EditorFeatureWrapper = lazy(() => import('./features/EditorFeatureWrapper'));
const RundownPanel = lazy(() => import('./features/rundown/RundownExport'));
const TimerControl = lazy(() => import('./features/control/playback/TimerControlExport'));
const MessageControl = lazy(() => import('./features/control/message/MessageControlExport'));
const STimer = withPreset(withData(TimerView));
const SMinimalTimer = withPreset(withData(MinimalTimerView));
const SClock = withPreset(withData(ClockView));
const SCountdown = withPreset(withData(Countdown));
const SBackstage = withPreset(withData(Backstage));
const SPublic = withPreset(withData(Public));
const SLowerThird = withPreset(withData(Lower));
const SStudio = withPreset(withData(StudioClock));
const STimeline = withPreset(withData(Timeline));
const EditorFeatureWrapper = React.lazy(() => import('./features/EditorFeatureWrapper'));
const RundownPanel = React.lazy(() => import('./features/rundown/RundownExport'));
const TimerControl = React.lazy(() => import('./features/control/playback/TimerControlExport'));
const MessageControl = React.lazy(() => import('./features/control/message/MessageControlExport'));
Sentry.init({
dsn: sentryDsn,
integrations: [
Sentry.reactRouterV6BrowserTracingIntegration({
useEffect: React.useEffect,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes,
}),
],
tracesSampleRate: 0.3,
release: ONTIME_VERSION,
enabled: import.meta.env.PROD,
ignoreErrors: [...sentryRecommendedIgnore, /Unable to preload CSS/i, /dynamically imported module/i],
denyUrls: [/extensions\//i, /^chrome:\/\//i, /^chrome-extension:\/\//i],
});
const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);
// Initialize Sentry with our configuration
const SentryRouter = initializeSentry();
export default function AppRouter() {
// handle client path changes
useClientPath();
return (
<React.Suspense fallback={null}>
<SentryRoutes>
<Suspense fallback={<Loader />}>
<SentryRouter>
<Route path='/' element={<Navigate to='/timer' />} />
<Route path='/timer' element={<STimer />} />
<Route path='/public' element={<SPublic />} />
<Route path='/minimal' element={<SMinimalTimer />} />
<Route path='/clock' element={<SClock />} />
<Route path='/countdown' element={<SCountdown />} />
<Route path='/backstage' element={<SBackstage />} />
<Route path='/studio' element={<SStudio />} />
<Route path='/lower' element={<SLowerThird />} />
<Route path='/timeline' element={<STimeline />} />
<Route
path='timer'
element={
<ViewLoader>
<ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
<Timer />
</ViewLoader>
}
/>
<Route
path='countdown'
element={
<ViewLoader>
<ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
<Countdown />
</ViewLoader>
}
/>
<Route
path='backstage'
element={
<ViewLoader>
<ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
<Backstage />
</ViewLoader>
}
/>
<Route
path='studio'
element={
<ViewLoader>
<ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
<StudioClock />
</ViewLoader>
}
/>
<Route
path='timeline'
element={
<ViewLoader>
<ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
<Timeline />
</ViewLoader>
}
/>
<Route
path='info'
element={
<ViewLoader>
<ViewNavigationMenu suppressSettings isNavigationLocked={getIsNavigationLocked()} />
<ProjectInfo />
</ViewLoader>
}
/>
{/*/!* Protected Routes *!/*/}
<Route path='/editor' element={<Editor />} />
<Route path='/cuesheet' element={<Cuesheet />} />
<Route path='/op' element={<Operator />} />
<Route path='editor' element={<Editor />} />
<Route path='cuesheet' element={<Cuesheet />} />
<Route
path='op'
element={
<ViewLoader>
<ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
<Operator />
</ViewLoader>
}
/>
{/*/!* Protected Routes - Elements *!/*/}
<Route
path='/rundown'
path='rundown'
element={
<EditorFeatureWrapper>
<RundownPanel />
@@ -101,7 +117,7 @@ export default function AppRouter() {
}
/>
<Route
path='/timercontrol'
path='timercontrol'
element={
<EditorFeatureWrapper>
<TimerControl />
@@ -109,7 +125,7 @@ export default function AppRouter() {
}
/>
<Route
path='/messagecontrol'
path='messagecontrol'
element={
<EditorFeatureWrapper>
<MessageControl />
@@ -117,16 +133,119 @@ export default function AppRouter() {
}
/>
<Route
path='/log'
path='log'
element={
<EditorFeatureWrapper>
<Log />
</EditorFeatureWrapper>
}
/>
{/*/!* Send to default if nothing found *!/*/}
<Route path='*' element={<STimer />} />
</SentryRoutes>
</React.Suspense>
{/**
* If the views are prefixed with the "preset" path, we are in a locked preset
* Locked presets do not expose their parameters
*/}
<Route path='preset/:alias' element={<PresetView />} />
{/**
* If we havent matched any views or presets, we may be in an unlocked preset
* Unlocked presets are unwrapped to expose their target and parameters
*/}
<Route path='*' element={<RedirectPreset />} />
</SentryRouter>
</Suspense>
);
}
const PresetViewMap: Record<OntimeViewPresettable, ComponentType> = {
[OntimeView.Cuesheet]: Cuesheet,
[OntimeView.Operator]: Operator,
[OntimeView.Timer]: Timer,
[OntimeView.Backstage]: Backstage,
[OntimeView.Timeline]: Timeline,
[OntimeView.StudioClock]: StudioClock,
[OntimeView.Countdown]: Countdown,
[OntimeView.ProjectInfo]: ProjectInfo,
};
/**
* This view will mask a configured canonical route
* and inject the preset search parameters to context
* User are not able to configure the parameters locked presets
*/
function PresetView() {
const { data, status } = useUrlPresets();
const { alias } = useParams();
const preset: URLPreset | undefined = useMemo(() => {
if (status === 'pending' || !alias) return;
return data.find((p) => p.alias === alias && p.enabled);
}, [data, status, alias]);
if (status === 'pending') {
return <Loader />;
}
/**
* We need to check the session scope to determine if the user can navigate
* If the user has a global scope, they can navigate freely
* Otherwise, they are locked to the preset view
*/
const showNav = sessionScope === 'rw';
/**
* If we are in a preset path but cannot find a preset, we will need to show a not found page
* This can happen if the preset was deleted or disabled
*/
if (!preset) {
return (
<>
<ViewNavigationMenu isNavigationLocked={!showNav} suppressSettings />
<NotFound />
</>
);
}
/**
* Locked presets do not allow configuration changes
* Whether the user can navigate is determined by the locked param
*
* We inject the preset to the context value for the view to consume
*/
const Component = PresetViewMap[preset.target as OntimeViewPresettable];
return (
<PresetContext value={preset}>
<ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} suppressSettings />
{Component ? <Component /> : <NotFound />}
</PresetContext>
);
}
function RedirectPreset() {
const { data, status } = useUrlPresets();
const navigate = useNavigate();
const location = useLocation();
// checks if we are in a preset path and resolves a destination URL
const destination = useMemo(() => {
if (status === 'pending') return null;
return getRouteFromPreset(location, data);
}, [data, location, status]);
// if we have a destination, we will navigate to it
useEffect(() => {
if (destination) {
navigate(`/${destination}`, { replace: true });
}
}, [destination, navigate]);
if (status === 'pending') {
return <Loader />;
}
return (
<>
<ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} suppressSettings />
<NotFound />
</>
);
}
+51
View File
@@ -0,0 +1,51 @@
import axios from 'axios';
import { TranslationObject } from 'ontime-types';
import { ontimeQueryClient } from '../../common/queryClient';
import { TRANSLATION, apiEntryUrl, customTranslationsURL } from './constants';
import type { RequestOptions } from './requestOptions';
const assetsPath = `${apiEntryUrl}/assets`;
/**
* HTTP request to get css contents
*/
export async function getCSSContents(options?: RequestOptions): Promise<string> {
const res = await axios.get(`${assetsPath}/css`, { signal: options?.signal });
return res.data;
}
/**
* HTTP request to post css contents
*/
export async function postCSSContents(css: string): Promise<void> {
await axios.post(`${assetsPath}/css`, {
css,
});
}
/**
* HTTP request to restore default css
*/
export async function restoreCSSContents(): Promise<string> {
const res = await axios.post(`${assetsPath}/css/restore`);
return res.data;
}
/**
* HTTP request to get user translation
*/
export async function getUserTranslation(options?: RequestOptions): Promise<TranslationObject> {
const res = await axios.get(customTranslationsURL, { signal: options?.signal });
return res.data;
}
/**
* HTTP request to post user translation
*/
export async function postUserTranslation(translation: TranslationObject): Promise<void> {
await axios.post(`${assetsPath}/translations`, {
translation,
});
await ontimeQueryClient.invalidateQueries({ queryKey: TRANSLATION });
}
+86
View File
@@ -0,0 +1,86 @@
import axios from 'axios';
import type {
Automation,
AutomationDTO,
AutomationOutput,
AutomationSettings,
Trigger,
TriggerDTO,
} from 'ontime-types';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
const automationsPath = `${apiEntryUrl}/automations`;
/**
* HTTP request to get the automations settings
*/
export async function getAutomationSettings(options?: RequestOptions): Promise<AutomationSettings> {
const res = await axios.get(automationsPath, { signal: options?.signal });
return res.data;
}
/**
* HTTP request to edit the automations settings
*/
export async function editAutomationSettings(
automationSettings: Partial<AutomationSettings>,
): Promise<AutomationSettings> {
const res = await axios.post(automationsPath, automationSettings);
return res.data;
}
/**
* HTTP request to create a new automation trigger
*/
export async function addTrigger(trigger: TriggerDTO): Promise<Trigger> {
const res = await axios.post(`${automationsPath}/trigger`, trigger);
return res.data;
}
/**
* HTTP request to update an automation trigger
*/
export async function editTrigger(id: string, trigger: Trigger): Promise<Trigger> {
const res = await axios.put(`${automationsPath}/trigger/${id}`, trigger);
return res.data;
}
/**
* HTTP request to delete an automation trigger
*/
export function deleteTrigger(id: string): Promise<void> {
return axios.delete(`${automationsPath}/trigger/${id}`);
}
/**
* HTTP request to create a new automation
*/
export async function addAutomation(automation: AutomationDTO): Promise<Automation> {
const res = await axios.post(`${automationsPath}/automation`, automation);
return res.data;
}
/**
* HTTP request to update a automation
*/
export async function editAutomation(id: string, automation: Automation): Promise<Automation> {
const res = await axios.put(`${automationsPath}/automation/${id}`, automation);
return res.data;
}
/**
* HTTP request to delete a automation
*/
export function deleteAutomation(id: string): Promise<void> {
return axios.delete(`${automationsPath}/automation/${id}`);
}
/**
* HTTP request to test automation output
* The return is irrelevant as we care for the resolution of the promise
*/
export function testOutput(output: AutomationOutput): Promise<void> {
return axios.post(`${automationsPath}/test`, output);
}
@@ -0,0 +1,8 @@
import axios from 'axios';
import { axiosConfig } from './requestTimeouts';
axios.defaults.validateStatus = (status) => {
return status >= 200 && status < 300;
};
axios.defaults.timeout = axiosConfig.shortTimeout;
+17 -20
View File
@@ -1,35 +1,32 @@
import { serverURL } from '../../externals';
// keys in tanstack store
export const APP_INFO = ['appinfo'];
export const APP_SETTINGS = ['appSettings'];
export const APP_SERVER_PORT = ['appServerPort'];
export const APP_VERSION = ['appVersion'];
export const AUTOMATION = ['automation'];
export const CUSTOM_FIELDS = ['customFields'];
export const HTTP_SETTINGS = ['httpSettings'];
export const OSC_SETTINGS = ['oscSettings'];
export const CUSTOM_VIEWS = ['customViews'];
export const PROJECT_DATA = ['project'];
export const PROJECT_LIST = ['projectList'];
export const PROJECT_RUNDOWNS = ['projectRundowns'];
export const RUNDOWN = ['rundown'];
export const CURRENT_RUNDOWN_QUERY_KEY = ['rundown', 'current'];
export const getRundownQueryKey = (rundownId: string) => ['rundown', rundownId];
export const RUNTIME = ['runtimeStore'];
export const SHEET_STATE = ['sheetState'];
export const URL_PRESETS = ['urlpresets'];
export const VIEW_SETTINGS = ['viewSettings'];
export const CSS_OVERRIDE = ['cssOverride'];
export const CLIENT_LIST = ['clientList'];
export const REPORT = ['report'];
export const TRANSLATION = ['translation'];
// resolve location
const location = window.location;
const socketProtocol = location.protocol === 'https:' ? 'wss' : 'ws';
export const isProduction = import.meta.env.MODE === 'production';
export const isDev = !isProduction;
// resolve port
const STATIC_PORT = 4001;
export const serverPort = isProduction ? location.port : STATIC_PORT;
export const serverURL = `${location.protocol}//${location.hostname}:${serverPort}`;
export const websocketUrl = `${socketProtocol}://${location.hostname}:${serverPort}/ws`;
// API URLs
export const apiEntryUrl = `${serverURL}/data`;
export const projectDataURL = `${serverURL}/project`;
export const rundownURL = `${serverURL}/events`;
export const ontimeURL = `${serverURL}/ontime`;
const userAssetsPath = 'user';
const customTranslationsPath = 'translations/translations.json';
export const stylesPath = 'external/styles/override.css';
export const overrideStylesURL = `${serverURL}/${stylesPath}`;
export const projectLogoPath = `${serverURL}/${userAssetsPath}/logo`;
export const customTranslationsURL = `${serverURL}/${userAssetsPath}/${customTranslationsPath}`;
+8 -7
View File
@@ -1,15 +1,16 @@
import axios from 'axios';
import { CustomField, CustomFieldLabel, CustomFields } from 'ontime-types';
import { CustomField, CustomFieldKey, CustomFields } from 'ontime-types';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
const customFieldsPath = `${apiEntryUrl}/custom-fields`;
/**
* Requests list of known custom fields
*/
export async function getCustomFields(): Promise<CustomFields> {
const res = await axios.get(customFieldsPath);
export async function getCustomFields(options?: RequestOptions): Promise<CustomFields> {
const res = await axios.get(customFieldsPath, { signal: options?.signal });
return res.data;
}
@@ -24,15 +25,15 @@ export async function postCustomField(newField: CustomField): Promise<CustomFiel
/**
* Edits single custom field
*/
export async function editCustomField(label: CustomFieldLabel, newField: CustomField): Promise<CustomFields> {
const res = await axios.put(`${customFieldsPath}/${label}`, { ...newField });
export async function editCustomField(key: CustomFieldKey, newField: CustomField): Promise<CustomFields> {
const res = await axios.put(`${customFieldsPath}/${key}`, { ...newField });
return res.data;
}
/**
* Deletes single custom field
*/
export async function deleteCustomField(label: CustomFieldLabel): Promise<CustomFields> {
const res = await axios.delete(`${customFieldsPath}/${label}`);
export async function deleteCustomField(key: CustomFieldKey): Promise<CustomFields> {
const res = await axios.delete(`${customFieldsPath}/${key}`);
return res.data;
}
+58
View File
@@ -0,0 +1,58 @@
import axios from 'axios';
import { type CustomViewsListResponse, type MessageResponse } from 'ontime-types';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
import { axiosConfig } from './requestTimeouts';
import { downloadBlob } from './utils';
const customViewsPath = `${apiEntryUrl}/custom-views`;
export async function getCustomViews(options?: RequestOptions): Promise<CustomViewsListResponse> {
const response = await axios.get<CustomViewsListResponse>(customViewsPath, {
signal: options?.signal,
timeout: options?.timeout ?? axiosConfig.shortTimeout,
});
return response.data;
}
export async function uploadCustomView(slug: string, file: File, options?: RequestOptions): Promise<MessageResponse> {
const formData = new FormData();
formData.append('indexHtml', file);
return (
await axios.post<MessageResponse>(`${customViewsPath}/${encodeURIComponent(slug)}/upload`, formData, {
signal: options?.signal,
timeout: options?.timeout ?? axiosConfig.longTimeout,
headers: {
'Content-Type': 'multipart/form-data',
},
})
).data;
}
export async function downloadCustomView(slug: string, options?: RequestOptions): Promise<void> {
const response = await axios.get(`${customViewsPath}/${encodeURIComponent(slug)}/download`, {
signal: options?.signal,
timeout: options?.timeout ?? axiosConfig.longTimeout,
responseType: 'blob',
});
downloadBlob(response.data, `${slug}-index.html`);
}
export async function restoreDemoView(options?: RequestOptions): Promise<MessageResponse> {
return (
await axios.post<MessageResponse>(`${customViewsPath}/restore-demo`, null, {
signal: options?.signal,
timeout: options?.timeout ?? axiosConfig.longTimeout,
})
).data;
}
export async function deleteCustomView(slug: string, options?: RequestOptions): Promise<void> {
await axios.delete(`${customViewsPath}/${encodeURIComponent(slug)}`, {
signal: options?.signal,
timeout: options?.timeout ?? axiosConfig.longTimeout,
});
}
+26 -33
View File
@@ -1,9 +1,9 @@
import axios, { AxiosResponse } from 'axios';
import { DatabaseModel, GetInfo, MessageResponse, ProjectData, ProjectFileListResponse } from 'ontime-types';
import { makeCSV, makeTable } from '../../features/cuesheet/cuesheetUtils';
import { DatabaseModel, MessageResponse, ProjectData, ProjectFileListResponse, QuickStartData } from 'ontime-types';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
import { axiosConfig } from './requestTimeouts';
import { createBlob, downloadBlob } from './utils';
const dbPath = `${apiEntryUrl}/db`;
@@ -12,7 +12,7 @@ const dbPath = `${apiEntryUrl}/db`;
* HTTP request to the current DB
*/
export function getDb(filename: string): Promise<AxiosResponse<DatabaseModel>> {
return axios.post(`${dbPath}/download/`, { filename });
return axios.post(`${dbPath}/download`, { filename }, { timeout: axiosConfig.longTimeout });
}
/**
@@ -32,25 +32,6 @@ export async function downloadProject(fileName: string) {
}
}
/**
* Request download of the current rundown as a CSV file
* @param fileName
*/
export async function downloadCSV(fileName: string = 'rundown') {
try {
const { data, name } = await fileDownload(fileName);
const { project, rundown, customFields } = data;
const sheetData = makeTable(project, rundown, customFields);
const fileContent = makeCSV(sheetData);
const blob = createBlob(fileContent, 'text/csv;charset=utf-8;');
downloadBlob(blob, `${name}.csv`);
} catch (error) {
console.error(error);
}
}
/**
* HTTP request to upload project file
*/
@@ -58,6 +39,7 @@ export async function uploadProjectFile(file: File): Promise<MessageResponse> {
const formData = new FormData();
formData.append('project', file);
const response = await axios.post(`${dbPath}/upload`, formData, {
timeout: axiosConfig.longTimeout,
headers: {
'Content-Type': 'multipart/form-data',
},
@@ -86,11 +68,22 @@ export async function createProject(
return res.data;
}
/**
* HTTP request to create a project file
*/
export async function quickProject(data: QuickStartData): Promise<MessageResponse> {
const res = await axios.post(`${dbPath}/quick`, data);
return res.data;
}
/**
* HTTP request to get the list of available project files
*/
export async function getProjects(): Promise<ProjectFileListResponse> {
const res = await axios.get(`${dbPath}/all`);
export async function getProjects(options?: RequestOptions): Promise<ProjectFileListResponse> {
const res = await axios.get(`${dbPath}/all`, {
signal: options?.signal,
timeout: options?.timeout,
});
return res.data;
}
@@ -104,6 +97,14 @@ export async function loadProject(filename: string): Promise<MessageResponse> {
return res.data;
}
/**
* HTTP request to load the demo project file
*/
export async function loadDemo(): Promise<MessageResponse> {
const res = await axios.post(`${dbPath}/demo`);
return res.data;
}
/**
* HTTP request to duplicate a project file
*/
@@ -138,14 +139,6 @@ export async function deleteProject(filename: string): Promise<MessageResponse>
return res.data;
}
/**
* HTTP request to retrieve application info
*/
export async function getInfo(): Promise<GetInfo> {
const res = await axios.get(`${dbPath}/info`);
return res.data;
}
/**
* Utility function gets project from db
* @param fileName
+59 -20
View File
@@ -1,42 +1,81 @@
import axios, { AxiosResponse } from 'axios';
import { CustomFields, OntimeRundown } from 'ontime-types';
import type {
SpreadsheetPreviewResponse,
SpreadsheetWorksheetMetadata,
SpreadsheetWorksheetOptions,
} from 'ontime-types';
import { ImportMap } from 'ontime-utils';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
import { axiosConfig } from './requestTimeouts';
import { downloadBlob } from './utils';
const excelPath = `${apiEntryUrl}/excel`;
type PreviewSpreadsheetResponse = {
rundown: OntimeRundown;
customFields: CustomFields;
};
/**
* upload Excel file to server
* @return string - file ID op the uploaded file
* Uploads an Excel file and returns worksheet names plus metadata for the initial worksheet.
*/
export async function upload(file: File) {
export async function upload(file: File, requestOptions?: RequestOptions): Promise<SpreadsheetWorksheetOptions> {
const formData = new FormData();
formData.append('excel', file);
await axios.post(`${excelPath}/upload`, formData, {
const response: AxiosResponse<SpreadsheetWorksheetOptions> = await axios.post(`${excelPath}/upload`, formData, {
signal: requestOptions?.signal,
timeout: requestOptions?.timeout ?? axiosConfig.longTimeout,
headers: {
'Content-Type': 'multipart/form-data',
},
});
return response.data;
}
export async function importRundownPreview(
options: ImportMap,
requestOptions?: RequestOptions,
): Promise<SpreadsheetPreviewResponse> {
const response: AxiosResponse<SpreadsheetPreviewResponse> = await axios.post(
`${excelPath}/preview`,
{
options,
},
{
signal: requestOptions?.signal,
timeout: requestOptions?.timeout ?? axiosConfig.longTimeout,
},
);
return response.data;
}
export async function getWorksheetMetadata(
worksheet: string,
requestOptions?: RequestOptions,
): Promise<SpreadsheetWorksheetMetadata> {
const response: AxiosResponse<SpreadsheetWorksheetMetadata> = await axios.post(
`${excelPath}/metadata`,
{ worksheet },
{
signal: requestOptions?.signal,
timeout: requestOptions?.timeout ?? axiosConfig.longTimeout,
},
);
return response.data;
}
/**
* Get Worksheet names
* @return string[] - array of available worksheets
* Downloads a xlsx representation of the rundown from the server
*/
export async function getWorksheetNames(): Promise<string[]> {
const response: AxiosResponse<string[]> = await axios.get(`${excelPath}/worksheets`);
return response.data;
}
export async function downloadAsExcel(rundownId: string, fileName?: string, requestOptions?: RequestOptions) {
try {
const response = await axios.get(`${excelPath}/${rundownId}/export`, {
signal: requestOptions?.signal,
timeout: requestOptions?.timeout ?? axiosConfig.longTimeout,
responseType: 'blob',
});
export async function importRundownPreview(options: ImportMap): Promise<PreviewSpreadsheetResponse> {
const response: AxiosResponse<PreviewSpreadsheetResponse> = await axios.post(`${excelPath}/preview`, {
options,
});
return response.data;
downloadBlob(response.data, `${fileName ?? 'Ontime_rundown'}.xlsx`);
} catch (error) {
console.error('Error downloading file:', error);
}
}
+3 -2
View File
@@ -1,6 +1,7 @@
import axios from 'axios';
import { apiRepoLatest } from '../../externals';
import type { RequestOptions } from './requestOptions';
export type HasUpdate = {
url: string;
@@ -10,8 +11,8 @@ export type HasUpdate = {
/**
* HTTP request to get the latest version and url from github
*/
export async function getLatestVersion(): Promise<HasUpdate> {
const res = await axios.get(`${apiRepoLatest}`);
export async function getLatestVersion(options?: RequestOptions): Promise<HasUpdate> {
const res = await axios.get(apiRepoLatest, { signal: options?.signal });
return {
url: res.data.html_url as string,
version: res.data.tag_name as string,
-21
View File
@@ -1,21 +0,0 @@
import axios, { AxiosResponse } from 'axios';
import { HttpSettings } from 'ontime-types';
import { apiEntryUrl } from './constants';
const httpPath = `${apiEntryUrl}/http`;
/**
* HTTP request to retrieve http settings
*/
export async function getHTTP(): Promise<HttpSettings> {
const res = await axios.get(httpPath);
return res.data;
}
/**
* HTTP request to mutate http settings
*/
export async function postHTTP(data: HttpSettings): Promise<AxiosResponse<HttpSettings>> {
return axios.post(httpPath, data);
}
-21
View File
@@ -1,21 +0,0 @@
import axios, { AxiosResponse } from 'axios';
import { OSCSettings } from 'ontime-types';
import { apiEntryUrl } from './constants';
const oscPath = `${apiEntryUrl}/osc`;
/**
* HTTP request to retrieve osc settings
*/
export async function getOSC(): Promise<OSCSettings> {
const res = await axios.get(oscPath);
return res.data;
}
/**
* HTTP request to mutate osc settings
*/
export async function postOSC(data: OSCSettings): Promise<AxiosResponse<OSCSettings>> {
return axios.post(oscPath, data);
}
+27 -5
View File
@@ -1,21 +1,43 @@
import axios, { AxiosResponse } from 'axios';
import { ProjectData } from 'ontime-types';
import { ProjectData, ProjectLogoResponse } from 'ontime-types';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
import { axiosConfig } from './requestTimeouts';
const projectPath = `${apiEntryUrl}/project`;
/**
* HTTP request to fetch project data
*/
export async function getProjectData(): Promise<ProjectData> {
const res = await axios.get(projectPath);
export async function getProjectData(options?: RequestOptions): Promise<ProjectData> {
const res = await axios.get(projectPath, {
signal: options?.signal,
timeout: options?.timeout,
});
return res.data;
}
/**
* HTTP request to mutate project data
*/
export async function postProjectData(data: ProjectData): Promise<AxiosResponse<ProjectData>> {
return axios.post(projectPath, data);
export async function postProjectData(data: ProjectData): Promise<ProjectData> {
const response = await axios.post(projectPath, data);
return response.data;
}
/**
* HTTP request to upload a project logo
*/
export async function uploadProjectLogo(file: File): Promise<AxiosResponse<ProjectLogoResponse>> {
const formData = new FormData();
formData.append('image', file);
const response = await axios.post(`${projectPath}/upload`, formData, {
timeout: axiosConfig.longTimeout,
headers: {
'Content-Type': 'multipart/form-data',
},
});
return response;
}
+26
View File
@@ -0,0 +1,26 @@
import axios from 'axios';
import { OntimeReport } from 'ontime-types';
import { ontimeQueryClient } from '../../common/queryClient';
import { REPORT, apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
export const reportUrl = `${apiEntryUrl}/report`;
/**
* HTTP request to fetch all reports
*/
export async function fetchReport(options?: RequestOptions): Promise<OntimeReport> {
const res = await axios.get(reportUrl, { signal: options?.signal });
return res.data;
}
export async function deleteReport(id: string) {
await axios.delete(`${reportUrl}/${id}`);
await ontimeQueryClient.invalidateQueries({ queryKey: REPORT });
}
export async function deleteAllReport() {
await axios.delete(`${reportUrl}/all`);
await ontimeQueryClient.invalidateQueries({ queryKey: REPORT });
}
@@ -0,0 +1,4 @@
export type RequestOptions = {
signal?: AbortSignal;
timeout?: number;
};
@@ -0,0 +1,8 @@
/**
* Keep a short global timeout for regular API requests, but allow
* longer windows for file transfer and heavy import/export operations.
*/
export const axiosConfig = {
shortTimeout: 20 * 1000, // 20 seconds
longTimeout: 3 * 60 * 1000, // 3 minutes
} as const;
+171 -37
View File
@@ -1,86 +1,220 @@
import axios, { AxiosResponse } from 'axios';
import { MessageResponse, OntimeEvent, OntimeRundownEntry, RundownCached } from 'ontime-types';
import {
EntryId,
OntimeEntry,
OntimeEvent,
ProjectRundownsList,
RenumberCues,
Rundown,
RundownImportPayload,
TransientEventPayload,
} from 'ontime-types';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
const rundownPath = `${apiEntryUrl}/rundown`;
type RundownId = string;
const rundownPath = `${apiEntryUrl}/rundowns`;
// #region operations on project rundowns =========================
/**
* HTTP request to fetch all events
* HTTP request to fetch a list of existing rundowns
*/
export async function fetchNormalisedRundown(): Promise<RundownCached> {
const res = await axios.get(`${rundownPath}/normalised`);
export async function fetchProjectRundownList(options?: RequestOptions): Promise<ProjectRundownsList> {
const res = await axios.get(rundownPath, { signal: options?.signal });
return res.data;
}
/**
* HTTP request to post new event
* HTTP request to fetch all entries in the currently loaded rundown
*/
export async function requestPostEvent(data: Partial<OntimeRundownEntry>): Promise<AxiosResponse<OntimeRundownEntry>> {
return axios.post(rundownPath, data);
export async function fetchCurrentRundown(options?: RequestOptions): Promise<Rundown> {
const res = await axios.get(`${rundownPath}/current`, { signal: options?.signal });
if (!isValidRundown(res.data)) {
throw new Error('Invalid rundown payload');
}
return res.data;
}
/**
* HTTP request to put new event
* HTTP request to fetch all entries in the given rundown
*/
export async function requestPutEvent(data: Partial<OntimeRundownEntry>): Promise<AxiosResponse<OntimeRundownEntry>> {
return axios.put(rundownPath, data);
export async function fetchRundown(rundownId: RundownId, options?: RequestOptions): Promise<Rundown> {
const res = await axios.get(`${rundownPath}/${rundownId}`, { signal: options?.signal });
if (!isValidRundown(res.data)) {
throw new Error('Invalid rundown payload');
}
return res.data;
}
type BatchEditEntry = {
/**
* HTTP request to switch the currently loaded rundown
*/
export async function loadRundown(rundownId: RundownId): Promise<AxiosResponse<ProjectRundownsList>> {
return axios.post(`${rundownPath}/${rundownId}/load`);
}
/**
* HTTP request to create a new rundown
*/
export async function createRundown(title: string): Promise<AxiosResponse<ProjectRundownsList>> {
return axios.post(rundownPath, { title });
}
/**
* HTTP request to duplicate an existing rundown
*/
export async function duplicateRundown(rundownId: RundownId): Promise<AxiosResponse<ProjectRundownsList>> {
return axios.post(`${rundownPath}/${rundownId}/duplicate`);
}
/**
* HTTP request to rename an existing rundown
*/
export async function renameRundown(rundownId: RundownId, title: string): Promise<AxiosResponse<ProjectRundownsList>> {
return axios.patch(`${rundownPath}/${rundownId}`, { title });
}
/**
* HTTP request to delete a rundown
*/
export async function deleteRundown(rundownId: RundownId): Promise<AxiosResponse<ProjectRundownsList>> {
return axios.delete(`${rundownPath}/${rundownId}`);
}
/**
* HTTP request to apply an imported rundown using a merge strategy or into a new rundown
*/
export async function importRundownWithOptions(
payload: RundownImportPayload,
): Promise<AxiosResponse<ProjectRundownsList>> {
return axios.post(`${rundownPath}/import`, payload);
}
// #endregion operations on project rundowns ======================
// #region operations on rundown entries ==========================
/**
* HTTP request to post new entry
*/
export async function postAddEntry(
rundownId: RundownId,
data: TransientEventPayload,
): Promise<AxiosResponse<OntimeEntry>> {
return axios.post(`${rundownPath}/${rundownId}/entry`, data);
}
/**
* HTTP request to edit an entry
*/
export async function putEditEntry(
rundownId: RundownId,
data: Partial<OntimeEntry>,
): Promise<AxiosResponse<OntimeEntry>> {
return axios.put(`${rundownPath}/${rundownId}/entry`, data);
}
export type BatchEditEntry = {
data: Partial<OntimeEvent>;
ids: string[];
ids: EntryId[];
};
/**
* HTTP request to put multiple events
* HTTP request to edit multiple events
*/
export async function requestBatchPutEvents(data: BatchEditEntry): Promise<AxiosResponse<MessageResponse>> {
return axios.put(`${rundownPath}/batch`, data);
export async function putBatchEditEvents(rundownId: RundownId, data: BatchEditEntry): Promise<AxiosResponse<Rundown>> {
return axios.put(`${rundownPath}/${rundownId}/batch`, data);
}
/**
* HTTP request to renumber cues for multiple events
*/
export function patchRenumberCues(rundownId: RundownId, data: RenumberCues): Promise<AxiosResponse<Rundown>> {
return axios.patch(`${rundownPath}/${rundownId}/renumber`, data);
}
export type ReorderEntry = {
eventId: string;
from: number;
to: number;
entryId: EntryId;
destinationId: EntryId;
order: 'before' | 'after' | 'insert';
};
/**
* HTTP request to reorder events
* HTTP request to reorder an entry
*/
export async function requestReorderEvent(data: ReorderEntry): Promise<AxiosResponse<OntimeRundownEntry>> {
return axios.patch(`${rundownPath}/reorder`, data);
export async function patchReorderEntry(rundownId: RundownId, data: ReorderEntry): Promise<AxiosResponse<Rundown>> {
return axios.patch(`${rundownPath}/${rundownId}/reorder`, data);
}
export type SwapEntry = {
from: string;
to: string;
};
/**
* HTTP request to swap two events
*/
export async function requestEventSwap(data: SwapEntry): Promise<AxiosResponse<MessageResponse>> {
return axios.patch(`${rundownPath}/swap`, data);
export async function requestEventSwap(
rundownId: RundownId,
from: EntryId,
to: EntryId,
): Promise<AxiosResponse<Rundown>> {
return axios.patch(`${rundownPath}/${rundownId}/swap`, { from, to });
}
/**
* HTTP request to request application of delay
*/
export async function requestApplyDelay(eventId: string): Promise<AxiosResponse<MessageResponse>> {
return axios.patch(`${rundownPath}/applydelay/${eventId}`);
export async function requestApplyDelay(rundownId: RundownId, delayId: EntryId): Promise<AxiosResponse<Rundown>> {
return axios.patch(`${rundownPath}/${rundownId}/applydelay/${delayId}`);
}
/**
* HTTP request to delete given event
* HTTP request for cloning an entry
*/
export async function requestDelete(eventIds: string[]): Promise<AxiosResponse<MessageResponse>> {
return axios.delete(rundownPath, { data: { ids: eventIds } });
export async function postCloneEntry(
rundownId: RundownId,
entryId: EntryId,
options?: { before?: EntryId; after?: EntryId },
): Promise<AxiosResponse<Rundown>> {
return axios.post(`${rundownPath}/${rundownId}/clone/${entryId}`, options);
}
/**
* HTTP request to delete all events
* HTTP request for grouping a list of entries into a group
*/
export async function requestDeleteAll(): Promise<AxiosResponse<MessageResponse>> {
return axios.delete(`${rundownPath}/all`);
export async function requestGroupEntries(rundownId: RundownId, entryIds: EntryId[]): Promise<AxiosResponse<Rundown>> {
return axios.post(`${rundownPath}/${rundownId}/group`, { ids: entryIds });
}
/**
* HTTP request for dissolving of a group
*/
export async function requestUngroup(rundownId: RundownId, groupId: EntryId): Promise<AxiosResponse<Rundown>> {
return axios.post(`${rundownPath}/${rundownId}/ungroup/${groupId}`);
}
/**
* HTTP request to delete entries of a given rundown
*/
export async function deleteEntries(rundownId: RundownId, entryIds: EntryId[]): Promise<AxiosResponse<Rundown>> {
return axios.delete(`${rundownPath}/${rundownId}/entries`, { data: { ids: entryIds } });
}
/**
* HTTP request to delete all entries of a given rundown
*/
export async function requestDeleteAll(rundownId: RundownId): Promise<AxiosResponse<Rundown>> {
return axios.delete(`${rundownPath}/${rundownId}/all`);
}
// #endregion operations on rundown entries =======================
function isValidRundown(x: any): x is Rundown {
return (
x &&
typeof x === 'object' &&
typeof x.id === 'string' &&
Array.isArray(x.order) &&
Array.isArray(x.flatOrder) &&
x.entries &&
typeof x.entries === 'object' &&
typeof x.revision === 'number'
);
}
+23
View File
@@ -0,0 +1,23 @@
import axios from 'axios';
import { GetInfo, LinkOptions } from 'ontime-types';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
const sessionPath = `${apiEntryUrl}/session`;
/**
* HTTP request to retrieve application info
*/
export async function getInfo(options?: RequestOptions): Promise<GetInfo> {
const res = await axios.get(`${sessionPath}/info`, { signal: options?.signal });
return res.data;
}
/**
* HTTP request to get a pre-authenticated URL
*/
export async function generateUrl(options: LinkOptions & { baseUrl: string; path: string }): Promise<string> {
const res = await axios.post(`${sessionPath}/url`, options);
return res.data.url;
}
+26 -3
View File
@@ -1,15 +1,16 @@
import axios, { AxiosResponse } from 'axios';
import { Settings } from 'ontime-types';
import { PortInfo, Settings } from 'ontime-types';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
const settingsPath = `${apiEntryUrl}/settings`;
/**
* HTTP request to retrieve application settings
*/
export async function getSettings(): Promise<Settings> {
const res = await axios.get(settingsPath);
export async function getSettings(options?: RequestOptions): Promise<Settings> {
const res = await axios.get(settingsPath, { signal: options?.signal });
return res.data;
}
@@ -19,3 +20,25 @@ export async function getSettings(): Promise<Settings> {
export async function postSettings(data: Settings): Promise<AxiosResponse<Settings>> {
return axios.post(settingsPath, data);
}
/**
* Allows setting the welcome modal dialog state from the clients
*/
export async function postShowWelcomeDialog(show: boolean) {
axios.post(`${settingsPath}/welcomedialog`, { show });
}
/**
* HTTP request to retrieve server port
*/
export async function getServerPort(options?: RequestOptions): Promise<PortInfo> {
const res = await axios.get(`${settingsPath}/serverport`, { signal: options?.signal });
return res.data;
}
/**
* HTTP request to set server port
*/
export async function postServerPort(serverPort: number): Promise<AxiosResponse<PortInfo>> {
return axios.post(`${settingsPath}/serverport`, { serverPort });
}
+68 -10
View File
@@ -1,8 +1,15 @@
import axios, { AxiosResponse } from 'axios';
import { AuthenticationStatus, CustomFields, OntimeRundown } from 'ontime-types';
import type {
AuthenticationStatus,
SpreadsheetPreviewResponse,
SpreadsheetWorksheetMetadata,
SpreadsheetWorksheetOptions,
} from 'ontime-types';
import { ImportMap } from 'ontime-utils';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
import { axiosConfig } from './requestTimeouts';
const sheetsPath = `${apiEntryUrl}/sheets`;
@@ -23,6 +30,7 @@ export const verifyAuthenticationStatus = async (): Promise<{
export const requestConnection = async (
file: File,
sheetId: string,
requestOptions?: RequestOptions,
): Promise<{
verification_url: string;
user_code: string;
@@ -31,6 +39,8 @@ export const requestConnection = async (
formData.append('client_secret', file);
const response = await axios.post(`${sheetsPath}/${sheetId}/connect`, formData, {
signal: requestOptions?.signal,
timeout: requestOptions?.timeout ?? axiosConfig.longTimeout,
headers: {
'Content-Type': 'multipart/form-data',
},
@@ -53,23 +63,71 @@ export const revokeAuthentication = async (): Promise<{ authenticated: Authentic
export const previewRundown = async (
sheetId: string,
options: ImportMap,
): Promise<{
rundown: OntimeRundown;
customFields: CustomFields;
}> => {
const response = await axios.post(`${sheetsPath}/${sheetId}/read`, { options });
requestOptions?: RequestOptions,
): Promise<SpreadsheetPreviewResponse> => {
const response = await axios.post(
`${sheetsPath}/${sheetId}/read`,
{ options },
{
signal: requestOptions?.signal,
timeout: requestOptions?.timeout ?? axiosConfig.longTimeout,
},
);
return response.data;
};
export const getWorksheetNames = async (sheetId: string): Promise<string[]> => {
const response: AxiosResponse<string[]> = await axios.post(`${sheetsPath}/${sheetId}/worksheets`);
/**
* Fetches derived metadata for a single worksheet by reading its rows and detecting headers.
*/
export const getWorksheetMetadata = async (
sheetId: string,
worksheet: string,
requestOptions?: RequestOptions,
): Promise<SpreadsheetWorksheetMetadata> => {
const response: AxiosResponse<SpreadsheetWorksheetMetadata> = await axios.post(
`${sheetsPath}/${sheetId}/metadata`,
{ worksheet },
{
signal: requestOptions?.signal,
timeout: requestOptions?.timeout ?? axiosConfig.longTimeout,
},
);
return response.data;
};
/**
* Fetches the available worksheets for a Google Sheet. Metadata is loaded lazily per worksheet.
*/
export const getWorksheetOptions = async (
sheetId: string,
requestOptions?: RequestOptions,
): Promise<SpreadsheetWorksheetOptions> => {
const response: AxiosResponse<SpreadsheetWorksheetOptions> = await axios.post(
`${sheetsPath}/${sheetId}/worksheet-options`,
undefined,
{
signal: requestOptions?.signal,
timeout: requestOptions?.timeout ?? axiosConfig.longTimeout,
},
);
return response.data;
};
/**
* HTTP request to upload the rundown to a google sheet
*/
export const uploadRundown = async (sheetId: string, options: ImportMap): Promise<void> => {
const response = await axios.post(`${sheetsPath}/${sheetId}/write`, { options });
export const uploadRundown = async (
sheetId: string,
options: ImportMap,
requestOptions?: RequestOptions,
): Promise<void> => {
const response = await axios.post(
`${sheetsPath}/${sheetId}/write`,
{ options },
{
signal: requestOptions?.signal,
timeout: requestOptions?.timeout ?? axiosConfig.longTimeout,
},
);
return response.data;
};
+21 -6
View File
@@ -2,20 +2,35 @@ import axios from 'axios';
import { URLPreset } from 'ontime-types';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
const urlPresetsPath = `${apiEntryUrl}/url-presets`;
/**
* HTTP request to retrieve aliases
* HTTP request to retrieve all presets
*/
export async function getUrlPresets(): Promise<URLPreset[]> {
const res = await axios.get(urlPresetsPath);
export async function getUrlPresets(options?: RequestOptions): Promise<URLPreset[]> {
const res = await axios.get(urlPresetsPath, { signal: options?.signal });
return res.data;
}
/**
* HTTP request to mutate aliases
* HTTP request to add a preset
*/
export async function postUrlPresets(data: URLPreset[]): Promise<URLPreset[]> {
return axios.post(urlPresetsPath, data);
export async function postUrlPreset(data: URLPreset): Promise<URLPreset[]> {
return (await axios.post(urlPresetsPath, data)).data;
}
/**
* HTTP request to edit a preset
*/
export async function putUrlPreset(alias: string, data: URLPreset): Promise<URLPreset[]> {
return (await axios.put(`${urlPresetsPath}/${alias}`, data)).data;
}
/**
* HTTP request to delete a preset
*/
export async function deleteUrlPreset(alias: string): Promise<URLPreset[]> {
return (await axios.delete(`${urlPresetsPath}/${alias}`)).data;
}
+18 -2
View File
@@ -7,7 +7,7 @@ import { addLog } from '../stores/logger';
import { nowInMillis } from '../utils/time';
/**
* Utility unrwap a potential axios error
* Utility unwrap a potential axios error
* @param error
* @returns
*/
@@ -23,6 +23,8 @@ export function maybeAxiosError(error: unknown) {
}
}
return `${statusText}: ${data}`;
} else if (error instanceof Error) {
return error.message;
} else {
if (typeof error !== 'string') {
return JSON.stringify(error);
@@ -32,7 +34,21 @@ export function maybeAxiosError(error: unknown) {
}
/**
* Utility unrwaps a potential axios error and sends to logger
* Utility unwrap a an instance of Error
*/
export function unwrapError(error: unknown) {
if (error instanceof Error) {
return error.message;
} else {
if (typeof error !== 'string') {
return JSON.stringify(error);
}
return error;
}
}
/**
* Utility unwraps a potential axios error and sends to logger
* @param prepend
* @param error
*/
+11 -7
View File
@@ -1,21 +1,25 @@
import axios from 'axios';
import { ViewSettings } from 'ontime-types';
import type { ViewSettings } from 'ontime-types';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
const viewSettingsPath = `${apiEntryUrl}/view-settings`;
/**
* HTTP request to retrieve view settings
* HTTP request to fetch view settings
* @returns
*/
export async function getView(): Promise<ViewSettings> {
const res = await axios.get(viewSettingsPath);
export async function getViewSettings(options?: RequestOptions): Promise<ViewSettings> {
const res = await axios.get(viewSettingsPath, { signal: options?.signal });
return res.data;
}
/**
* HTTP request to mutate view settings
* HTTP request to update view settings
* needs to update entire objects, not just a patch
*/
export async function postViewSettings(data: ViewSettings) {
return axios.post(viewSettingsPath, data);
export async function postViewSettings(data: ViewSettings): Promise<ViewSettings> {
const res = await axios.post(viewSettingsPath, data);
return res.data;
}
@@ -1,12 +0,0 @@
@use "../../../theme/mixins" as *;
.link {
@include action-link;
font-size: $inner-section-text-size;
}
.linkIcon {
margin-left: $element-inner-spacing;
display: inline-block;
transform: rotate(45deg);
}
@@ -1,26 +0,0 @@
import { MouseEvent, ReactNode } from 'react';
import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp';
import { openLink } from '../../utils/linkUtils';
import style from './AppLink.module.scss';
interface AppLinkProps {
href: string;
children: ReactNode;
}
export default function AppLink(props: AppLinkProps) {
const { href, children } = props;
const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
openLink(href);
};
return (
<a href='#!' target='_blank' rel='noreferrer' className={style.link} onClick={handleClick}>
{children} <IoArrowUp className={style.linkIcon} />
</a>
);
}
@@ -0,0 +1,61 @@
.positioner {
outline: 0;
}
.popup {
font-size: 1rem;
box-sizing: border-box;
padding: 2px;
min-width: var(--anchor-width);
max-width: var(--available-width);
border: 1px solid $gray-1000;
border-radius: $component-border-radius-md;
background-color: $gray-1200;
color: $ui-white;
&[data-empty][data-hide-empty] {
display: none;
}
}
.list {
box-sizing: border-box;
max-height: 20rem;
max-height: var(--available-height);
overflow-y: auto;
overscroll-behavior: contain;
padding-block: 0.25rem;
scroll-padding-block: 1.5rem;
outline: 0;
}
.item,
.empty:not(:empty) {
box-sizing: border-box;
padding: 0.25rem 0.5rem;
line-height: 1rem;
}
.item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
outline: 0;
cursor: default;
user-select: none;
overflow-wrap: break-word;
&[data-highlighted] {
background-color: $blue-700;
}
}
.itemLabel {
min-width: 0;
overflow-wrap: anywhere;
}
.empty {
color: $gray-400;
}
@@ -0,0 +1,121 @@
import { Autocomplete as BaseAutocomplete } from '@base-ui/react/autocomplete';
import type { ReactNode, Ref } from 'react';
import { useEffect, useRef, useState } from 'react';
import { cx } from '../../utils/styleUtils';
import type { InputProps } from '../input/input/Input';
import { getScrollParent } from './autocompleteInput.utils';
import inputStyles from '../input/input/Input.module.scss';
import styles from './AutocompleteInput.module.scss';
export interface AutocompleteInputProps extends Omit<InputProps, 'value' | 'defaultValue' | 'onChange'> {
options: string[];
value: string;
onValueChange: (value: string) => void;
emptyLabel?: string;
trailingElement?: (option: string) => ReactNode;
inputRef?: Ref<HTMLInputElement>;
openOnFocus?: boolean;
}
export default function AutocompleteInput({
className,
disabled,
emptyLabel,
fluid,
height = 'medium',
inputRef,
onValueChange,
options,
openOnFocus = false,
trailingElement,
value,
variant = 'subtle',
...inputProps
}: AutocompleteInputProps) {
const internalInputRef = useRef<HTMLInputElement | null>(null);
const [open, setOpen] = useState(false);
const { onFocus, ...restInputProps } = inputProps;
const handleInputRef = (node: HTMLInputElement | null) => {
internalInputRef.current = node;
if (!inputRef) {
return;
}
if (typeof inputRef === 'function') {
inputRef(node);
return;
}
inputRef.current = node;
};
// close the popover when the parent scrollable container scrolls or the window resizes
useEffect(() => {
if (!open) {
return;
}
const scrollTarget = getScrollParent(internalInputRef.current);
const handleScroll = () => setOpen(false);
scrollTarget.addEventListener('scroll', handleScroll, { passive: true });
window.addEventListener('resize', handleScroll, { passive: true });
return () => {
scrollTarget.removeEventListener('scroll', handleScroll);
window.removeEventListener('resize', handleScroll);
};
}, [open]);
return (
<BaseAutocomplete.Root
items={options}
autoHighlight
highlightItemOnHover
disabled={disabled}
open={open}
value={value}
onOpenChange={setOpen}
onValueChange={onValueChange}
>
<BaseAutocomplete.Input
ref={handleInputRef}
onFocus={(event) => {
onFocus?.(event);
if (openOnFocus) {
setOpen(true);
}
}}
className={cx([
inputStyles.input,
inputStyles[variant],
inputStyles[height],
fluid && inputStyles.fluid,
className,
])}
{...restInputProps}
/>
<BaseAutocomplete.Portal>
<BaseAutocomplete.Positioner side='bottom' align='start' className={styles.positioner}>
<BaseAutocomplete.Popup className={styles.popup} data-hide-empty={emptyLabel ? undefined : ''}>
<BaseAutocomplete.List className={styles.list}>
<BaseAutocomplete.Collection>
{(option: string) => (
<BaseAutocomplete.Item key={option} value={option} className={styles.item}>
<span className={styles.itemLabel}>{option}</span>
{trailingElement?.(option)}
</BaseAutocomplete.Item>
)}
</BaseAutocomplete.Collection>
{emptyLabel && <BaseAutocomplete.Empty className={styles.empty}>{emptyLabel}</BaseAutocomplete.Empty>}
</BaseAutocomplete.List>
</BaseAutocomplete.Popup>
</BaseAutocomplete.Positioner>
</BaseAutocomplete.Portal>
</BaseAutocomplete.Root>
);
}
@@ -0,0 +1,14 @@
export function getScrollParent(node: HTMLElement | null): HTMLElement | Window {
let parent = node?.parentElement ?? null;
while (parent) {
const { overflowY } = window.getComputedStyle(parent);
if (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') {
return parent;
}
parent = parent.parentElement;
}
return window;
}
@@ -0,0 +1,84 @@
@use './buttonVariants' as base;
@include base.button-variants;
.baseButton {
position: relative;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5em;
width: fit-content;
padding-inline: 1em;
white-space: nowrap;
border: 1px solid transparent;
border-radius: $component-border-radius-md;
cursor: pointer;
&:disabled {
opacity: 0.4;
cursor: not-allowed;
}
&:focus-visible {
outline: 2px solid $blue-500;
outline-offset: 2px;
}
&.loading {
cursor: wait;
.content {
opacity: 0;
}
}
}
.content {
display: flex;
align-items: center;
gap: inherit;
}
.loadingOverlay {
position: absolute;
display: grid;
place-content: center;
}
.spinner {
animation: spin 1s linear infinite;
stroke-dasharray: 8;
}
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
.small {
height: 1.5rem;
font-size: calc(1rem - 3px);
}
.medium {
height: 2rem;
font-size: calc(1rem - 2px);
}
.large {
height: 2.5rem;
font-weight: 600;
}
.xlarge {
height: 3rem;
font-weight: 600;
}
.fluid {
width: 100%;
}
@@ -0,0 +1,53 @@
import { ButtonHTMLAttributes, forwardRef } from 'react';
import { IoEllipseOutline } from 'react-icons/io5';
import { cx } from '../../utils/styleUtils';
import style from './Button.module.scss';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?:
| 'primary'
| 'subtle'
| 'subtle-white'
| 'destructive'
| 'subtle-destructive'
| 'ghosted'
| 'ghosted-white'
| 'ghosted-destructive';
size?: 'small' | 'medium' | 'large' | 'xlarge';
fluid?: boolean;
loading?: boolean;
}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, children, variant = 'subtle', size = 'medium', fluid, loading, ...buttonProps }, ref) => {
return (
<button
ref={ref}
className={cx([
style.baseButton,
style[variant],
style[size],
fluid && style.fluid,
loading && style.loading,
className,
])}
type='button'
disabled={loading || buttonProps.disabled}
{...buttonProps}
>
<span className={style.content}>{children}</span>
{loading && (
<div className={style.loadingOverlay}>
<IoEllipseOutline className={style.spinner} />
</div>
)}
</button>
);
},
);
Button.displayName = 'Button';
export default Button;
@@ -0,0 +1,45 @@
@use './buttonVariants' as base;
@include base.button-variants;
.baseIconButton {
aspect-ratio: 1;
display: grid;
place-content: center;
border: 1px solid transparent;
border-radius: $component-border-radius-md;
cursor: pointer;
&:focus-visible {
outline: 2px solid $blue-500;
outline-offset: 2px;
}
&:disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
.small {
height: 1.5rem;
font-size: calc(1rem - 3px);
}
.medium {
height: 2rem;
width: 2rem;
}
.large {
height: 2.5rem;
width: 2.5rem;
}
.xlarge {
height: 3rem;
height: 3rem;
font-size: 1.5rem;
}
@@ -0,0 +1,37 @@
import { ButtonHTMLAttributes, forwardRef } from 'react';
import { cx } from '../../utils/styleUtils';
import style from './IconButton.module.scss';
interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?:
| 'primary'
| 'subtle'
| 'subtle-white'
| 'destructive'
| 'subtle-destructive'
| 'ghosted'
| 'ghosted-white'
| 'ghosted-destructive';
size?: 'small' | 'medium' | 'large' | 'xlarge';
}
const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
({ className, children, variant = 'subtle', size = 'medium', ...buttonProps }, ref) => {
return (
<button
ref={ref}
className={cx([style.baseIconButton, style[variant], style[size], className])}
type='button'
{...buttonProps}
>
{children}
</button>
);
},
);
IconButton.displayName = 'IconButton';
export default IconButton;
@@ -1,17 +0,0 @@
import { MouseEvent } from 'react';
import { IconButton, IconButtonProps, Tooltip } from '@chakra-ui/react';
interface TooltipActionBtnProps extends IconButtonProps {
clickHandler: (event: MouseEvent) => void | Promise<void>;
tooltip: string;
openDelay?: number;
}
export default function TooltipActionBtn(props: TooltipActionBtnProps) {
const { clickHandler, icon, size = 'xs', tooltip, openDelay = 0, className, ...rest } = props;
return (
<Tooltip label={tooltip} openDelay={openDelay}>
<IconButton {...rest} size={size} icon={icon} onClick={clickHandler} className={className} />
</Tooltip>
);
}
@@ -0,0 +1,144 @@
@use '@/theme/themeTokens' as *;
@use '@/theme/viewerDefs' as *;
@mixin button-variants {
.subtle {
background: $gray-1050;
color: $blue-400;
line-height: 1em;
&:hover:not(:disabled):not(:active) {
background: $gray-1000;
color: $blue-500;
}
&:active:not(:disabled) {
background: $gray-1100;
border-color: $gray-1250;
}
}
.subtle-white {
background: $gray-1050;
color: $ui-white;
&:hover:not(:disabled):not(:active) {
background: $gray-1000;
color: $ui-white;
}
&:active:not(:disabled) {
background: $gray-1100;
border-color: $gray-1250;
}
&:disabled {
background: $gray-1050;
}
}
.primary {
background: $blue-700;
color: $ui-white;
&:hover:not(:disabled):not(:active) {
background: $blue-600;
}
&:active:not(:disabled) {
background: $blue-800;
border-color: $blue-900;
}
&:disabled {
opacity: $viewer-opacity-disabled;
}
}
.destructive {
background: $red-700;
color: $ui-white;
&:hover:not(:disabled):not(:active) {
background: $red-600;
color: $ui-white;
}
&:active:not(:disabled) {
background: $red-800;
border-color: $red-900;
}
}
.subtle-destructive {
background: $gray-1050;
color: $red-400;
&:hover:not(:disabled):not(:active) {
background: $gray-1000;
color: $red-500;
}
&:active:not(:disabled) {
background: $gray-1100;
border-color: $gray-1250;
}
}
.ghosted {
background: transparent;
color: $blue-500;
&:hover:not(:disabled):not(:active) {
background: $gray-1000;
color: $blue-500;
}
&:active:not(:disabled) {
background: $gray-1100;
border-color: $gray-1250;
}
&:disabled {
opacity: $opacity-disabled;
}
}
.ghosted-white {
background: transparent;
color: $ui-white;
&:hover:not(:disabled):not(:active) {
background: $gray-1000;
color: $ui-white;
}
&:active:not(:disabled) {
background: $gray-1100;
border-color: $gray-1250;
}
&:disabled {
opacity: $opacity-disabled;
}
}
.ghosted-destructive {
background: transparent;
color: $red-500;
&:hover:not(:disabled):not(:active) {
background: $gray-1000;
color: $red-500;
}
&:active:not(:disabled) {
background: $gray-1100;
border-color: $gray-1250;
}
&:disabled {
opacity: $opacity-disabled;
}
}
}
@@ -0,0 +1,50 @@
.checkbox {
box-sizing: border-box;
display: flex;
width: 1rem;
height: 1rem;
align-items: center;
justify-content: center;
border-radius: $component-border-radius-sm;
&[data-unchecked] {
background-color: $gray-1100;
&:hover:not(:disabled) {
background: $gray-1000;
}
&:active:not(:disabled) {
background: $gray-1200;
}
}
&[data-checked] {
background-color: $blue-500;
&:hover:not(:disabled) {
background: $blue-400;
}
&:active:not(:disabled) {
background: $blue-600;
}
}
&:disabled {
opacity: 0.4;
cursor: not-allowed;
}
&:focus-visible {
outline: 2px solid $blue-500;
outline-offset: 2px;
}
}
.indicator {
display: flex;
color: $ui-white;
&[data-unchecked] {
display: none;
}
}
@@ -0,0 +1,14 @@
import { Checkbox as BaseCheckbox } from '@base-ui/react/checkbox';
import { IoCheckmark } from 'react-icons/io5';
import style from './Checkbox.module.scss';
export default function Checkbox(checkboxProps: BaseCheckbox.Root.Props) {
return (
<BaseCheckbox.Root className={style.checkbox} {...checkboxProps}>
<BaseCheckbox.Indicator className={style.indicator}>
<IoCheckmark className={style.icon} />
</BaseCheckbox.Indicator>
</BaseCheckbox.Root>
);
}
@@ -1,6 +0,0 @@
.buttonSection {
display: grid;
grid-template-columns: 1fr 1fr;
gap: $element-spacing;
margin-top: -$element-spacing;
}
@@ -0,0 +1,24 @@
.textEntry {
grid-area: input;
display: flex;
align-items: center;
gap: 0.5rem;
color: $label-gray;
}
.inlineEntry {
display: grid;
grid-template-areas:
'label label'
'input btn';
grid-template-columns: 1fr auto;
column-gap: 0.5rem;
}
.redirect {
grid-area: btn;
}
.label {
color: $label-gray;
}
@@ -1,69 +1,123 @@
import { useState } from 'react';
import {
Button,
Input,
InputGroup,
InputLeftAddon,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
} from '@chakra-ui/react';
import { IoArrowForward } from 'react-icons/io5';
import { navigatorConstants } from '../../../viewerConfig';
import useUrlPresets from '../../hooks-query/useUrlPresets';
import { setClientRemote } from '../../hooks/useSocket';
import Button from '../buttons/Button';
import Dialog from '../dialog/Dialog';
import Info from '../info/Info';
import Input from '../input/input/Input';
import AppLink from '../link/app-link/AppLink';
import Select from '../select/Select';
import style from './ClientModal.module.scss';
import style from './RedirectClientModal.module.scss';
interface RedirectClientModalProps {
id: string;
name?: string;
path?: string;
name: string;
currentPath: string;
origin: string;
isOpen: boolean;
onClose: () => void;
}
export function RedirectClientModal(props: RedirectClientModalProps) {
const { id, isOpen, name = '', path: currentPath = '', onClose } = props;
export function RedirectClientModal({ id, isOpen, name, currentPath, origin, onClose }: RedirectClientModalProps) {
const { data } = useUrlPresets();
const [path, setPath] = useState(currentPath);
const [selected, setSelected] = useState('/');
const { setRedirect } = setClientRemote;
const handleRedirect = () => {
if (path !== currentPath && path !== '') {
setRedirect({ target: id, redirect: path });
const handleRedirect = (newPath: string) => {
if (newPath === '/' || newPath === currentPath) {
return;
}
if (newPath.startsWith('preset-')) {
setRedirect({ target: id, redirect: newPath.slice(7) });
} else {
setRedirect({ target: id, redirect: newPath });
}
onClose();
};
const host = window.location.origin;
const canSubmit = path !== currentPath && path !== '';
const enabledPresets = data.filter((preset) => preset.enabled);
const viewOptions = [
...navigatorConstants.map((view) => ({
value: `/${view.url}`,
label: view.label,
})),
...enabledPresets.map((preset) => ({
value: `preset-${preset.alias}`,
label: `URL Preset: ${preset.alias}`,
})),
];
return (
<Modal isOpen={isOpen} onClose={onClose} variant='ontime'>
<ModalOverlay />
<ModalContent>
<ModalHeader>Redirect: {name}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<InputGroup variant='ontime-filled' size='md'>
<InputLeftAddon>{host}</InputLeftAddon>
<Input placeholder='minimal?key=0000ffff' value={path} onChange={(event) => setPath(event.target.value)} />
</InputGroup>
</ModalBody>
<ModalFooter>
<div className={style.buttonSection}>
<Button size='md' variant='ontime-subtle' onClick={onClose}>
Cancel
</Button>
<Button size='md' variant='ontime-filled' onClick={handleRedirect} isDisabled={!canSubmit}>
Submit
<Dialog
isOpen={isOpen}
onClose={onClose}
showCloseButton
showBackdrop
title={`Redirect: ${name}`}
bodyElements={
<>
<Info>
Remotely redirect the client to a different URL. <br />
Either by entering a custom path or selecting a URL Preset.
<br />
<br />
<AppLink search='settings=sharing__presets'>Manage URL Presets</AppLink>
</Info>
<div className={style.inlineEntry}>
<span className={style.label}>Enter custom path</span>
<label className={style.textEntry}>
{origin}
<Input placeholder='eg. /timer' fluid value={path} onChange={(event) => setPath(event.target.value)} />
</label>
<Button
variant='primary'
aria-label='Redirect'
disabled={path === currentPath || path === ''}
className={style.redirect}
onClick={() => handleRedirect(path)}
>
Redirect
<IoArrowForward />
</Button>
</div>
</ModalFooter>
</ModalContent>
</Modal>
<div>
<span className={style.label}>Select View or URL Preset</span>
<div className={style.inlineEntry}>
<label className={style.textEntry}>
{origin}
<Select
fluid
options={viewOptions}
defaultValue={viewOptions[0].value}
onValueChange={(value) => {
if (value === null) return;
setSelected(value);
}}
disabled={enabledPresets.length === 0}
/>
</label>
<Button
variant='primary'
aria-label='Redirect to preset'
className={style.redirect}
disabled={enabledPresets.length === 0 || selected === '/'}
onClick={() => handleRedirect(selected)}
>
Redirect <IoArrowForward />
</Button>
</div>
</div>
</>
}
/>
);
}
@@ -1,19 +1,9 @@
import { useState } from 'react';
import {
Button,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
} from '@chakra-ui/react';
import Button from '../../../common/components/buttons/Button';
import { setClientRemote } from '../../hooks/useSocket';
import style from './ClientModal.module.scss';
import Dialog from '../dialog/Dialog';
import Input from '../input/input/Input';
interface RenameClientModalProps {
id: string;
@@ -22,8 +12,7 @@ interface RenameClientModalProps {
onClose: () => void;
}
export function RenameClientModal(props: RenameClientModalProps) {
const { id, name: currentName = '', isOpen, onClose } = props;
export function RenameClientModal({ id, name: currentName = '', isOpen, onClose }: RenameClientModalProps) {
const [name, setName] = useState(currentName);
const { setClientName } = setClientRemote;
@@ -38,31 +27,24 @@ export function RenameClientModal(props: RenameClientModalProps) {
const canSubmit = name !== currentName && name !== '';
return (
<Modal isOpen={isOpen} onClose={onClose} variant='ontime'>
<ModalOverlay />
<ModalContent>
<ModalHeader>Rename: {currentName}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Input
variant='ontime-filled'
size='md'
placeholder='new name'
value={name}
onChange={(event) => setName(event.target.value)}
/>
</ModalBody>
<ModalFooter>
<div className={style.buttonSection}>
<Button size='md' variant='ontime-subtle' onClick={onClose}>
Cancel
</Button>
<Button size='md' variant='ontime-filled' onClick={handleRename} isDisabled={!canSubmit}>
Submit
</Button>
</div>
</ModalFooter>
</ModalContent>
</Modal>
<Dialog
isOpen={isOpen}
title={`Rename Client: ${currentName}`}
showCloseButton
onClose={onClose}
bodyElements={
<Input height='large' placeholder='New name' value={name} onChange={(event) => setName(event.target.value)} />
}
footerElements={
<>
<Button variant='subtle' size='large' onClick={onClose}>
Cancel
</Button>
<Button variant='primary' size='large' onClick={handleRename} disabled={!canSubmit}>
Submit
</Button>
</>
}
/>
);
}
@@ -1,8 +0,0 @@
.contextMenuButton {
position: absolute;
}
.contextMenuBackdrop {
position: fixed;
inset: 0;
}
@@ -1,99 +0,0 @@
// logic (with some modifications) culled from:
// https://github.com/lukasbach/chakra-ui-contextmenu/blob/main/src/ContextMenu.tsx
import { ReactElement } from 'react';
import { Menu, MenuButton, MenuGroup, MenuList } from '@chakra-ui/react';
import { IconType } from '@react-icons/all-files';
import { create } from 'zustand';
import { ContextMenuOption } from './ContextMenuOption';
import style from './ContextMenu.module.scss';
type ContextMenuCoords = {
x: number;
y: number;
};
export type OptionWithoutGroup = {
label: string;
isDisabled?: boolean;
icon: IconType;
onClick: () => void;
withDivider?: boolean;
};
export type OptionWithGroup = {
label: string;
group: Omit<OptionWithoutGroup, 'isGroup'>[];
};
export type Option = OptionWithoutGroup | OptionWithGroup;
const isOptionWithGroup = (option: Option): option is OptionWithGroup => 'group' in option;
type ContextMenuStore = {
coords: ContextMenuCoords;
options: Option[];
isOpen: boolean;
setContextMenu: (coords: ContextMenuCoords, options: Option[]) => void;
setIsOpen: (newIsOpen: boolean) => void;
};
export const useContextMenuStore = create<ContextMenuStore>((set) => ({
coords: { x: 0, y: 0 },
options: [],
isOpen: false,
setContextMenu: (coords, options) => set(() => ({ coords, options, isOpen: true })),
setIsOpen: (newIsOpen) => set(() => ({ isOpen: newIsOpen })),
}));
interface ContextMenuProps {
// ReactElement type required due to early `return` (line 51) returning {children}
children: ReactElement;
}
export const ContextMenu = ({ children }: ContextMenuProps) => {
const { coords, options, isOpen, setIsOpen } = useContextMenuStore();
const onClose = () => {
return setIsOpen(false);
};
if (!isOpen) {
return children;
}
return (
<>
{children}
<div className={style.contextMenuBackdrop} />
<Menu isOpen size='sm' gutter={0} onClose={onClose} isLazy lazyBehavior='unmount' variant='ontime-on-dark'>
<MenuButton
className={style.contextMenuButton}
aria-hidden
w={1}
h={1}
style={{
position: 'fixed',
left: coords.x,
top: coords.y,
}}
/>
<MenuList>
{options.map((option) =>
isOptionWithGroup(option) ? (
<MenuGroup key={option.label} title={option.label}>
{option.group.map((groupOption) => (
<ContextMenuOption key={groupOption.label} {...groupOption} />
))}
</MenuGroup>
) : (
<ContextMenuOption key={option.label} {...option} />
),
)}
</MenuList>
</Menu>
</>
);
};
@@ -1,12 +0,0 @@
import { MenuDivider, MenuItem } from '@chakra-ui/react';
import { OptionWithoutGroup } from './ContextMenu';
export const ContextMenuOption = ({ label, onClick, isDisabled, icon: Icon, withDivider }: OptionWithoutGroup) => (
<>
{withDivider && <MenuDivider />}
<MenuItem icon={<Icon style={{ fontSize: '1rem' }} />} onClick={onClick} isDisabled={isDisabled}>
{label}
</MenuItem>
</>
);
@@ -0,0 +1,40 @@
.copytag {
display: flex;
align-items: center;
}
.action {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.label {
border-radius: 3px 0 0 3px;
border: 1px solid $gray-1200;
background-color: $white-3;
padding-inline: 0.5rem 1rem;
line-height: 1em;
display: flex;
align-items: center;
color: $label-gray;
}
.small {
height: 1.5rem;
font-size: calc(1rem - 3px);
}
.medium {
height: 2rem;
font-size: calc(1rem - 2px);
}
.large {
height: 2.5rem;
font-weight: 600;
}
.copy {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
@@ -1,38 +1,57 @@
import { PropsWithChildren } from 'react';
import { Button, ButtonGroup, IconButton, Tooltip } from '@chakra-ui/react';
import { IoCopy } from '@react-icons/all-files/io5/IoCopy';
import { PropsWithChildren, useRef, useState } from 'react';
import { IoCheckmark, IoCopy } from 'react-icons/io5';
import { tooltipDelayFast } from '../../../ontimeConfig';
import { Size } from '../../models/Util.type';
import copyToClipboard from '../../utils/copyToClipboard';
import { copyToClipboard } from '../../utils/copyToClipboard';
import { cx } from '../../utils/styleUtils';
import Button from '../buttons/Button';
import IconButton from '../buttons/IconButton';
import style from './CopyTag.module.scss';
interface CopyTagProps {
label: string;
className?: string;
size?: Size;
copyValue: string;
disabled?: boolean;
onClick?: () => void;
size?: 'small' | 'medium' | 'large';
}
export default function CopyTag(props: PropsWithChildren<CopyTagProps>) {
const { label, className, size = 'xs', disabled, children } = props;
export default function CopyTag({
copyValue,
disabled,
size = 'medium',
children,
onClick,
}: PropsWithChildren<CopyTagProps>) {
const [copied, setCopied] = useState(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const handleClick = () => copyToClipboard(children as string);
const handleClick = async () => {
try {
await copyToClipboard(copyValue);
setCopied(true);
// reset copied state
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => setCopied(false), 2000);
} catch {
// ignore errors
}
};
return (
<Tooltip label={label} openDelay={tooltipDelayFast}>
<ButtonGroup size={size} isAttached className={className}>
<Button variant='ontime-subtle' tabIndex={-1} isDisabled={disabled}>
<div className={style.copytag}>
{onClick !== undefined ? (
<Button className={style.action} size={size} onClick={onClick} disabled={disabled}>
{children}
</Button>
<IconButton
aria-label={label}
icon={<IoCopy />}
variant='ontime-filled'
tabIndex={-1}
onClick={handleClick}
isDisabled={disabled}
/>
</ButtonGroup>
</Tooltip>
) : (
<div className={cx([style.label, style[size]])}>{children}</div>
)}
<IconButton className={style.copy} variant='primary' size={size} onClick={handleClick} disabled={disabled}>
{copied ? <IoCheckmark /> : <IoCopy />}
</IconButton>
</div>
);
}
@@ -1,5 +1,7 @@
.delaySymbol {
svg {
display: inline;
vertical-align: middle;
font-size: 1.5rem;
color: $ontime-delay;
margin: 0 auto;
@@ -1,40 +1,29 @@
import { Tooltip } from '@chakra-ui/react';
import { IoChevronDown } from '@react-icons/all-files/io5/IoChevronDown';
import { IoChevronUp } from '@react-icons/all-files/io5/IoChevronUp';
import { IoChevronDown, IoChevronUp } from 'react-icons/io5';
import { tooltipDelayFast } from '../../../ontimeConfig';
import { millisToDelayString } from '../../utils/dateConfig';
import Tooltip from '../tooltip/Tooltip';
import style from './DelayIndicator.module.scss';
interface DelayIndicatorProps {
delayValue?: number;
tooltipPrefix?: string;
}
export default function DelayIndicator(props: DelayIndicatorProps) {
const { delayValue } = props;
const { delayValue, tooltipPrefix } = props;
if (typeof delayValue === 'number') {
if (delayValue < 0) {
return (
<Tooltip openDelay={tooltipDelayFast} label={millisToDelayString(delayValue)}>
<span className={style.delaySymbol}>
<IoChevronDown />
</span>
</Tooltip>
);
}
if (delayValue > 0) {
return (
<Tooltip openDelay={tooltipDelayFast} label={millisToDelayString(delayValue)}>
<span className={style.delaySymbol}>
<IoChevronUp />
</span>
</Tooltip>
);
}
if (typeof delayValue !== 'number' || delayValue === 0) {
return null;
}
return null;
const delayString = tooltipPrefix
? `${tooltipPrefix} ${millisToDelayString(delayValue)}`
: millisToDelayString(delayValue);
return (
<Tooltip text={delayString} render={<span />} className={style.delaySymbol}>
{delayValue < 0 ? <IoChevronDown /> : <IoChevronUp />}
</Tooltip>
);
}
@@ -0,0 +1,56 @@
.dialog {
position: fixed;
top: 10%;
left: 50%;
transform: translateX(-50%);
padding-inline: 1rem;
min-width: min(600px, 90vw);
background-color: $gray-1250;
color: $ui-white;
border-radius: 3px;
box-shadow: $box-shadow-l1;
border: 1px solid $gray-1100;
outline: none;
}
.backdrop {
position: fixed;
inset: 0;
background-color: $backdrop-color;
transition: opacity 300ms cubic-bezier(0.45, 1.005, 0, 1.005);
&[data-starting-style],
&[data-ending-style] {
opacity: 0;
}
}
.title {
font-size: 1rem;
font-weight: 600;
padding-block: 1rem;
display: flex;
align-items: center;
justify-content: space-between;
}
.body {
display: flex;
flex-direction: column;
gap: 0.5rem;
max-height: min(80vh, 600px);
overflow-y: auto;
}
.footer {
padding-block: 1rem;
display: flex;
align-items: center;
justify-content: end;
gap: 1rem;
}
@@ -0,0 +1,52 @@
import { Dialog as BaseDialog } from '@base-ui/react/dialog';
import type { ReactNode } from 'react';
import { IoClose } from 'react-icons/io5';
import IconButton from '../buttons/IconButton';
import style from './Dialog.module.scss';
interface DialogProps {
isOpen: boolean;
title: string;
showCloseButton?: boolean;
showBackdrop?: boolean;
bodyElements: ReactNode;
footerElements?: ReactNode;
onClose: () => void;
}
export default function Dialog({
isOpen,
title,
showCloseButton,
showBackdrop,
bodyElements,
footerElements,
onClose,
}: DialogProps) {
return (
<BaseDialog.Root
open={isOpen}
onOpenChange={(isOpen) => {
if (!isOpen) onClose();
}}
>
<BaseDialog.Portal>
{showBackdrop && <BaseDialog.Backdrop className={style.backdrop} />}
<BaseDialog.Popup className={style.dialog}>
<div className={style.title}>
{title}
{showCloseButton && (
<IconButton variant='subtle-white' onClick={onClose}>
<IoClose />
</IconButton>
)}
</div>
<div className={style.body}>{bodyElements}</div>
<div className={style.footer}>{footerElements}</div>
</BaseDialog.Popup>
</BaseDialog.Portal>
</BaseDialog.Root>
);
}

Some files were not shown because too many files have changed in this diff Show More