@panphora/clayjs 1.11.2 → 1.12.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/README.md +3 -1
- package/dist/clay.standalone.js +13465 -10302
- package/entries/clay-data.js +1 -1
- package/entries/sap.js +6 -6
- package/package.json +1 -1
- package/src/core/draft-mode.js +57 -0
- package/src/core/edit-mode.js +7 -2
- package/src/core/host-meta.js +2 -0
- package/src/core/is-edit-mode.js +27 -9
- package/src/core/page-data.js +12 -0
- package/src/core/save-core.js +22 -2
- package/src/core/save.js +2 -2
- package/src/core/unsaved-warning.js +5 -2
- package/src/lib/root-attrs.js +1 -1
- package/src/loader-logic.js +21 -2
- package/src/loader.js +10 -3
- package/src/plugins/visitor-drafts/intro-style.js +156 -0
- package/src/plugins/visitor-drafts/intro.js +231 -0
- package/src/plugins/visitor-drafts/owner-chip.js +151 -0
- package/src/plugins/visitor-drafts/prompt-style.js +173 -0
- package/src/plugins/visitor-drafts/prompt.js +177 -0
- package/src/plugins/visitor-drafts/source.js +21 -0
- package/src/plugins/visitor-drafts/store.js +342 -0
- package/src/plugins/visitor-drafts/ui-memory.js +14 -0
- package/src/plugins/visitor-drafts.js +111 -0
- package/src/standalone.js +5 -0
- package/src/vendor/hyper-html-api-data.vendor.js +4 -0
- package/src/vendor/hyper-undo.vendor.js +1 -1
- package/src/vendor/hypercms.vendor.js +21 -21
- package/src/vendor/richclay.vendor.js +15 -15
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { INTRO_CSS } from "./intro-style.js";
|
|
2
|
+
|
|
3
|
+
export const COPY = {
|
|
4
|
+
kicker: "Free app",
|
|
5
|
+
download: "Download free",
|
|
6
|
+
signupLabel: "I make one of these about every two weeks. Get the next one by email.",
|
|
7
|
+
emailPlaceholder: "you@example.com",
|
|
8
|
+
signup: "Send me the next one",
|
|
9
|
+
signupThanks: "Thanks. Check your email for a link to confirm.",
|
|
10
|
+
signupFailed: "That didn't go through. Check the address and try again.",
|
|
11
|
+
start: (appName) => `Start using ${appName}`,
|
|
12
|
+
about: "About this app",
|
|
13
|
+
label: (appName) => `About ${appName}`,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const FOCUSABLE = "a[href], button, input, select, textarea, [tabindex]:not([tabindex='-1'])";
|
|
17
|
+
|
|
18
|
+
function forced() {
|
|
19
|
+
try {
|
|
20
|
+
return new URLSearchParams(window.location.search).get("intro") === "1";
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function stylesOnce() {
|
|
27
|
+
if (document.getElementById("clay-intro-style")) return;
|
|
28
|
+
const style = document.createElement("style");
|
|
29
|
+
style.id = "clay-intro-style";
|
|
30
|
+
style.setAttribute("clay", "editor-ui");
|
|
31
|
+
style.textContent = INTRO_CSS;
|
|
32
|
+
document.head.appendChild(style);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function focusApp() {
|
|
36
|
+
const next = [...document.querySelectorAll(FOCUSABLE)].find(
|
|
37
|
+
(el) => !el.disabled && !el.closest("[clay~='editor-ui']") && (typeof el.checkVisibility !== "function" || el.checkVisibility())
|
|
38
|
+
);
|
|
39
|
+
if (next) next.focus();
|
|
40
|
+
else document.body.focus();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function mountIntro({ memory, template, appName, link, tag, source, hadDraft, prompt, snapshot }) {
|
|
44
|
+
if (!template) return null;
|
|
45
|
+
const opts = { memory, template, appName, link, tag, source, hadDraft, prompt, snapshot };
|
|
46
|
+
if (forced()) return mount(opts);
|
|
47
|
+
if (memory.get().intro.done || hadDraft) return mountAbout(opts);
|
|
48
|
+
if (window.location.hash) return null;
|
|
49
|
+
if (window.matchMedia && window.matchMedia("print").matches) return null;
|
|
50
|
+
return mount(opts);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function mountAbout(opts) {
|
|
54
|
+
stylesOnce();
|
|
55
|
+
const button = document.createElement("button");
|
|
56
|
+
button.type = "button";
|
|
57
|
+
button.className = "clay-about";
|
|
58
|
+
button.setAttribute("clay", "editor-ui");
|
|
59
|
+
button.setAttribute("sap-ignore", "");
|
|
60
|
+
button.textContent = COPY.about;
|
|
61
|
+
button.addEventListener("click", () => {
|
|
62
|
+
button.remove();
|
|
63
|
+
mount({ ...opts, fromAbout: true });
|
|
64
|
+
});
|
|
65
|
+
document.body.appendChild(button);
|
|
66
|
+
return button;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function mount(opts) {
|
|
70
|
+
const { memory, template, appName, link, tag, source, prompt, snapshot, fromAbout } = opts;
|
|
71
|
+
stylesOnce();
|
|
72
|
+
|
|
73
|
+
const section = document.createElement("section");
|
|
74
|
+
section.className = "clay-intro";
|
|
75
|
+
section.setAttribute("clay", "editor-ui");
|
|
76
|
+
section.setAttribute("sap-ignore", "");
|
|
77
|
+
section.setAttribute("aria-label", COPY.label(appName));
|
|
78
|
+
|
|
79
|
+
const inner = document.createElement("div");
|
|
80
|
+
inner.className = "clay-intro__inner";
|
|
81
|
+
|
|
82
|
+
const kicker = document.createElement("p");
|
|
83
|
+
kicker.className = "clay-intro__kicker";
|
|
84
|
+
kicker.textContent = COPY.kicker;
|
|
85
|
+
|
|
86
|
+
const name = document.createElement("h1");
|
|
87
|
+
name.className = "clay-intro__name";
|
|
88
|
+
name.textContent = appName;
|
|
89
|
+
|
|
90
|
+
const actions = document.createElement("div");
|
|
91
|
+
actions.className = "clay-intro__actions";
|
|
92
|
+
|
|
93
|
+
const download = document.createElement("a");
|
|
94
|
+
download.className = "clay-intro__download";
|
|
95
|
+
download.href = link;
|
|
96
|
+
download.textContent = COPY.download;
|
|
97
|
+
|
|
98
|
+
const form = document.createElement("form");
|
|
99
|
+
form.className = "clay-intro__signup";
|
|
100
|
+
form.noValidate = true;
|
|
101
|
+
|
|
102
|
+
const label = document.createElement("label");
|
|
103
|
+
label.setAttribute("for", "clay-intro-email");
|
|
104
|
+
label.textContent = COPY.signupLabel;
|
|
105
|
+
|
|
106
|
+
const row = document.createElement("div");
|
|
107
|
+
row.className = "clay-intro__row";
|
|
108
|
+
|
|
109
|
+
const email = document.createElement("input");
|
|
110
|
+
email.id = "clay-intro-email";
|
|
111
|
+
email.type = "email";
|
|
112
|
+
email.autocomplete = "email";
|
|
113
|
+
email.required = true;
|
|
114
|
+
email.placeholder = COPY.emailPlaceholder;
|
|
115
|
+
|
|
116
|
+
const send = document.createElement("button");
|
|
117
|
+
send.type = "submit";
|
|
118
|
+
send.textContent = COPY.signup;
|
|
119
|
+
|
|
120
|
+
row.append(email, send);
|
|
121
|
+
|
|
122
|
+
const honey = document.createElement("input");
|
|
123
|
+
honey.className = "clay-intro__hp";
|
|
124
|
+
honey.name = "website";
|
|
125
|
+
honey.tabIndex = -1;
|
|
126
|
+
honey.autocomplete = "off";
|
|
127
|
+
honey.setAttribute("aria-hidden", "true");
|
|
128
|
+
|
|
129
|
+
const message = document.createElement("p");
|
|
130
|
+
message.className = "clay-intro__msg";
|
|
131
|
+
message.setAttribute("role", "status");
|
|
132
|
+
message.hidden = true;
|
|
133
|
+
|
|
134
|
+
form.append(label, row, honey, message);
|
|
135
|
+
actions.append(download, form);
|
|
136
|
+
|
|
137
|
+
const start = document.createElement("button");
|
|
138
|
+
start.type = "button";
|
|
139
|
+
start.className = "clay-intro__start";
|
|
140
|
+
start.textContent = COPY.start(appName);
|
|
141
|
+
|
|
142
|
+
inner.append(kicker, name, template.content.cloneNode(true), actions, start);
|
|
143
|
+
section.appendChild(inner);
|
|
144
|
+
|
|
145
|
+
let observer = null;
|
|
146
|
+
let gone = false;
|
|
147
|
+
|
|
148
|
+
function stop() {
|
|
149
|
+
document.removeEventListener("input", onUse, true);
|
|
150
|
+
document.removeEventListener("change", onUse, true);
|
|
151
|
+
document.removeEventListener("clay:dirty", onUse, true);
|
|
152
|
+
if (observer) { observer.disconnect(); observer = null; }
|
|
153
|
+
if (prompt) prompt.hideForIntro(false);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function dismiss(reason, target) {
|
|
157
|
+
if (gone) return;
|
|
158
|
+
gone = true;
|
|
159
|
+
const anchor = target && target.getBoundingClientRect ? target : document.activeElement;
|
|
160
|
+
const before = anchor && anchor.getBoundingClientRect ? anchor.getBoundingClientRect().top : 0;
|
|
161
|
+
memory.get().intro.done = reason;
|
|
162
|
+
memory.save();
|
|
163
|
+
stop();
|
|
164
|
+
section.remove();
|
|
165
|
+
if (reason === "button") {
|
|
166
|
+
window.scrollTo({ top: 0 });
|
|
167
|
+
focusApp();
|
|
168
|
+
} else if (anchor && anchor.getBoundingClientRect) {
|
|
169
|
+
window.scrollBy(0, anchor.getBoundingClientRect().top - before);
|
|
170
|
+
}
|
|
171
|
+
if (fromAbout) mountAbout(opts);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function unchanged() {
|
|
175
|
+
return !snapshot || snapshot() === baseline;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function onUse(event) {
|
|
179
|
+
if (section.contains(event.target)) return;
|
|
180
|
+
if (event.type === "clay:dirty") {
|
|
181
|
+
if (!unchanged()) dismiss("use", event.target);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (!unchanged() || event.target?.closest?.("[persist]")) {
|
|
185
|
+
dismiss("use", event.target);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const target = event.target;
|
|
189
|
+
setTimeout(() => {
|
|
190
|
+
if (!gone && !unchanged()) dismiss("use", target);
|
|
191
|
+
}, 0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
form.addEventListener("submit", (event) => {
|
|
195
|
+
event.preventDefault();
|
|
196
|
+
fetch("/_/newsletter/subscribe", {
|
|
197
|
+
method: "POST",
|
|
198
|
+
headers: { "Content-Type": "application/json" },
|
|
199
|
+
credentials: "same-origin",
|
|
200
|
+
body: JSON.stringify({ email: email.value.trim(), tags: [tag], source, website: honey.value }),
|
|
201
|
+
}).then((response) => {
|
|
202
|
+
if (!response.ok) throw new Error("signup failed");
|
|
203
|
+
row.hidden = true;
|
|
204
|
+
message.textContent = COPY.signupThanks;
|
|
205
|
+
message.hidden = false;
|
|
206
|
+
}).catch(() => {
|
|
207
|
+
message.textContent = COPY.signupFailed;
|
|
208
|
+
message.hidden = false;
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
start.addEventListener("click", () => dismiss("button"));
|
|
213
|
+
|
|
214
|
+
const baseline = snapshot ? snapshot() : null;
|
|
215
|
+
document.body.prepend(section);
|
|
216
|
+
document.addEventListener("input", onUse, true);
|
|
217
|
+
document.addEventListener("change", onUse, true);
|
|
218
|
+
document.addEventListener("clay:dirty", onUse, true);
|
|
219
|
+
|
|
220
|
+
if (prompt) {
|
|
221
|
+
prompt.hideForIntro(true);
|
|
222
|
+
if (typeof IntersectionObserver === "function") {
|
|
223
|
+
observer = new IntersectionObserver((entries) => {
|
|
224
|
+
for (const entry of entries) prompt.hideForIntro(entry.intersectionRatio > 0.3);
|
|
225
|
+
});
|
|
226
|
+
observer.observe(section);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return section;
|
|
231
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { isDraftMode } from "../../core/is-edit-mode.js";
|
|
2
|
+
|
|
3
|
+
export const COPY = {
|
|
4
|
+
draft: "You're seeing a private draft, like a visitor.",
|
|
5
|
+
draftAction: "Edit the original",
|
|
6
|
+
edit: "You're editing the original. Visitors get these changes.",
|
|
7
|
+
editAction: "Back to my draft",
|
|
8
|
+
close: "Hide this note",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const CHIP_CSS = `.clay-owner {
|
|
12
|
+
--hcp-ground: #FDF8F0;
|
|
13
|
+
--hcp-surface: #FFFCF6;
|
|
14
|
+
--hcp-sunk: #F4ECDF;
|
|
15
|
+
--hcp-ink: #2B241B;
|
|
16
|
+
--hcp-ink-2: #4A4033;
|
|
17
|
+
--hcp-muted: #6B5942;
|
|
18
|
+
--hcp-line: #E8DCC6;
|
|
19
|
+
--hcp-line-2: #D8C8AF;
|
|
20
|
+
--hcp-brass: #8F6310;
|
|
21
|
+
--hcp-shadow: 0 1px 2px rgba(23, 28, 36, .06), 0 14px 34px -14px rgba(43, 36, 27, .32);
|
|
22
|
+
--hcp-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
23
|
+
--hcp-lift: 0px;
|
|
24
|
+
color-scheme: light;
|
|
25
|
+
}
|
|
26
|
+
@media (prefers-color-scheme: dark) {
|
|
27
|
+
.clay-owner {
|
|
28
|
+
--hcp-surface: #11131E; --hcp-sunk: #1A1D2C;
|
|
29
|
+
--hcp-ink: #ECEAF2; --hcp-ink-2: #C9CDE0; --hcp-muted: #8A90AB;
|
|
30
|
+
--hcp-line: #262B42; --hcp-line-2: #353B52;
|
|
31
|
+
--hcp-brass: #D9A445;
|
|
32
|
+
--hcp-shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 16px 38px -14px rgba(0, 0, 0, .7);
|
|
33
|
+
color-scheme: dark;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
.clay-owner, .clay-owner *, .clay-owner *::before, .clay-owner *::after {
|
|
37
|
+
box-sizing: border-box; margin: 0; padding: 0; border: 0;
|
|
38
|
+
font: inherit; letter-spacing: normal; text-transform: none; text-shadow: none;
|
|
39
|
+
line-height: inherit; color: inherit; background: none; float: none; max-width: none;
|
|
40
|
+
}
|
|
41
|
+
.clay-owner {
|
|
42
|
+
position: fixed; left: 12px; bottom: calc(12px + var(--hcp-lift)); z-index: 2147482000;
|
|
43
|
+
display: flex; align-items: center; gap: 10px;
|
|
44
|
+
max-width: min(460px, calc(100vw - 24px));
|
|
45
|
+
padding: 8px 8px 8px 12px;
|
|
46
|
+
font-family: var(--hcp-sans); font-size: 13px; font-weight: 400; line-height: 1.45;
|
|
47
|
+
color: var(--hcp-ink-2); text-align: left; background: var(--hcp-surface);
|
|
48
|
+
border: 1px solid var(--hcp-line-2); border-left: 3px solid var(--hcp-brass);
|
|
49
|
+
box-shadow: var(--hcp-shadow);
|
|
50
|
+
-webkit-font-smoothing: antialiased;
|
|
51
|
+
}
|
|
52
|
+
.clay-owner__action {
|
|
53
|
+
flex: none; font-weight: 600; color: var(--hcp-brass); cursor: pointer;
|
|
54
|
+
text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--hcp-line-2);
|
|
55
|
+
white-space: nowrap;
|
|
56
|
+
}
|
|
57
|
+
.clay-owner__action:hover { color: var(--hcp-ink); }
|
|
58
|
+
.clay-owner__x {
|
|
59
|
+
flex: none; display: inline-flex; align-items: center; justify-content: center;
|
|
60
|
+
width: 24px; height: 24px; font-size: 15px; color: var(--hcp-muted); cursor: pointer;
|
|
61
|
+
}
|
|
62
|
+
.clay-owner__x:hover { color: var(--hcp-ink); }
|
|
63
|
+
.clay-owner__action:focus-visible, .clay-owner__x:focus-visible { outline: 2px solid var(--hcp-brass); outline-offset: 2px; }
|
|
64
|
+
@media (pointer: coarse) {
|
|
65
|
+
.clay-owner__action, .clay-owner__x { min-height: 44px; }
|
|
66
|
+
.clay-owner__x { width: 44px; }
|
|
67
|
+
}
|
|
68
|
+
@media (prefers-reduced-motion: reduce) { .clay-owner { transition: none; } }
|
|
69
|
+
@media (forced-colors: active) { .clay-owner { border: 1px solid CanvasText; } }
|
|
70
|
+
@media print { .clay-owner { display: none; } }
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
function stylesOnce() {
|
|
74
|
+
if (document.getElementById("clay-owner-style")) return;
|
|
75
|
+
const style = document.createElement("style");
|
|
76
|
+
style.id = "clay-owner-style";
|
|
77
|
+
style.setAttribute("clay", "editor-ui");
|
|
78
|
+
style.textContent = CHIP_CSS;
|
|
79
|
+
document.head.appendChild(style);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function mountOwnerChip() {
|
|
83
|
+
if (document.getElementById("clay-owner")) return null;
|
|
84
|
+
stylesOnce();
|
|
85
|
+
|
|
86
|
+
const draft = isDraftMode;
|
|
87
|
+
const chip = document.createElement("div");
|
|
88
|
+
chip.id = "clay-owner";
|
|
89
|
+
chip.className = "clay-owner";
|
|
90
|
+
chip.setAttribute("clay", "editor-ui");
|
|
91
|
+
chip.setAttribute("sap-ignore", "");
|
|
92
|
+
chip.setAttribute("role", "status");
|
|
93
|
+
|
|
94
|
+
const lead = document.createElement("span");
|
|
95
|
+
lead.className = "clay-owner__lead";
|
|
96
|
+
lead.textContent = draft ? COPY.draft : COPY.edit;
|
|
97
|
+
|
|
98
|
+
const action = document.createElement("button");
|
|
99
|
+
action.type = "button";
|
|
100
|
+
action.className = "clay-owner__action";
|
|
101
|
+
action.textContent = draft ? COPY.draftAction : COPY.editAction;
|
|
102
|
+
action.addEventListener("click", () => {
|
|
103
|
+
const api = window.clay && window.clay.visitorDrafts;
|
|
104
|
+
if (!api) return;
|
|
105
|
+
if (draft) api.editOriginal();
|
|
106
|
+
else api.backToDraft();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const close = document.createElement("button");
|
|
110
|
+
close.type = "button";
|
|
111
|
+
close.className = "clay-owner__x";
|
|
112
|
+
close.setAttribute("aria-label", COPY.close);
|
|
113
|
+
close.textContent = "\u00d7";
|
|
114
|
+
close.addEventListener("click", () => { chip.remove(); liftOff(); });
|
|
115
|
+
|
|
116
|
+
chip.append(lead, action, close);
|
|
117
|
+
document.body.appendChild(chip);
|
|
118
|
+
|
|
119
|
+
let frame = 0;
|
|
120
|
+
function lift() {
|
|
121
|
+
if (frame) return;
|
|
122
|
+
frame = requestAnimationFrame(() => {
|
|
123
|
+
frame = 0;
|
|
124
|
+
const y = window.innerHeight - 3;
|
|
125
|
+
let raise = 0;
|
|
126
|
+
const hits = document.elementsFromPoint ? document.elementsFromPoint(24, y) : [];
|
|
127
|
+
for (const hit of hits) {
|
|
128
|
+
for (let el = hit; el && el !== document.body && el !== document.documentElement; el = el.parentElement) {
|
|
129
|
+
if (chip.contains(el)) break;
|
|
130
|
+
const position = getComputedStyle(el).position;
|
|
131
|
+
if (position === "fixed" || position === "sticky") {
|
|
132
|
+
raise = Math.max(raise, Math.ceil(window.innerHeight - el.getBoundingClientRect().top));
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
chip.style.setProperty("--hcp-lift", (raise > 0 && raise < window.innerHeight / 3 ? raise : 0) + "px");
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function liftOff() {
|
|
142
|
+
removeEventListener("scroll", lift);
|
|
143
|
+
removeEventListener("resize", lift);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
addEventListener("scroll", lift, { passive: true });
|
|
147
|
+
addEventListener("resize", lift);
|
|
148
|
+
lift();
|
|
149
|
+
|
|
150
|
+
return chip;
|
|
151
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
export const PROMPT_CSS = `.hcp {
|
|
2
|
+
--hcp-ground: #FDF8F0;
|
|
3
|
+
--hcp-surface: #FFFCF6;
|
|
4
|
+
--hcp-sunk: #F4ECDF;
|
|
5
|
+
--hcp-ink: #2B241B;
|
|
6
|
+
--hcp-ink-2: #4A4033;
|
|
7
|
+
--hcp-muted: #6B5942;
|
|
8
|
+
--hcp-line: #E8DCC6;
|
|
9
|
+
--hcp-line-2: #D8C8AF;
|
|
10
|
+
--hcp-brass: #8F6310;
|
|
11
|
+
--hcp-brass-soft: #F0E4CC;
|
|
12
|
+
--hcp-face: #F1E7D4;
|
|
13
|
+
--hcp-edge-hi: #FFF9ED;
|
|
14
|
+
--hcp-edge-lo: #D6C3A5;
|
|
15
|
+
--hcp-shadow: 0 1px 2px rgba(23, 28, 36, .06), 0 14px 34px -14px rgba(43, 36, 27, .32);
|
|
16
|
+
--hcp-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
17
|
+
--hcp-serif: Newsreader, Georgia, "Times New Roman", serif;
|
|
18
|
+
--hcp-mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;
|
|
19
|
+
--hcp-lift: 0px;
|
|
20
|
+
color-scheme: light;
|
|
21
|
+
}
|
|
22
|
+
@media (prefers-color-scheme: dark) {
|
|
23
|
+
.hcp:not([data-hcp-theme="light"]) {
|
|
24
|
+
--hcp-ground: #0B0C13; --hcp-surface: #11131E; --hcp-sunk: #1A1D2C;
|
|
25
|
+
--hcp-ink: #ECEAF2; --hcp-ink-2: #C9CDE0; --hcp-muted: #8A90AB;
|
|
26
|
+
--hcp-line: #262B42; --hcp-line-2: #353B52;
|
|
27
|
+
--hcp-brass: #D9A445; --hcp-brass-soft: #3A2F17;
|
|
28
|
+
--hcp-face: #22273E; --hcp-edge-hi: #3C4260; --hcp-edge-lo: #1B2033;
|
|
29
|
+
--hcp-shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 16px 38px -14px rgba(0, 0, 0, .7);
|
|
30
|
+
color-scheme: dark;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
.hcp[data-hcp-theme="dark"] {
|
|
34
|
+
--hcp-ground: #0B0C13; --hcp-surface: #11131E; --hcp-sunk: #1A1D2C;
|
|
35
|
+
--hcp-ink: #ECEAF2; --hcp-ink-2: #C9CDE0; --hcp-muted: #8A90AB;
|
|
36
|
+
--hcp-line: #262B42; --hcp-line-2: #353B52;
|
|
37
|
+
--hcp-brass: #D9A445; --hcp-brass-soft: #3A2F17;
|
|
38
|
+
--hcp-face: #22273E; --hcp-edge-hi: #3C4260; --hcp-edge-lo: #1B2033;
|
|
39
|
+
--hcp-shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 16px 38px -14px rgba(0, 0, 0, .7);
|
|
40
|
+
color-scheme: dark;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.hcp, .hcp * , .hcp *::before, .hcp *::after {
|
|
44
|
+
box-sizing: border-box; margin: 0; padding: 0; border: 0;
|
|
45
|
+
font: inherit; letter-spacing: normal; text-transform: none; text-shadow: none;
|
|
46
|
+
line-height: inherit; color: inherit; background: none; float: none; max-width: none;
|
|
47
|
+
}
|
|
48
|
+
.hcp {
|
|
49
|
+
position: fixed; right: 16px; bottom: calc(16px + var(--hcp-lift)); z-index: 2147482000;
|
|
50
|
+
display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
|
|
51
|
+
font-family: var(--hcp-sans); font-size: 14px; font-weight: 400; line-height: 1.5;
|
|
52
|
+
color: var(--hcp-ink); text-align: left;
|
|
53
|
+
-webkit-font-smoothing: antialiased;
|
|
54
|
+
transition: bottom .25s ease, opacity .2s ease;
|
|
55
|
+
pointer-events: none;
|
|
56
|
+
}
|
|
57
|
+
.hcp[hidden], .hcp[data-hcp-state="dismissed"], .hcp[data-hcp-state="off"], .hcp[data-hcp-intro] { display: none; }
|
|
58
|
+
.hcp[data-hcp-typing] { opacity: 0; visibility: hidden; }
|
|
59
|
+
.hcp > * { pointer-events: auto; }
|
|
60
|
+
.hcp svg { display: block; flex: none; }
|
|
61
|
+
|
|
62
|
+
.hcp-btn {
|
|
63
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 7px;
|
|
64
|
+
height: 34px; padding: 0 14px;
|
|
65
|
+
font-family: var(--hcp-sans); font-size: 14px; font-weight: 500; line-height: 1.5;
|
|
66
|
+
white-space: nowrap; text-decoration: none; cursor: pointer; user-select: none;
|
|
67
|
+
color: var(--hcp-ink); background: var(--hcp-face);
|
|
68
|
+
border: 2px solid; border-radius: 0;
|
|
69
|
+
border-color: var(--hcp-edge-hi) var(--hcp-edge-lo) var(--hcp-edge-lo) var(--hcp-edge-hi);
|
|
70
|
+
outline: none;
|
|
71
|
+
}
|
|
72
|
+
.hcp-btn:hover { background: color-mix(in srgb, var(--hcp-face), var(--hcp-edge-hi) 45%); color: var(--hcp-ink); }
|
|
73
|
+
.hcp-btn:active { border-color: var(--hcp-edge-lo) var(--hcp-edge-hi) var(--hcp-edge-hi) var(--hcp-edge-lo); }
|
|
74
|
+
.hcp-btn:active > span { translate: 1px 1px; }
|
|
75
|
+
.hcp-btn > span { display: inline-flex; align-items: center; gap: 7px; }
|
|
76
|
+
.hcp-btn:focus-visible, .hcp-x:focus-visible, .hcp-quiet:focus-visible { outline: 2px solid var(--hcp-brass); outline-offset: 2px; }
|
|
77
|
+
.hcp-btn--primary {
|
|
78
|
+
--hcp-face: var(--hcp-ink);
|
|
79
|
+
--hcp-edge-hi: color-mix(in srgb, var(--hcp-ink), white 24%);
|
|
80
|
+
--hcp-edge-lo: color-mix(in srgb, var(--hcp-ink), black 32%);
|
|
81
|
+
color: var(--hcp-ground);
|
|
82
|
+
}
|
|
83
|
+
.hcp-btn--primary:hover { background: color-mix(in srgb, var(--hcp-ink), var(--hcp-ground) 12%); color: var(--hcp-ground); }
|
|
84
|
+
|
|
85
|
+
.hcp-dock { display: flex; align-items: stretch; box-shadow: 0 6px 16px -8px rgba(43, 36, 27, .45); }
|
|
86
|
+
.hcp-tab { font-size: 13.5px; padding: 0 12px 0 10px; }
|
|
87
|
+
.hcp-tab svg { color: var(--hcp-brass); }
|
|
88
|
+
.hcp-dock .hcp-x {
|
|
89
|
+
width: 26px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
|
|
90
|
+
background: var(--hcp-face); color: var(--hcp-muted); cursor: pointer;
|
|
91
|
+
border: 2px solid; border-left: 0;
|
|
92
|
+
border-color: var(--hcp-edge-hi) var(--hcp-edge-lo) var(--hcp-edge-lo) var(--hcp-edge-hi);
|
|
93
|
+
}
|
|
94
|
+
.hcp-dock .hcp-x:hover { color: var(--hcp-ink); }
|
|
95
|
+
.hcp-tab__label { display: inline; }
|
|
96
|
+
.hcp[data-hcp-state="mini"] .hcp-tab__label,
|
|
97
|
+
.hcp[data-hcp-state="mini"] .hcp-dock .hcp-x { display: none; }
|
|
98
|
+
.hcp[data-hcp-state="mini"] .hcp-tab { width: 34px; padding: 0; }
|
|
99
|
+
.hcp[data-hcp-state="card"] .hcp-dock { display: none; }
|
|
100
|
+
|
|
101
|
+
.hcp-card {
|
|
102
|
+
display: none; position: relative;
|
|
103
|
+
width: min(360px, calc(100vw - 32px)); max-height: calc(100dvh - 32px - var(--hcp-lift)); overflow: auto;
|
|
104
|
+
background: var(--hcp-surface); border: 1px solid var(--hcp-line-2); border-top: 3px solid var(--hcp-brass);
|
|
105
|
+
box-shadow: var(--hcp-shadow);
|
|
106
|
+
padding: 18px 20px 16px;
|
|
107
|
+
}
|
|
108
|
+
.hcp[data-hcp-state="card"] .hcp-card { display: block; animation: hcp-rise .32s cubic-bezier(.2, .8, .3, 1) both; }
|
|
109
|
+
@keyframes hcp-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
|
|
110
|
+
|
|
111
|
+
.hcp-card .hcp-x {
|
|
112
|
+
position: absolute; top: 10px; right: 10px; width: 28px; height: 28px;
|
|
113
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
114
|
+
color: var(--hcp-muted); cursor: pointer;
|
|
115
|
+
}
|
|
116
|
+
.hcp-card .hcp-x:hover { color: var(--hcp-ink); background: var(--hcp-sunk); }
|
|
117
|
+
.hcp-kicker {
|
|
118
|
+
display: flex; align-items: center; gap: 8px;
|
|
119
|
+
font-family: var(--hcp-mono); font-size: 11px; font-weight: 500; letter-spacing: .08em; text-transform: uppercase;
|
|
120
|
+
color: var(--hcp-brass); margin-bottom: 8px;
|
|
121
|
+
}
|
|
122
|
+
.hcp-title {
|
|
123
|
+
font-family: var(--hcp-serif); font-size: 23px; font-weight: 500; line-height: 1.18;
|
|
124
|
+
color: var(--hcp-ink); padding-right: 26px; margin-bottom: 10px;
|
|
125
|
+
font-variation-settings: "opsz" 36;
|
|
126
|
+
}
|
|
127
|
+
.hcp-title [data-hcp-for] { display: none; }
|
|
128
|
+
.hcp[data-hcp-reason="use"] .hcp-title [data-hcp-for="use"],
|
|
129
|
+
.hcp[data-hcp-reason="open"] .hcp-title [data-hcp-for="open"],
|
|
130
|
+
.hcp[data-hcp-reason="return"] .hcp-title [data-hcp-for="return"],
|
|
131
|
+
.hcp[data-hcp-reason="error"] .hcp-title [data-hcp-for="error"] { display: inline; }
|
|
132
|
+
.hcp-body [data-hcp-for] { display: none; }
|
|
133
|
+
.hcp[data-hcp-reason="use"] .hcp-body [data-hcp-for="use"], .hcp[data-hcp-reason="open"] .hcp-body [data-hcp-for="open"], .hcp[data-hcp-reason="return"] .hcp-body [data-hcp-for="return"], .hcp[data-hcp-reason="error"] .hcp-body [data-hcp-for="error"] { display: inline; }
|
|
134
|
+
.hcp-body { font-size: 14px; line-height: 1.55; color: var(--hcp-ink-2); }
|
|
135
|
+
.hcp-file {
|
|
136
|
+
display: flex; align-items: center; gap: 10px; margin: 12px 0 14px;
|
|
137
|
+
padding: 9px 11px; background: var(--hcp-sunk); border: 1px solid var(--hcp-line);
|
|
138
|
+
}
|
|
139
|
+
.hcp-file svg { color: var(--hcp-brass); }
|
|
140
|
+
.hcp-file b { display: block; font-family: var(--hcp-sans); font-size: 14px; font-weight: 600; color: var(--hcp-ink); overflow-wrap: anywhere; }
|
|
141
|
+
.hcp-file small { display: block; font-size: 12px; color: var(--hcp-muted); line-height: 1.4; }
|
|
142
|
+
.hcp-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
|
|
143
|
+
.hcp-actions .hcp-btn--primary { flex: 1 1 auto; }
|
|
144
|
+
.hcp-foot { margin-top: 12px; display: flex; justify-content: space-between; align-items: center; gap: 10px; }
|
|
145
|
+
.hcp-foot small { font-size: 12px; color: var(--hcp-muted); }
|
|
146
|
+
.hcp-quiet {
|
|
147
|
+
font-size: 12px; color: var(--hcp-muted); cursor: pointer;
|
|
148
|
+
text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--hcp-line-2);
|
|
149
|
+
white-space: nowrap;
|
|
150
|
+
}
|
|
151
|
+
.hcp-quiet:hover { color: var(--hcp-ink); }
|
|
152
|
+
|
|
153
|
+
@media (max-width: 480px) {
|
|
154
|
+
.hcp { right: 12px; bottom: calc(12px + var(--hcp-lift)); }
|
|
155
|
+
.hcp-card { width: calc(100vw - 24px); padding: 16px 16px 14px; max-height: min(70dvh, calc(100dvh - 24px - var(--hcp-lift))); }
|
|
156
|
+
.hcp-title { font-size: 21px; }
|
|
157
|
+
}
|
|
158
|
+
@media (pointer: coarse) {
|
|
159
|
+
.hcp-btn, .hcp-dock .hcp-x, .hcp-card .hcp-x { min-height: 44px; }
|
|
160
|
+
.hcp-card .hcp-x { width: 44px; }
|
|
161
|
+
.hcp-quiet { display: inline-flex; align-items: center; min-height: 44px; }
|
|
162
|
+
}
|
|
163
|
+
@media (prefers-reduced-motion: reduce) {
|
|
164
|
+
.hcp { transition: none; }
|
|
165
|
+
.hcp[data-hcp-state="card"] .hcp-card { animation: none; }
|
|
166
|
+
.hcp-btn:active > span { translate: none; }
|
|
167
|
+
}
|
|
168
|
+
@media (forced-colors: active) {
|
|
169
|
+
.hcp-btn, .hcp-dock .hcp-x { border: 1px solid ButtonText; }
|
|
170
|
+
.hcp-card { border: 1px solid CanvasText; }
|
|
171
|
+
}
|
|
172
|
+
@media print { .hcp { display: none; } }
|
|
173
|
+
`;
|