Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions public/js/admin-app-form.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { clr as rc, el, inp as inpById, q as qSel, qa, qi, tgt } from '/js/utils.js?v=d949e985';
import { clr as rc, el, inp as inpById, q as qSel, qa, qi, tgt } from '/js/utils.js?v=4c1189b9';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { loadLocalIcons, resolveIcon, iconChain, cdnIconName } from '/js/icons.js?v=69c2b9bd';
import { state } from '/js/admin-state.js?v=7d68e98e';
Expand All @@ -19,9 +19,9 @@ import {
initInlineEdit,
setTogDisabled,
wireChecklist,
} from '/js/admin-shared.js?v=132c869f';
} from '/js/admin-shared.js?v=d7dd8b71';
import { MAX_LABELS } from '/js/badge-logic.js?v=b3c8b6c2';
import { renderColorControl, BADGE_SWATCHES, BADGE_DEFAULT } from '/js/admin-color-control.js?v=3a61c02f';
import { renderColorControl, BADGE_SWATCHES, BADGE_DEFAULT } from '/js/admin-color-control.js?v=f638b750';
import { badgeErrorAdvice, TONE } from '/js/admin-error.js?v=10f3cdb1';

export function buildFolderForm(body, item) {
Expand Down
4 changes: 2 additions & 2 deletions public/js/admin-auth.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ag, ap } from '/js/admin-shared.js?v=132c869f';
import { ag, ap } from '/js/admin-shared.js?v=d7dd8b71';
import { t } from '/js/i18n.js?v=e644a5c5';
import { pwStrength } from '/js/password-strength.js?v=42f45ac7';
import { el, inp as inpById, qa } from '/js/utils.js?v=d949e985';
import { el, inp as inpById, qa } from '/js/utils.js?v=4c1189b9';

