PageSourceSearch

https://pressthink.org/j/rosen-archive/frontend/desktop/desktopWindowState.js?v=3.8.36

js pressthink.org collected 2026-10-02 04:26:56 UTC 8,172 bytes, 246 lines download raw bytes

1export const DESKTOP_LAYOUT_SCHEMA = 2;
2export const DESKTOP_LAYOUT_STORAGE_KEY = 'jrda-desktop-layout';
3
4const MAX_PERSISTED_WINDOWS = 12;
5const LEGACY_DESKTOP_LAYOUT_SCHEMA = 1;
6const MAX_POSITION_OFFSET = 4096;
7const CASCADE_STEP = 32;
8const CASCADE_COLUMNS = 4;
9
10// Some destinations are alternate presentations of one underlying surface.
11// Keeping each pair in one family prevents duplicate control/heading IDs and
12// mirrors the standard archive's existing layout and guided-path switches.
13export const desktopWindowFamily = (id) => {
14  if (id === 'archive' || id === 'folders') return 'archive';
15  if (id === 'start' || id === 'findings') return 'start';
16  return id;
17};
18
19export const emptyDesktopLayout = () => ({
20  schema: DESKTOP_LAYOUT_SCHEMA,
21  windows: [],
22  zOrder: [],
23});
24
25const allowedIdSet = (allowedIds) => new Set(
26  Array.isArray(allowedIds) ? allowedIds.filter((id) => typeof id === 'string') : [],
27);
28
29const cascadePosition = (index) => {
30  const safeIndex = Math.max(0, index);
31  return {
32    x: (safeIndex % CASCADE_COLUMNS) * CASCADE_STEP,
33    y: safeIndex * CASCADE_STEP,
34  };
35};
36
37const nextCascadePosition = (windows) => {
38  const occupied = new Set(
39    windows.map(({ position }) => `${position.x},${position.y}`),
40  );
41  for (let index = 0; index <= MAX_PERSISTED_WINDOWS; index += 1) {
42    const candidate = cascadePosition(index);
43    if (!occupied.has(`${candidate.x},${candidate.y}`)) return candidate;
44  }
45  return cascadePosition(windows.length);
46};
47
48const normalizedPosition = (position) => {
49  if (
50    !position
51    || typeof position.x !== 'number'
52    || typeof position.y !== 'number'
53    || !Number.isFinite(position.x)
54    || !Number.isFinite(position.y)
55    || Math.abs(position.x) > MAX_POSITION_OFFSET
56    || Math.abs(position.y) > MAX_POSITION_OFFSET
57  ) {
58    return null;
59  }
60  return { x: Math.round(position.x), y: Math.round(position.y) };
61};
62
63export function normalizeDesktopLayout(candidate, allowedIds) {
64  const allowed = allowedIdSet(allowedIds);
65  const supportedSchema = candidate?.schema === LEGACY_DESKTOP_LAYOUT_SCHEMA
66    || candidate?.schema === DESKTOP_LAYOUT_SCHEMA;
67  if (!supportedSchema || !Array.isArray(candidate.windows)) {
68    return emptyDesktopLayout();
69  }
70
71  const windowsByFamily = new Map();
72  for (const entry of candidate.windows.slice(0, MAX_PERSISTED_WINDOWS)) {
73    if (!entry || !allowed.has(entry.id)) continue;
74    windowsByFamily.set(desktopWindowFamily(entry.id), {
75      id: entry.id,
76      minimized: entry.minimized === true,
77      candidatePosition: candidate.schema === DESKTOP_LAYOUT_SCHEMA
78        ? normalizedPosition(entry.position)
79        : null,
80    });
81  }
82  const candidateWindows = [...windowsByFamily.values()];
83  const openIds = new Set(candidateWindows.map((entry) => entry.id));
84  const zOrder = [];
85  const seen = new Set();
86
87  if (Array.isArray(candidate.zOrder)) {
88    for (const id of candidate.zOrder) {
89      if (!openIds.has(id) || seen.has(id)) continue;
90      seen.add(id);
91      zOrder.push(id);
92    }
93  }
94  for (const { id } of candidateWindows) {
95    if (seen.has(id)) continue;
96    seen.add(id);
97    zOrder.push(id);
98  }
99
100  const cascadeIndexById = new Map(zOrder.map((id, index) => [id, index]));
101  const windows = candidateWindows.map(({ id, minimized, candidatePosition }) => ({
102    id,
103    minimized,
104    position: candidatePosition || cascadePosition(cascadeIndexById.get(id) || 0),
105  }));
106
107  return { schema: DESKTOP_LAYOUT_SCHEMA, windows, zOrder };
108}
109
110export function parseDesktopLayout(rawValue, allowedIds) {
111  if (typeof rawValue !== 'string' || rawValue === '') return emptyDesktopLayout();
112  try {
113    return normalizeDesktopLayout(JSON.parse(rawValue), allowedIds);
114  } catch {
115    return emptyDesktopLayout();
116  }
117}
118
119export const serializeDesktopLayout = (layout, allowedIds) => (
120  JSON.stringify(normalizeDesktopLayout(layout, allowedIds))
121);
122
123export function activateDesktopWindow(layout, id, allowedIds) {
124  const allowed = allowedIdSet(allowedIds);
125  if (!allowed.has(id)) return normalizeDesktopLayout(layout, allowedIds);
126
127  const current = normalizeDesktopLayout(layout, allowedIds);
128  const family = desktopWindowFamily(id);
129  const familyIndex = current.windows.findIndex(
130    (entry) => desktopWindowFamily(entry.id) === family,
131  );
132  const windows = familyIndex >= 0
133    ? current.windows.map((entry, index) => (
134      index === familyIndex ? { ...entry, id, minimized: false } : entry
135    ))
136    : current.windows.concat({
137      id,
138      minimized: false,
139      position: nextCascadePosition(current.windows),
140    });
141  const retainedIds = new Set(windows.map((entry) => entry.id));
142  const zOrder = current.zOrder
143    .filter((entryId) => retainedIds.has(entryId) && desktopWindowFamily(entryId) !== family)
144    .concat(id);
145
146  return { schema: DESKTOP_LAYOUT_SCHEMA, windows, zOrder };
147}
148
149export function moveDesktopWindow(layout, id, position, allowedIds) {
150  const allowed = allowedIdSet(allowedIds);
151  const nextPosition = normalizedPosition(position);
152  const current = normalizeDesktopLayout(layout, allowedIds);
153  if (!allowed.has(id) || !nextPosition || !current.windows.some((entry) => entry.id === id)) {
154    return current;
155  }
156  return {
157    ...current,
158    windows: current.windows.map((entry) => (
159      entry.id === id ? { ...entry, position: nextPosition } : entry
160    )),
161  };
162}
163
164const finiteRect = (rect, fields) => (
165  rect && fields.every((field) =>
165 typeof rect[field] === 'number' && Number.isFinite(rect[field]))
166);
167
168export function clampDesktopWindowPosition(position, geometry = {}) {
169  const candidate = normalizedPosition(position);
170  const currentPosition = normalizedPosition(geometry.currentPosition);
171  const {
172    frameRect,
173    titlebarRect,
174    workArea,
175    margin = 8,
176    minimumVisibleHeight = 128,
177  } = geometry;
178  if (
179    !candidate
180    || !currentPosition
181    || !finiteRect(frameRect, ['left', 'right', 'top', 'bottom'])
182    || !finiteRect(titlebarRect, ['left', 'right', 'top', 'bottom'])
183    || !finiteRect(workArea, ['left', 'right', 'top', 'bottom'])
184    || !Number.isFinite(margin)
185    || !Number.isFinite(minimumVisibleHeight)
186  ) {
187    return currentPosition || { x: 0, y: 0 };
188  }
189
190  let minX = currentPosition.x + workArea.left + margin - titlebarRect.left;
191  let maxX = currentPosition.x + workArea.right - margin - titlebarRect.right;
192  if (minX > maxX) {
193    const centered = currentPosition.x
194      + ((workArea.left + workArea.right) / 2)
195      - ((titlebarRect.left + titlebarRect.right) / 2);
196    minX = centered;
197    maxX = centered;
198  }
199
200  const visibleHeight = Math.min(
201    Math.max(titlebarRect.bottom - frameRect.top, minimumVisibleHeight),
202    frameRect.bottom - frameRect.top,
203  );
204  const minY = currentPosition.y + workArea.top + margin - titlebarRect.top;
205  let maxY = currentPosition.y
206    + workArea.bottom
207    - margin
208    - visibleHeight
209    - frameRect.top;
210  // When extreme zoom or browser chrome leaves less room than the requested
211  // visible body height, the title bar is the non-negotiable recovery handle.
212  if (minY > maxY) maxY = minY;
213
214  return {
215    x: Math.round(Math.min(maxX, Math.max(minX, candidate.x))),
216    y: Math.round(Math.min(maxY, Math.max(minY, candidate.y))),
217  };
218}
219
220export function minimizeDesktopWindow(layout, id, allowedIds) {
221  const current = normalizeDesktopLayout(layout, allowedIds);
222  return {
223    ...current,
224    windows: current.windows.map((entry) => (
225      entry.id === id ? { ...entry, minimized: true } : entry
226    )),
227  };
228}
229
230export function closeDesktopWindow(layout, id, allowedIds) {
231  const current = normalizeDesktopLayout(layout, allowedIds);
232  return {
233    schema: DESKTOP_LAYOUT_SCHEMA,
234    windows: current.windows.filter((entry) => entry.id !== id),
235    zOrder: current.zOrder.filter((entryId) => entryId !== id),
236  };
237}
238
239export function nextVisibleDesktopWindow(layout, excludedId = null) {
240  const minimizedById = new Map(layout.windows.map((entry) => [entry.id, entry.minimized]));
241  for (let index = layout.zOrder.length - 1; index >= 0; index -= 1) {
242    const id = layout.zOrder[index];
243    if (id !== excludedId && minimizedById.get(id) === false) return id;
244  }
245  return null;
246}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.