@pathmx/player 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +43 -0
- package/LICENSE.md +172 -0
- package/README.md +161 -0
- package/actions.ts +69 -0
- package/client.ts +1047 -0
- package/controls.tsx +716 -0
- package/fragments.ts +6 -0
- package/gestures.ts +143 -0
- package/grid.ts +199 -0
- package/guide.ts +86 -0
- package/index.ts +59 -0
- package/keyboard.ts +172 -0
- package/media.ts +47 -0
- package/navigation.ts +97 -0
- package/notes.css +65 -0
- package/notes.ts +10 -0
- package/package.json +32 -0
- package/play.css +1052 -0
- package/preferences.ts +71 -0
- package/progress.tsx +70 -0
- package/ref/deckset-exports/Letters from Sweden.dstheme +1 -0
- package/ref/deckset-exports/Merriweather.dstheme +1 -0
- package/ref/deckset-exports/Next.dstheme +1 -0
- package/route.ts +231 -0
- package/scroll.ts +276 -0
- package/themes.ts +39 -0
- package/variants.ts +36 -0
package/navigation.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { PlayIntensity, PlayMode, PlayThemeChoice } from "./controls.ts"
|
|
2
|
+
|
|
3
|
+
type PlayLinkState = {
|
|
4
|
+
mode: PlayMode
|
|
5
|
+
intensity: PlayIntensity
|
|
6
|
+
theme: PlayThemeChoice
|
|
7
|
+
guide: boolean
|
|
8
|
+
notes: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type BeatFragmentNavigation = (fragment: string) => boolean
|
|
12
|
+
|
|
13
|
+
function decodedFragment(url: URL) {
|
|
14
|
+
if (!url.hash) return
|
|
15
|
+
try {
|
|
16
|
+
return decodeURIComponent(url.hash.slice(1)) || undefined
|
|
17
|
+
} catch {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function eligibleLink(event: MouseEvent) {
|
|
23
|
+
if (
|
|
24
|
+
event.defaultPrevented ||
|
|
25
|
+
event.button !== 0 ||
|
|
26
|
+
event.metaKey ||
|
|
27
|
+
event.ctrlKey ||
|
|
28
|
+
event.shiftKey ||
|
|
29
|
+
event.altKey
|
|
30
|
+
) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
const target = event.target
|
|
34
|
+
if (!(target instanceof Element)) return
|
|
35
|
+
const link = target.closest<HTMLAnchorElement>("a[href]")
|
|
36
|
+
if (
|
|
37
|
+
!link ||
|
|
38
|
+
link.download ||
|
|
39
|
+
(link.target && link.target !== "_self") ||
|
|
40
|
+
link.relList.contains("external")
|
|
41
|
+
) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
return link
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Carry an active Play session across ordinary same-origin Source links. */
|
|
48
|
+
export function installPlayLinkContinuation(
|
|
49
|
+
current: () => PlayLinkState | undefined,
|
|
50
|
+
navigateToBeat?: BeatFragmentNavigation,
|
|
51
|
+
) {
|
|
52
|
+
document.addEventListener(
|
|
53
|
+
"click",
|
|
54
|
+
(event) => {
|
|
55
|
+
if (!(event instanceof MouseEvent)) return
|
|
56
|
+
const state = current()
|
|
57
|
+
const link = state && eligibleLink(event)
|
|
58
|
+
if (!link) return
|
|
59
|
+
|
|
60
|
+
const destination = new URL(link.href, location.href)
|
|
61
|
+
if (
|
|
62
|
+
destination.origin !== location.origin ||
|
|
63
|
+
destination.pathname.endsWith(".md") ||
|
|
64
|
+
destination.pathname.startsWith("/.pmx/") ||
|
|
65
|
+
destination.pathname.startsWith("/actions/")
|
|
66
|
+
) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
if (
|
|
70
|
+
destination.pathname === location.pathname &&
|
|
71
|
+
destination.search === location.search
|
|
72
|
+
) {
|
|
73
|
+
const fragment = decodedFragment(destination)
|
|
74
|
+
if (fragment && navigateToBeat?.(fragment)) event.preventDefault()
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
destination.searchParams.set("play", state.mode)
|
|
79
|
+
destination.searchParams.set("intensity", state.intensity)
|
|
80
|
+
if (state.theme === "source")
|
|
81
|
+
destination.searchParams.delete("play-theme")
|
|
82
|
+
else destination.searchParams.set("play-theme", state.theme)
|
|
83
|
+
if (state.guide) destination.searchParams.delete("play-guide")
|
|
84
|
+
else destination.searchParams.set("play-guide", "off")
|
|
85
|
+
if (state.notes) destination.searchParams.delete("play-notes")
|
|
86
|
+
else destination.searchParams.set("play-notes", "off")
|
|
87
|
+
const authoredHref = link.getAttribute("href")
|
|
88
|
+
link.href = destination.href
|
|
89
|
+
setTimeout(() => {
|
|
90
|
+
if (!link.isConnected) return
|
|
91
|
+
if (authoredHref === null) link.removeAttribute("href")
|
|
92
|
+
else link.setAttribute("href", authoredHref)
|
|
93
|
+
})
|
|
94
|
+
},
|
|
95
|
+
{ capture: true },
|
|
96
|
+
)
|
|
97
|
+
}
|
package/notes.css
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
.pmx-play-note {
|
|
2
|
+
position: fixed;
|
|
3
|
+
z-index: 999;
|
|
4
|
+
right: max(1rem, env(safe-area-inset-right));
|
|
5
|
+
bottom: calc(max(1rem, env(safe-area-inset-bottom)) + 3.25rem);
|
|
6
|
+
display: grid;
|
|
7
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
8
|
+
align-items: start;
|
|
9
|
+
gap: 0.7rem;
|
|
10
|
+
box-sizing: border-box;
|
|
11
|
+
width: min(25rem, calc(100vw - 2rem));
|
|
12
|
+
max-height: min(34vh, 18rem);
|
|
13
|
+
padding: 0.85rem 1rem;
|
|
14
|
+
overflow-y: auto;
|
|
15
|
+
border: 1px solid
|
|
16
|
+
color-mix(in oklab, var(--pmx-color-border, currentColor) 78%, transparent);
|
|
17
|
+
border-radius: 0.85rem;
|
|
18
|
+
color: var(--pmx-color-fg, CanvasText);
|
|
19
|
+
background: color-mix(
|
|
20
|
+
in oklab,
|
|
21
|
+
var(--pmx-color-surface, Canvas) 94%,
|
|
22
|
+
transparent
|
|
23
|
+
);
|
|
24
|
+
box-shadow: 0 0.75rem 2rem rgb(0 0 0 / 14%);
|
|
25
|
+
font: 520 0.875rem/1.45 var(--pmx-font-body, system-ui, sans-serif);
|
|
26
|
+
backdrop-filter: blur(14px);
|
|
27
|
+
animation: pmx-play-note-in 180ms ease-out;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.pmx-play-note > svg {
|
|
31
|
+
width: 1rem;
|
|
32
|
+
height: 1rem;
|
|
33
|
+
margin-block-start: 0.2rem;
|
|
34
|
+
color: var(--pmx-color-accent, #3897ff);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.pmx-play-note p {
|
|
38
|
+
margin: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.pmx-play-note p + p {
|
|
42
|
+
margin-block-start: 0.65rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@keyframes pmx-play-note-in {
|
|
46
|
+
from {
|
|
47
|
+
opacity: 0;
|
|
48
|
+
transform: translateY(0.35rem);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@media (max-width: 42rem), (pointer: coarse) {
|
|
53
|
+
.pmx-play-note {
|
|
54
|
+
right: max(0.75rem, env(safe-area-inset-right));
|
|
55
|
+
bottom: calc(max(0.75rem, env(safe-area-inset-bottom)) + 3.5rem);
|
|
56
|
+
width: min(24rem, calc(100vw - 1.5rem));
|
|
57
|
+
max-height: min(30vh, 14rem);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@media (prefers-reduced-motion: reduce) {
|
|
62
|
+
.pmx-play-note {
|
|
63
|
+
animation: none;
|
|
64
|
+
}
|
|
65
|
+
}
|
package/notes.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const NOTE_PARAGRAPH = /<p>\s*\^\s+([\s\S]*?)<\/p>/gi
|
|
2
|
+
|
|
3
|
+
/** Compile Deckset-style caret paragraphs into hidden Player note metadata. */
|
|
4
|
+
export function rewritePlayNotes(html: string) {
|
|
5
|
+
if (!html.includes("^")) return html
|
|
6
|
+
return html.replace(
|
|
7
|
+
NOTE_PARAGRAPH,
|
|
8
|
+
"<aside data-pmx-play-note hidden>$1</aside>",
|
|
9
|
+
)
|
|
10
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pathmx/player",
|
|
3
|
+
"description": "Focused reading and presentation experiences for PathMX.",
|
|
4
|
+
"version": "0.5.0",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/pathmx/pathmx-beta.git",
|
|
9
|
+
"directory": "paths/player"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/pathmx/pathmx-beta#readme",
|
|
12
|
+
"bugs": "https://github.com/pathmx/pathmx-beta/issues",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public",
|
|
15
|
+
"registry": "https://registry.npmjs.org/",
|
|
16
|
+
"tag": "beta"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"exports": "./index.ts",
|
|
20
|
+
"engines": {
|
|
21
|
+
"bun": ">=1.4.0"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@pathmx/core": "^0.5.0",
|
|
25
|
+
"react": ">=19.0.0",
|
|
26
|
+
"react-dom": ">=19.0.0"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@pathmx/react": "0.5.0",
|
|
30
|
+
"lucide-react": "^1.34.0"
|
|
31
|
+
}
|
|
32
|
+
}
|