@sixb/desktop 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,111 @@
1
+ import type { ShellConfig } from "./navigation"
2
+
3
+ /**
4
+ * Whether the window itself may show `url`: only the app and the API that serves its sign-in
5
+ * pages. Anything else a page links to opens in the user's browser instead.
6
+ */
7
+ export function mayNavigate(config: ShellConfig, url: string): boolean {
8
+ try {
9
+ const { origin } = new URL(url)
10
+ return origin === new URL(config.appUrl).origin || origin === new URL(config.apiOrigin).origin
11
+ } catch {
12
+ return false
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Whether `url` is on the app's own origin. Camera, location and the like are the app's to ask for,
18
+ * and not a frame it embeds from elsewhere.
19
+ */
20
+ export function isAppUrl(config: ShellConfig, url: string): boolean {
21
+ try {
22
+ return new URL(url).origin === new URL(config.appUrl).origin
23
+ } catch {
24
+ return false
25
+ }
26
+ }
27
+
28
+ /**
29
+ * What the window paints before a page has: the loading page's own background, so the window
30
+ * opens, and passes from that page to the app, without a flash of white.
31
+ */
32
+ export const WINDOW_BACKGROUND = { light: "#fafafa", dark: "#0a0a0a" } as const
33
+
34
+ /** The band at the top of a window without a title bar where macOS draws the window controls. */
35
+ export const CONTROLS_HEIGHT_PX = 36
36
+
37
+ /**
38
+ * Styles for a window without a title bar, inserted into every page it shows.
39
+ *
40
+ * The page reaches the top edge, under the window controls, so two things a title bar did have
41
+ * to come from the page: somewhere to grab the window, and room for the controls.
42
+ * - `#sixb-desktop-drag` (added by the preload) is the drag handle: a strip across the top, above
43
+ * the page. It ignores the pointer, and every control is cut out of the draggable area, so
44
+ * buttons and links under the strip still take their clicks and the space between them moves
45
+ * the window. Something clickable that is none of the things listed here needs
46
+ * `-webkit-app-region: no-drag` of its own.
47
+ * - The shared sidebar is moved down so most apps need nothing. Other layouts can use
48
+ * `html[data-sixb-desktop]` and `--sixb-desktop-controls-height`, which the preload sets.
49
+ */
50
+ export const HIDDEN_TITLE_BAR_CSS = [
51
+ `#sixb-desktop-drag{position:fixed;top:0;left:0;right:0;height:${CONTROLS_HEIGHT_PX}px;`,
52
+ "z-index:2147483647;pointer-events:none;-webkit-app-region:drag}",
53
+ `${[
54
+ "a",
55
+ "button",
56
+ "input",
57
+ "select",
58
+ "textarea",
59
+ "summary",
60
+ "label",
61
+ "[role=button]",
62
+ "[role=link]",
63
+ "[role=tab]",
64
+ "[role=menuitem]",
65
+ "[role=switch]",
66
+ "[role=checkbox]",
67
+ "[role=combobox]",
68
+ "[contenteditable]",
69
+ "[tabindex]:not([tabindex='-1'])",
70
+ ].join(",")}{-webkit-app-region:no-drag}`,
71
+ `[data-slot="sidebar-inner"]{padding-top:calc(${CONTROLS_HEIGHT_PX}px - 0.5rem)}`,
72
+ ].join("")
73
+
74
+ /**
75
+ * The preload of a window without a title bar: runs in every page before the page's own scripts.
76
+ *
77
+ * It marks `<html>` the moment the element exists, so an app can read the mark on its first
78
+ * render, and adds the drag handle once there is a body to hold it. Written to a file by
79
+ * `writeDesktopApp`, which is why it is source text and not a function.
80
+ */
81
+ export const HIDDEN_TITLE_BAR_PRELOAD = `(() => {
82
+ const mark = () => {
83
+ const root = document.documentElement
84
+ if (!root) return false
85
+ root.dataset.sixbDesktop = "inset"
86
+ root.style.setProperty("--sixb-desktop-controls-height", "${CONTROLS_HEIGHT_PX}px")
87
+ return true
88
+ }
89
+ if (!mark()) {
90
+ const observer = new MutationObserver(() => {
91
+ if (mark()) observer.disconnect()
92
+ })
93
+ observer.observe(document, { childList: true })
94
+ }
95
+
96
+ const addHandle = () => {
97
+ if (!document.body || document.getElementById("sixb-desktop-drag")) return
98
+ const handle = document.createElement("div")
99
+ handle.id = "sixb-desktop-drag"
100
+ handle.setAttribute("aria-hidden", "true")
101
+ // First in the body, not last: the draggable area is built in document order, so a control
102
+ // is only cut out of it if it comes after the handle.
103
+ document.body.prepend(handle)
104
+ }
105
+ if (document.readyState === "loading") {
106
+ document.addEventListener("DOMContentLoaded", addHandle, { once: true })
107
+ } else {
108
+ addHandle()
109
+ }
110
+ })()
111
+ `