export async function checkAuth(onLogin) {
try {
Expand Down
4 changes: 2 additions & 2 deletions public/js/admin-color-control.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PE_SVG, initInlineEdit, toast } from '/js/admin-shared.js?v=132c869f';
import { PE_SVG, initInlineEdit, toast } from '/js/admin-shared.js?v=d7dd8b71';
import { t } from '/js/i18n.js?v=e644a5c5';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { qa, q } from '/js/utils.js?v=d949e985';
import { qa, q } from '/js/utils.js?v=4c1189b9';

const CC_SWATCHES = ['#1c1c1e', '#8e8e93', '#f2f2f7', '#ff393c', '#ffcd00', '#35c759', '#0289ff', '#cb30df'];
export const BADGE_DEFAULT = '#1e6ef4';
Expand Down
4 changes: 2 additions & 2 deletions public/js/admin-drag.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

import { applyDrop, canJoinFolder, folderRowZone } from '/js/admin-drag-logic.js?v=6b767e76';
import { state } from '/js/admin-state.js?v=7d68e98e';
import { snapshotItems } from '/js/admin-save-logic.js?v=48a9e055';
import { qa } from '/js/utils.js?v=d949e985';
import { snapshotItems } from '/js/admin-save-logic.js?v=4f71ef6c';
import { qa } from '/js/utils.js?v=4c1189b9';

/** @type {(before: unknown) => void} */
let _save = () => {};
Expand Down
10 changes: 5 additions & 5 deletions public/js/admin-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
reads and writes them too. */

import { collapsedFolders, filter, state } from '/js/admin-state.js?v=7d68e98e';
import { snapshotItems } from '/js/admin-save-logic.js?v=48a9e055';
import { snapshotItems } from '/js/admin-save-logic.js?v=4f71ef6c';
import { reorderItems } from '/js/admin-logic.js?v=dcf7c37d';
import { initDrag, wireRowDrag } from '/js/admin-drag.js?v=f9adcc10';
import { paintIcon } from '/js/admin-shared.js?v=132c869f';
import { clr as rc, el, qa, setUserText } from '/js/utils.js?v=d949e985';
import { initDrag, wireRowDrag } from '/js/admin-drag.js?v=971d097b';
import { paintIcon } from '/js/admin-shared.js?v=d7dd8b71';
import { clr as rc, el, qa, setUserText } from '/js/utils.js?v=4c1189b9';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { t } from '/js/i18n.js?v=e644a5c5';
import { sizeLabel } from '/js/admin-widget-form.js?v=0ca20e21';
import { sizeLabel } from '/js/admin-widget-form.js?v=cc1dfdec';
import { widgetGlyph } from '/js/widget-glyphs.js?v=12b0a947';

/** @type {{ openModal: (idx: number|null) => void,
Expand Down
2 changes: 1 addition & 1 deletion public/js/admin-save-logic.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function newItemId(label, fallback = 'item', taken = []) {

/** @param {any[]} items @returns {any[]} */
export function snapshotItems(items) {
return Array.isArray(items) ? JSON.parse(JSON.stringify(items)) : [];
return Array.isArray(items) ? structuredClone(items) : [];
}

/** Run `write` and undo the local change when it did not reach the server.
Expand Down
4 changes: 2 additions & 2 deletions public/js/admin-settings.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { toast, ag, ap } from '/js/admin-shared.js?v=132c869f';
import { toast, ag, ap } from '/js/admin-shared.js?v=d7dd8b71';
import { pwStrength } from '/js/password-strength.js?v=42f45ac7';
import { t } from '/js/i18n.js?v=e644a5c5';
import { shouldWritePassword, settingsSaveBlocker, clearsStoredPassword, BLOCK } from '/js/admin-logic.js?v=dcf7c37d';
import { confirmText } from '/js/modal.js?v=11fa1eff';
import { el, inp, q, qa, setUserText } from '/js/utils.js?v=d949e985';
import { el, inp, q, qa, setUserText } from '/js/utils.js?v=4c1189b9';

/* Mirrors the server's rule: auth cannot be switched on with no password. */
let _passwordSet = false;
Expand Down
2 changes: 1 addition & 1 deletion public/js/admin-shared.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* Stateless helpers shared by the admin modules. Mutable state stays out. */
import { nextActiveIndex, recoversSession, toastHoldMs } from '/js/admin-logic.js?v=dcf7c37d';
import { el, qa, q } from '/js/utils.js?v=d949e985';
import { el, qa, q } from '/js/utils.js?v=4c1189b9';
import { t } from '/js/i18n.js?v=e644a5c5';
import { iconChain } from '/js/icons.js?v=69c2b9bd';

Expand Down
6 changes: 3 additions & 3 deletions public/js/admin-widget-form.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { state } from '/js/admin-state.js?v=7d68e98e';
import { PE_SVG, CHEV_SVG, initInlineEdit } from '/js/admin-shared.js?v=132c869f';
import { renderWidgetConfigForm } from '/js/widget-config-form.js?v=0f95ec4c';
import { PE_SVG, CHEV_SVG, initInlineEdit } from '/js/admin-shared.js?v=d7dd8b71';
import { renderWidgetConfigForm } from '/js/widget-config-form.js?v=02918336';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { sizesForView, widgetConfigMode, rejectionLines, carriesTypedValues } from '/js/admin-logic.js?v=dcf7c37d';
import { t } from '/js/i18n.js?v=e644a5c5';
import { q, qi, qa } from '/js/utils.js?v=d949e985';
import { q, qi, qa } from '/js/utils.js?v=4c1189b9';

const SIZE_ICONS = {
small:
Expand Down
24 changes: 12 additions & 12 deletions public/js/admin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { buildAppForm, buildFolderForm, captureActLabels, serializeKvRows } from '/js/admin-app-form.js?v=5702feef';
import { checkAuth, requireLogin, wirePasswordStrength } from '/js/admin-auth.js?v=9fc96f28';
import { initList, render, syncFilterUI } from '/js/admin-list.js?v=0a1e50b5';
import { buildAppForm, buildFolderForm, captureActLabels, serializeKvRows } from '/js/admin-app-form.js?v=4a5478d6';
import { checkAuth, requireLogin, wirePasswordStrength } from '/js/admin-auth.js?v=681cfd29';
import { initList, render, syncFilterUI } from '/js/admin-list.js?v=8524801f';
import { resolveAdminSection } from '/js/admin-logic.js?v=dcf7c37d';
import {
buildAppItem,
Expand All @@ -9,11 +9,11 @@ import {
saveWithRevert,
snapshotItems,
upsertItem,
} from '/js/admin-save-logic.js?v=48a9e055';
import { loadSettings, showBgFields, showBgFit, showWallpaperFile } from '/js/admin-settings.js?v=f019211c';
import { ag, ap, initInlineEdit, paintIcon, setReauthHandler, toast } from '/js/admin-shared.js?v=132c869f';
} from '/js/admin-save-logic.js?v=4f71ef6c';
import { loadSettings, showBgFields, showBgFit, showWallpaperFile } from '/js/admin-settings.js?v=909c58ad';
import { ag, ap, initInlineEdit, paintIcon, setReauthHandler, toast } from '/js/admin-shared.js?v=d7dd8b71';
import { collapsedFolders, filter, state } from '/js/admin-state.js?v=7d68e98e';
import { buildWidgetForm } from '/js/admin-widget-form.js?v=0ca20e21';
import { buildWidgetForm } from '/js/admin-widget-form.js?v=cc1dfdec';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { initI18n, LANGUAGES, t } from '/js/i18n.js?v=e644a5c5';
import { loadLocalIcons } from '/js/icons.js?v=69c2b9bd';
Expand All @@ -25,13 +25,13 @@ import {
NOTE,
parseErrorsAsSkipped,
SKIP,
} from '/js/import-foreign.js?v=ef4f3d44';
} from '/js/import-foreign.js?v=722d1c25';
import { isMobileLayout, onLayoutChange } from '/js/layout.js?v=9de1cb7d';
import { confirmModal, confirmText, openModal as openDialog, promptModal } from '/js/modal.js?v=11fa1eff';
import { readMode, watchSystemTheme, writeMode } from '/js/theme.js?v=00c011c9';
import { el, inp, q, qa, clr as rc, sanitizeCssUrl, setUserText, tgt } from '/js/utils.js?v=d949e985';
import { normalizeColorInput } from '/js/admin-color-control.js?v=3a61c02f';
import { parseYamlTolerant, YamlLiteError } from '/js/yaml-lite.js?v=1907cce7';
import { el, inp, q, qa, clr as rc, sanitizeCssUrl, setUserText, tgt } from '/js/utils.js?v=4c1189b9';
import { normalizeColorInput } from '/js/admin-color-control.js?v=f638b750';
import { parseYamlTolerant, YamlLiteError } from '/js/yaml-lite.js?v=6ebb564c';
import { loadWallpaper, saveWallpaper } from '/js/wallpaper-cache.js?v=c5f8a3e6';

/* A class rather than a bare media query. Some phones report a wider CSS
Expand Down Expand Up @@ -238,7 +238,7 @@ function _renderEditBody() {
function openModal(idx) {
const editing = idx != null ? state.items[idx] : null;
state.eid = editing?.id ?? null;
const item = editing ? JSON.parse(JSON.stringify(editing)) : null;
const item = editing ? structuredClone(editing) : null;
state.ctype = item?.type || 'app';
state.siurl = item?.iconUrl || '';
state.scol = item?.color || 'dark';
Expand Down
6 changes: 6 additions & 0 deletions public/js/brand-mark.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 17 additions & 6 deletions public/js/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,26 @@ import {
} from '/js/widget-types.js?v=a1b61636';
import {
el,
isDashboardEmpty,
mk,
mkWrap as _mkWrap,
mountScaledWidget,
pageDir,
renderEmptyState,
q,
qa,
qi,
sanitizeCssUrl,
setUserText,
teardownWidgets,
titleWhenTruncated,
} from '/js/utils.js?v=d949e985';
import { initSpotlight } from '/js/spotlight.js?v=687fae26';
} from '/js/utils.js?v=4c1189b9';
import { initSpotlight } from '/js/spotlight.js?v=c8d5d2d8';
import { html, setHtml, raw } from '/js/html.js?v=c71f8903';
import { initI18n, t, currentLang } from '/js/i18n.js?v=e644a5c5';
import { pwStrength, passwordMismatch } from '/js/password-strength.js?v=42f45ac7';
import { sanitizeItemLinks } from '/js/link-url.js?v=54adb40f';
import { initUI, mkFolder, openFolderDesktop, openFolderMobile, buildMobile } from '/js/ui.js?v=82e0bf6b';
import { initUI, mkFolder, openFolderDesktop, openFolderMobile, buildMobile } from '/js/ui.js?v=b71e1c75';
import { badgeMinimum, badgeSignature, computeBadgeVisual, readBadgeUpdate } from '/js/badge-logic.js?v=b3c8b6c2';
import { formatNumber } from '/js/format-number.js?v=4a5ccef4';
import { closeBadgePopover, wireBadgePopover } from '/js/badge-popover.js?v=aa52b1a3';
Expand Down Expand Up @@ -81,10 +83,14 @@ function gridMetrics() {
}
let gm = { tile: DESIGN_TILE, rowGap: DESIGN_ROW_GAP, scale: 1 };

function desktopSlots() {
/* The reserves mirror the .page padding in dashboard.css. Change one and the
page breaks stop matching the box.

@param {boolean} hasDock */
function desktopSlots(hasDock) {
const ih = innerHeight;
const top = Math.min(70, Math.max(44, ih * 0.04));
const bottom = Math.min(160, Math.max(110, ih * 0.1));
const bottom = hasDock ? Math.min(160, Math.max(110, ih * 0.1)) : top;
const rows = Math.max(1, Math.min(4, Math.floor((ih - top - bottom + gm.rowGap) / (gm.tile + gm.rowGap))));
return DCOLS * rows;
}
Expand Down Expand Up @@ -265,11 +271,13 @@ function paginate() {
.flatMap(f => f.children || [])
.map(String),
);
const budget = desktopSlots();
const budget = desktopSlots(items.some(i => i.type === 'app' && i.dock && !i.hidden));
const bare = isDashboardEmpty(items);
const pages = [];
let cur = [],
used = 0;
for (const item of items) {
if (bare && item.system) continue;
if (item.dock) continue;
if (item.hidden) continue;
if (inFolder.has(String(item.id))) continue;
Expand Down Expand Up @@ -379,6 +387,7 @@ function buildDesktop() {
/* Before paginate() and before any tile is built: both size against it. */
gm = gridMetrics();
const dock = items.filter(i => i.type === 'app' && i.dock && !i.hidden).slice(0, 4);
document.body.classList.toggle('no-dock', !dock.length);
const pages = paginate();
totalPages = pages.length;
const strip = el('pages');
Expand All @@ -392,6 +401,7 @@ function buildDesktop() {
p.appendChild(g);
strip.appendChild(p);
});
renderEmptyState(items);
const dots = el('dots');
dots.style.cssText = '';
dots.replaceChildren();
Expand All @@ -400,6 +410,7 @@ function buildDesktop() {
const dk = el('dock');
dk.replaceChildren();
dock.forEach(item => dk.appendChild(mkDock(item)));
dk.hidden = !dock.length;
}

/* Every page is mounted at once, so widgets the user has swiped away from keep
Expand Down
2 changes: 1 addition & 1 deletion public/js/import-foreign.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
only: a widget in the source becomes a plain app tile, never a Stackyard
widget. Keep this module free of the DOM and of the network. */

import { buildAppItem, newItemId } from '/js/admin-save-logic.js?v=48a9e055';
import { buildAppItem, newItemId } from '/js/admin-save-logic.js?v=4f71ef6c';
import { isSafeLinkUrl } from '/js/link-url.js?v=54adb40f';

export const SKIP = Object.freeze({
Expand Down
5 changes: 3 additions & 2 deletions public/js/mobile-metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ export function gridCellCount({ gridW, gridH, sc }) {
return { cols, rows };
}

export function mobileMetrics(vw) {
/** @param {number} vw @param {boolean} docked */
export function mobileMetrics(vw, docked = true) {
const sc = Math.min(vw / BASE_VW, MAX_SC);
const sm = Math.round(18 * sc);
const dh = Math.round(108 * sc);
const dh = docked ? Math.round(108 * sc) : 0;
const pillH = Math.round(34 * sc);
const pillGap = Math.round(10 * sc);
/* Reserving less lets the pill sit on top of the last row. */
Expand Down
2 changes: 1 addition & 1 deletion public/js/spotlight.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mk, clr, el, inp as inpById, q, qa, setUserText } from '/js/utils.js?v=d949e985';
import { mk, clr, el, inp as inpById, q, qa, setUserText } from '/js/utils.js?v=4c1189b9';
import { t } from '/js/i18n.js?v=e644a5c5';

/* Attached to the window so a re-open can undo the previous one. */
Expand Down
22 changes: 14 additions & 8 deletions public/js/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { widgetSrc, cardPreset, uniqueTitle, WIDGET_DESIGN } from '/js/widget-ty
import {
mk,
clr,
isDashboardEmpty,
renderEmptyState,
mkWrap as _mkWrap,
mountScaledWidget,
pageDir,
Expand All @@ -11,10 +13,10 @@ import {
q,
qa,
setUserText,
} from '/js/utils.js?v=d949e985';
} from '/js/utils.js?v=4c1189b9';
import { t, currentLang } from '/js/i18n.js?v=e644a5c5';
import { toneForColor } from '/js/label-contrast.js?v=c1ac6fb8';
import { mobileMetrics, gridColumnWidth, gridCellCount } from '/js/mobile-metrics.js?v=7be08bb0';
import { mobileMetrics, gridColumnWidth, gridCellCount } from '/js/mobile-metrics.js?v=ab5fe77e';

let _state = null;
export function initUI(state) {
Expand Down Expand Up @@ -547,7 +549,11 @@ export function buildMobile() {
st().BEL.clear();
const vw = innerWidth,
vh = innerHeight;
const { sc, sm, dh, pillH, pillGap, dz } = mobileMetrics(vw);
const dock = items()
.filter(i => i.type === 'app' && i.dock && !i.hidden)
.slice(0, 4);
const { sc, sm, dh, pillH, pillGap, dz } = mobileMetrics(vw, dock.length > 0);
document.body.classList.toggle('no-dock', !dock.length);
const gap = Math.round(sm * 0.5);
css(document.body, {
'--sc': String(sc),
Expand Down Expand Up @@ -610,9 +616,6 @@ export function buildMobile() {
const isz = Math.round(Math.min(cw * 0.9, rh * 0.8, maxIsz));
const ir = Math.round(isz * 0.225),
im = Math.round(isz * 0.64);
const dock = items()
.filter(i => i.type === 'app' && i.dock && !i.hidden)
.slice(0, 4);
const showLabel = S().showLabels?.ios === true;
/* The reference draws a 66 icon and a 28 widget corner, so a widget's corner
is 0.424 of the icon it sits beside. */
Expand All @@ -626,7 +629,8 @@ export function buildMobile() {
.flatMap(f => f.children || [])
.map(String),
);
const gridItems = items().filter(i => !i.dock && !i.hidden && !inFolder.has(String(i.id)));
const bare = isDashboardEmpty(items());
const gridItems = items().filter(i => !i.dock && !i.hidden && !inFolder.has(String(i.id)) && !(bare && i.system));

function packMobile(list) {
const pages = [];
Expand Down Expand Up @@ -743,6 +747,7 @@ export function buildMobile() {
/* Nothing was placed, so the measuring page would show as a blank first
page. */
if (!pages.length) firstPage.page.remove();
renderEmptyState(items());

const dw = el('dots');
dw.style.cssText = 'display:none';
Expand All @@ -763,6 +768,7 @@ export function buildMobile() {
? dock.length * dockIconSz + (dock.length - 1) * Math.round(22 * sc) + dockPad * 2
: maxDockW;
const dockW = Math.min(maxDockW, dockContentW);
dk.hidden = !dock.length;
dk.style.cssText = `position:fixed;left:50%;bottom:${dockGap}px;transform:translateX(-50%);width:${dockW}px;height:${dh}px;padding:0 ${dockPad}px;border-radius:${Math.round(44 * sc)}px;z-index:400;`;
dk.replaceChildren();
dock.forEach(item => {
Expand All @@ -782,7 +788,7 @@ export function buildMobile() {
_pdotPad = Math.round(14 * sc);
const pillDotsW = pages.length * (_pdotSz + _pdotGap) - _pdotGap + _pdotPad * 2;
const pill = el('mob-search-pill');
pill.style.cssText = `position:fixed;left:50%;bottom:${dockGap + dh + pillGap}px;transform:translateX(-50%);width:${pillSearchW}px;height:${pillH}px;display:flex;z-index:500;`;
pill.style.cssText = `position:fixed;left:50%;bottom:${dockGap + dh + pillGap}px;transform:translateX(-50%);width:${pillSearchW}px;height:${pillH}px;display:${bare ? 'none' : 'flex'};z-index:500;`;

const pillNew = /** @type {HTMLElement} */ (pill.cloneNode(true));
const pillNewDots = q('.msp-dots', pillNew);
Expand Down
Loading