@open-press/core 1.1.1 → 1.1.2

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.
@@ -17,7 +17,7 @@ export async function run({ root, options }) {
17
17
  }
18
18
  const host = options.host ?? "127.0.0.1";
19
19
  const port = options.port ?? "5173";
20
- const url = `http://${host}:${port}/?dev=1`;
20
+ const url = `http://${host}:${port}/workspace`;
21
21
  if (options.dryRun) {
22
22
  console.log(`OpenPress dev URL: ${url}`);
23
23
  if (!options.noBuild) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-press/core",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "type": "module",
5
5
  "description": "open-press core — runtime primitives, CLI, and render pipeline for AI-first fixed-layout documents.",
6
6
  "license": "MIT",
@@ -218,20 +218,33 @@ export function OpenPressApp() {
218
218
 
219
219
  function currentSlugFromLocation(): string {
220
220
  if (typeof window === "undefined") return "";
221
- return normalizeSlug(window.location.pathname);
221
+ return slugFromWorkspacePathname(window.location.pathname);
222
222
  }
223
223
 
224
224
  function normalizeSlug(raw: string): string {
225
225
  return raw.replace(/^\/+|\/+$/g, "");
226
226
  }
227
227
 
228
+ function slugFromWorkspacePathname(pathname: string): string {
229
+ const normalized = normalizeSlug(pathname);
230
+ if (!normalized || normalized === "workspace") return "";
231
+
232
+ const segments = normalized.split("/").filter(Boolean);
233
+ if (segments.length === 2 && segments[1] === "preview") {
234
+ return segments[0] ?? "";
235
+ }
236
+
237
+ // Legacy static/public route compatibility. New workspace navigation
238
+ // writes /workspace and /<press-slug>/preview.
239
+ return normalized;
240
+ }
241
+
228
242
  function pushSlug(slug: string) {
229
243
  if (typeof window === "undefined") return;
230
- // Preserve the current query string (e.g. ?dev=1 keeps the workbench
231
- // chrome alive across gallery navigation). Drop the hash — it's a
232
- // page anchor that means nothing in a different document.
233
- const pathname = slug ? `/${normalizeSlug(slug)}` : "/";
234
- const target = `${pathname}${window.location.search}`;
244
+ // Drop query + hash: workbench routing is path-based, and page anchors
245
+ // do not transfer across documents.
246
+ const pathname = slug ? `/${normalizeSlug(slug)}/preview` : "/workspace";
247
+ const target = pathname;
235
248
  if (window.location.pathname === pathname) return;
236
249
  window.history.pushState({}, "", target);
237
250
  }
@@ -69,6 +69,7 @@ function ThumbnailCard({
69
69
  aspectRatio: string;
70
70
  }) {
71
71
  const surfaceRef = useRef<HTMLDivElement>(null);
72
+ const cardRef = useRef<HTMLDivElement>(null);
72
73
  const [scale, setScale] = useState<number | null>(null);
73
74
 
74
75
  useEffect(() => {
@@ -86,6 +87,11 @@ function ThumbnailCard({
86
87
  return () => ro.disconnect();
87
88
  }, [pageWidthPx, pageHeightPx]);
88
89
 
90
+ useEffect(() => {
91
+ if (!active) return;
92
+ cardRef.current?.scrollIntoView({ block: "nearest" });
93
+ }, [active]);
94
+
89
95
  const className = `openpress-thumb-card${active ? " is-active" : ""}`;
90
96
  // Wrap the page HTML using the same class structure as the main
91
97
  // reader (`.openpress-html-page > .openpress-html-page__html`) so
@@ -117,6 +123,7 @@ function ThumbnailCard({
117
123
 
118
124
  return (
119
125
  <div
126
+ ref={cardRef}
120
127
  role="button"
121
128
  tabIndex={0}
122
129
  className={className}
@@ -2,10 +2,18 @@ export function isLocalWorkspaceHost(hostname: string) {
2
2
  return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
3
3
  }
4
4
 
5
- export function isWorkspaceModeLocation(location: Pick<Location, "hostname" | "search">) {
6
- return isLocalWorkspaceHost(location.hostname) && new URLSearchParams(location.search).has("dev");
5
+ export function isWorkspaceModeLocation(location: Pick<Location, "hostname" | "pathname">) {
6
+ if (!isLocalWorkspaceHost(location.hostname)) return false;
7
+ const pathname = normalizePathname(location.pathname);
8
+ if (pathname === "workspace") return true;
9
+ const segments = pathname.split("/").filter(Boolean);
10
+ return segments.length === 2 && segments[1] === "preview";
7
11
  }
8
12
 
9
13
  export function isPrintModeLocation(location: Pick<Location, "search">) {
10
14
  return new URLSearchParams(location.search).has("print");
11
15
  }
16
+
17
+ function normalizePathname(pathname: string) {
18
+ return pathname.replace(/^\/+|\/+$/g, "");
19
+ }
@@ -597,24 +597,36 @@
597
597
  (canvas-style Press: social posts, slides). Renders the actual page
598
598
  HTML scaled down so the user can navigate by visual recognition. */
599
599
  .openpress-reader-app .openpress-panel-section--thumbnails {
600
- display: flex;
601
- flex-direction: column;
602
- gap: 10px;
603
- padding: 8px 14px 16px;
600
+ display: grid;
601
+ grid-template-rows: minmax(0, 1fr);
604
602
  min-height: 0;
603
+ overflow: hidden;
604
+ padding: 8px 14px 12px;
605
605
  }
606
606
 
607
607
  .openpress-reader-app .openpress-thumb-list {
608
608
  display: flex;
609
+ min-height: 0;
609
610
  flex-direction: column;
610
611
  gap: 10px;
611
612
  margin: 0;
612
- padding: 0;
613
+ overflow: auto;
614
+ padding: 0 0 10px;
613
615
  list-style: none;
616
+ overscroll-behavior: contain;
617
+ scrollbar-width: none;
618
+ }
619
+
620
+ .openpress-reader-app .openpress-thumb-list::-webkit-scrollbar {
621
+ width: 0;
622
+ height: 0;
623
+ display: none;
614
624
  }
615
625
 
616
626
  .openpress-reader-app .openpress-thumb-card {
617
627
  display: grid;
628
+ width: 100%;
629
+ min-width: 0;
618
630
  grid-template-columns: 20px minmax(0, 1fr);
619
631
  align-items: stretch;
620
632
  gap: 6px;