@georeferencing/react 0.1.0 → 0.2.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.
@@ -11,7 +11,13 @@ export function ImagePanel({
11
11
  /** Shared authoritative editor controller; the panel does not own its lifecycle. */
12
12
  controller,
13
13
  /** Optional translation function; defaults to returning English messages unchanged. */
14
- t = identity, }) {
14
+ t = identity,
15
+ /** Show previous/next view history and linked image/map navigation. */
16
+ navigation = true,
17
+ /** Show brightness, contrast and histogram display adjustment. */
18
+ displayAdjustment = true,
19
+ /** Extra actions inside the empty drop zone, such as a sample image button. */
20
+ emptyActions, }) {
15
21
  const s = useGeoreferencer(controller), image = s.document.sourceImage, svg = useRef(null);
16
22
  const view = s.imageView ?? [
17
23
  0, 0, 100, 100,
@@ -113,31 +119,55 @@ t = identity, }) {
113
119
  const p = new DOMPoint(e.clientX, e.clientY).matrixTransform(matrix.inverse());
114
120
  return [p.x, p.y];
115
121
  };
116
- const zoom = (factor, center = [view[0] + view[2] / 2, view[1] + view[3] / 2]) => setView((v) => [
117
- center[0] + (v[0] - center[0]) * factor,
118
- center[1] + (v[1] - center[1]) * factor,
119
- Math.max(1, v[2] * factor),
120
- Math.max(1, v[3] * factor),
121
- ]);
122
+ // Read the latest view from the controller so rapid wheel events never use a stale render.
123
+ const zoom = (factor, center) => {
124
+ const v = controller.getSnapshot().imageView ?? view;
125
+ const c = center ?? [v[0] + v[2] / 2, v[1] + v[3] / 2];
126
+ controller.setImageView([
127
+ c[0] + (v[0] - c[0]) * factor,
128
+ c[1] + (v[1] - c[1]) * factor,
129
+ Math.max(1, v[2] * factor),
130
+ Math.max(1, v[3] * factor),
131
+ ]);
132
+ };
133
+ const zoomRef = useRef(zoom);
134
+ zoomRef.current = zoom;
135
+ const hasImage = Boolean(image);
136
+ // React registers wheel listeners as passive; a native one can stop the page scrolling.
137
+ useEffect(() => {
138
+ const element = svg.current;
139
+ if (!hasImage || !element)
140
+ return;
141
+ const wheel = (e) => {
142
+ e.preventDefault();
143
+ const matrix = element.getScreenCTM();
144
+ const center = matrix
145
+ ? new DOMPoint(e.clientX, e.clientY).matrixTransform(matrix.inverse())
146
+ : undefined;
147
+ zoomRef.current(e.deltaY > 0 ? 1.15 : 1 / 1.15, center ? [center.x, center.y] : undefined);
148
+ };
149
+ element.addEventListener("wheel", wheel, { passive: false });
150
+ return () => element.removeEventListener("wheel", wheel);
151
+ }, [hasImage]);
122
152
  const down = (e) => {
123
153
  if (!image)
124
154
  return;
125
155
  e.preventDefault();
126
156
  e.currentTarget.focus({ preventScroll: true });
127
157
  const p = point(e);
128
- if (s.tool === "gcp" && e.button === 0 && !e.shiftKey) {
129
- if (p[0] >= 0 && p[1] >= 0 && p[0] <= image.width && p[1] <= image.height)
130
- controller.setPendingPoint(p);
131
- return;
132
- }
158
+ const inside = p[0] >= 0 && p[1] >= 0 && p[0] <= image.width && p[1] <= image.height;
159
+ // Dragging always pans; a click without movement places a point while matching.
133
160
  drag.current = {
134
161
  start: [e.clientX, e.clientY],
135
162
  view,
136
163
  pointerId: e.pointerId,
164
+ click: s.tool === "gcp" && e.button === 0 && !e.shiftKey && inside
165
+ ? p
166
+ : undefined,
137
167
  };
138
168
  e.currentTarget.setPointerCapture(e.pointerId);
139
169
  };
140
- return (_jsxs("section", { className: "rg-image-panel", "aria-label": t("Source image"), onDragOver: (e) => e.preventDefault(), onDrop: (e) => {
170
+ return (_jsxs("section", { className: "rg-image-panel", "data-empty": image ? undefined : "", "aria-label": t("Source image"), onDragOver: (e) => e.preventDefault(), onDrop: (e) => {
141
171
  e.preventDefault();
142
172
  const file = e.dataTransfer.files[0];
143
173
  if (file)
@@ -183,6 +213,10 @@ t = identity, }) {
183
213
  setDragPoint({ id: d.id, point: d.point });
184
214
  }
185
215
  else {
216
+ if (!d.moved &&
217
+ Math.hypot(e.clientX - d.start[0], e.clientY - d.start[1]) < 4)
218
+ return;
219
+ d.moved = true;
186
220
  const scale = Math.max(view[2] / e.currentTarget.clientWidth, view[3] / e.currentTarget.clientHeight);
187
221
  setView([
188
222
  d.view[0] - (e.clientX - d.start[0]) * scale,
@@ -196,14 +230,14 @@ t = identity, }) {
196
230
  drag.current = null;
197
231
  if (d?.id && d.point)
198
232
  controller.updateGcp(d.id, { image: d.point });
233
+ else if (d?.click && !d.moved)
234
+ controller.setPendingPoint(d.click);
199
235
  setDragPoint(null);
200
236
  if (e.currentTarget.hasPointerCapture(e.pointerId))
201
237
  e.currentTarget.releasePointerCapture(e.pointerId);
202
238
  }, onPointerCancel: () => {
203
239
  drag.current = null;
204
240
  setDragPoint(null);
205
- }, onWheel: (e) => {
206
- zoom(e.deltaY > 0 ? 1.15 : 1 / 1.15, point(e));
207
241
  }, children: [_jsx("defs", { children: _jsx("filter", { id: filterId, colorInterpolationFilters: "sRGB", children: _jsxs("feComponentTransfer", { children: [_jsx("feFuncR", { type: "linear", slope: 255 / (stretch[0][1] - stretch[0][0]), intercept: -stretch[0][0] / (stretch[0][1] - stretch[0][0]) }), _jsx("feFuncG", { type: "linear", slope: 255 / (stretch[1][1] - stretch[1][0]), intercept: -stretch[1][0] / (stretch[1][1] - stretch[1][0]) }), _jsx("feFuncB", { type: "linear", slope: 255 / (stretch[2][1] - stretch[2][0]), intercept: -stretch[2][0] / (stretch[2][1] - stretch[2][0]) })] }) }) }), s.imageUrl && (_jsx("image", { href: s.imageUrl, width: image.width, height: image.height, style: {
208
242
  filter: `url(#${filterId}) brightness(${brightness}) contrast(${contrast})`,
209
243
  } })), s.document.gcps.map((gcp) => {
@@ -228,7 +262,7 @@ t = identity, }) {
228
262
  -image.height * 0.06,
229
263
  image.width * 1.08,
230
264
  image.height * 1.12,
231
- ]), children: t("Fit image") }), _jsx("button", { type: "button", onClick: () => controller.navigateImageHistory(-1), children: t("Previous image view") }), _jsx("button", { type: "button", onClick: () => controller.navigateImageHistory(1), children: t("Next image view") }), _jsx("button", { type: "button", onClick: () => void controller.removeImage(), children: t("Remove image") })] }), _jsxs("label", { children: [t("Linked navigation"), _jsxs("select", { disabled: !s.fit, value: s.linkedNavigation, onChange: (e) => controller.setLinkedNavigation(e.target.value), children: [_jsx("option", { value: "off", children: t("Independent views") }), _jsx("option", { value: "image-to-map", children: t("Map follows image") }), _jsx("option", { value: "map-to-image", children: t("Image follows map") })] })] }), _jsxs("details", { children: [_jsx("summary", { children: t("Display adjustment & histogram") }), _jsxs("div", { className: "rg-toolbar", children: [_jsx("button", { type: "button", onClick: () => stretchHistogram(false), children: t("Full histogram stretch") }), _jsx("button", { type: "button", onClick: () => stretchHistogram(true), children: t("Local histogram stretch") }), _jsx("button", { type: "button", onClick: () => {
265
+ ]), children: t("Fit image") }), navigation && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => controller.navigateImageHistory(-1), children: t("Previous image view") }), _jsx("button", { type: "button", onClick: () => controller.navigateImageHistory(1), children: t("Next image view") })] })), _jsx("button", { type: "button", onClick: () => void controller.removeImage(), children: t("Remove image") })] }), navigation && (_jsxs("label", { children: [t("Linked navigation"), _jsxs("select", { disabled: !s.fit, value: s.linkedNavigation, onChange: (e) => controller.setLinkedNavigation(e.target.value), children: [_jsx("option", { value: "off", children: t("Independent views") }), _jsx("option", { value: "image-to-map", children: t("Map follows image") }), _jsx("option", { value: "map-to-image", children: t("Image follows map") })] })] })), displayAdjustment && (_jsxs("details", { children: [_jsx("summary", { children: t("Display adjustment & histogram") }), _jsxs("div", { className: "rg-toolbar", children: [_jsx("button", { type: "button", onClick: () => stretchHistogram(false), children: t("Full histogram stretch") }), _jsx("button", { type: "button", onClick: () => stretchHistogram(true), children: t("Local histogram stretch") }), _jsx("button", { type: "button", onClick: () => {
232
266
  setStretch([
233
267
  [0, 255],
234
268
  [0, 255],
@@ -236,5 +270,10 @@ t = identity, }) {
236
270
  ]);
237
271
  setBrightness(1);
238
272
  setContrast(1);
239
- }, children: t("Reset display") })] }), _jsxs("label", { children: [t("Brightness"), _jsx("input", { type: "range", min: "0.25", max: "2", step: "0.05", value: brightness, onChange: (e) => setBrightness(Number(e.target.value)) })] }), _jsxs("label", { children: [t("Contrast"), _jsx("input", { type: "range", min: "0.25", max: "2", step: "0.05", value: contrast, onChange: (e) => setContrast(Number(e.target.value)) })] }), _jsx("svg", { viewBox: "0 0 320 50", "aria-label": t("RGB intensity histogram of preview"), className: "rg-histogram", children: histogram.map((v, i) => (_jsx("rect", { x: i * 10, y: 50 - (50 * v) / Math.max(1, ...histogram), width: "9", height: (50 * v) / Math.max(1, ...histogram), fill: "currentColor" }, i))) }), _jsx("small", { children: t("Display only. Original samples are preserved for export.") })] })] })) : (_jsxs("div", { className: "rg-empty", children: [_jsx("span", { className: "rg-cross", children: "\uFF0B" }), _jsx("p", { children: t("Drop a map, plan, or photograph here.") }), _jsx("small", { children: t("PNG, JPEG, WebP, ordinary TIFF · up to 25 MiB · stays on this device") })] }))] }));
273
+ }, children: t("Reset display") })] }), _jsxs("label", { children: [t("Brightness"), _jsx("input", { type: "range", min: "0.25", max: "2", step: "0.05", value: brightness, onChange: (e) => setBrightness(Number(e.target.value)) })] }), _jsxs("label", { children: [t("Contrast"), _jsx("input", { type: "range", min: "0.25", max: "2", step: "0.05", value: contrast, onChange: (e) => setContrast(Number(e.target.value)) })] }), _jsx("svg", { viewBox: "0 0 320 50", "aria-label": t("RGB intensity histogram of preview"), className: "rg-histogram", children: histogram.map((v, i) => (_jsx("rect", { x: i * 10, y: 50 - (50 * v) / Math.max(1, ...histogram), width: "9", height: (50 * v) / Math.max(1, ...histogram), fill: "currentColor" }, i))) }), _jsx("small", { children: t("Display only. Original samples are preserved for export.") })] }))] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "rg-image-meta", children: t("No image selected") }), _jsxs("div", { className: "rg-empty", children: [_jsx("span", { className: "rg-cross", children: "\uFF0B" }), _jsx("p", { children: t("Drop a map, plan, or photograph here.") }), _jsx("small", { children: t("PNG, JPEG, WebP, ordinary TIFF · up to 25 MiB · stays on this device") }), _jsxs("div", { className: "rg-toolbar rg-empty-actions", children: [_jsxs("label", { className: "rg-file rg-primary", children: [t("Choose image"), _jsx("input", { "aria-label": t("Choose image file"), type: "file", accept: "image/png,image/jpeg,image/webp,image/tiff,.tif,.tiff", onChange: (e) => {
274
+ const file = e.target.files?.[0];
275
+ if (file)
276
+ void controller.loadImage(file);
277
+ e.target.value = "";
278
+ } })] }), emptyActions] })] })] }))] }));
240
279
  }
@@ -5,11 +5,13 @@ import type { Translate } from "../localization.js";
5
5
  * enough complete, enabled pairs; the worker validates rank and domain. `onReview`
6
6
  * fires only for a successful preview of the same document/alignment revision.
7
7
  */
8
- export declare function PreviewControls({ controller, t, onReview, }: {
8
+ export declare function PreviewControls({ controller, t, onReview, modeSelector, }: {
9
9
  /** Shared authoritative controller. */
10
10
  controller: GeoreferencerController;
11
11
  /** Translate default English labels. */
12
12
  t?: Translate;
13
13
  /** Optional transition from matching into review after a current preview is ready. */
14
14
  onReview?: () => void;
15
+ /** Show the manual/automatic preview selector. Defaults to true. */
16
+ modeSelector?: boolean;
15
17
  }): import("react/jsx-runtime").JSX.Element;
@@ -7,11 +7,12 @@ import { identity } from "../localization.js";
7
7
  * enough complete, enabled pairs; the worker validates rank and domain. `onReview`
8
8
  * fires only for a successful preview of the same document/alignment revision.
9
9
  */
10
- export function PreviewControls({ controller, t = identity, onReview, }) {
10
+ export function PreviewControls({ controller, t = identity, onReview, modeSelector = true, }) {
11
11
  const s = useGeoreferencer(controller), d = s.document;
12
12
  const count = d.gcps.filter((p) => p.enabled).length;
13
- const missing = Math.max(0, MODELS[d.model].minimum - count);
14
- return (_jsxs("div", { className: "rg-preview-controls", children: [_jsxs("label", { children: [t("Preview updates"), _jsxs("select", { value: s.previewMode, onChange: (e) => controller.setPreviewMode(e.target.value), children: [_jsx("option", { value: "manual", children: t("Manual") }), _jsx("option", { value: "automatic", children: t("Automatic") })] })] }), _jsxs("div", { children: [_jsx("button", { type: "button", className: "rg-primary", disabled: !s.imageUrl ||
13
+ const minimum = MODELS[d.model].minimum;
14
+ const missing = Math.max(0, minimum - count);
15
+ return (_jsxs("div", { className: "rg-preview-controls", children: [modeSelector && (_jsxs("label", { children: [t("Preview updates"), _jsxs("select", { value: s.previewMode, onChange: (e) => controller.setPreviewMode(e.target.value), children: [_jsx("option", { value: "manual", children: t("Manual") }), _jsx("option", { value: "automatic", children: t("Automatic") })] })] })), _jsxs("div", { children: [_jsx("button", { type: "button", className: "rg-primary", disabled: !s.imageUrl ||
15
16
  !!s.pendingImagePoint ||
16
17
  missing > 0 ||
17
18
  s.fitting === "running" ||
@@ -30,7 +31,7 @@ export function PreviewControls({ controller, t = identity, onReview, }) {
30
31
  ? "Running alignment…"
31
32
  : s.fit && s.previewMode === "automatic"
32
33
  ? "Review alignment"
33
- : "Run alignment") }), _jsxs("small", { className: "rg-readiness", "aria-live": "polite", children: [count, " ", t("pairs"), missing > 0
34
- ? ` · ${t("Add at least")} ${missing} ${t("more")}`
35
- : ` · ${t("You can keep adding pairs")}`] })] })] }));
34
+ : "Run alignment") }), _jsx("small", { className: "rg-readiness", "aria-live": "polite", children: missing > 0
35
+ ? `${count} ${t("of")} ${minimum} ${t("points matched")}`
36
+ : `${count} ${t(count === 1 ? "point matched" : "points matched")} · ${t("more points improve accuracy")}` })] })] }));
36
37
  }
package/dist/styles.css CHANGED
@@ -277,18 +277,19 @@
277
277
  }
278
278
  }
279
279
 
280
- /* Guided two-view layout; all selectors remain scoped to the editor. */
281
- .rg-preview-controls {
282
- display: flex;
283
- align-items: center;
284
- gap: 16px;
285
- }
286
- .rg-preview-controls label {
287
- min-width: 115px;
280
+ /* Guided step-by-step layout; all selectors remain scoped to the editor. */
281
+ .rg-editor .rg-primary:hover:not(:disabled) {
282
+ background: #0e5a4e;
283
+ border-color: #0e5a4e;
284
+ color: white;
288
285
  }
289
- .rg-readiness {
290
- display: block;
291
- margin-top: 4px;
286
+ .rg-visually-hidden {
287
+ position: absolute;
288
+ width: 1px;
289
+ height: 1px;
290
+ overflow: hidden;
291
+ clip-path: inset(50%);
292
+ white-space: nowrap;
292
293
  }
293
294
  .rg-guided {
294
295
  padding: 18px;
@@ -296,45 +297,136 @@
296
297
  .rg-steps {
297
298
  display: flex;
298
299
  flex-wrap: wrap;
299
- gap: 12px 28px;
300
+ gap: 8px 22px;
300
301
  list-style: none;
301
302
  margin: 0;
302
303
  padding: 0 0 14px;
303
304
  border-bottom: 1px solid var(--rg-border);
304
- font-size: 12px;
305
+ font-size: 13px;
305
306
  color: #5b6c65;
306
307
  }
307
- .rg-steps [aria-current="step"] {
308
+ .rg-steps li,
309
+ .rg-editor .rg-step-button {
310
+ display: inline-flex;
311
+ align-items: center;
312
+ gap: 8px;
313
+ }
314
+ .rg-editor .rg-step-button {
315
+ padding: 0;
316
+ border: 0;
317
+ background: none;
318
+ color: inherit;
319
+ font-size: inherit;
320
+ }
321
+ .rg-editor .rg-step-button:hover .rg-step-label {
322
+ text-decoration: underline;
323
+ }
324
+ .rg-step-index {
325
+ display: inline-grid;
326
+ place-items: center;
327
+ width: 24px;
328
+ height: 24px;
329
+ border-radius: 50%;
330
+ border: 1.5px solid #9dab9e;
331
+ font-size: 12px;
332
+ font-weight: 700;
333
+ font-variant-numeric: tabular-nums;
334
+ }
335
+ .rg-steps [data-state="current"] {
308
336
  color: var(--rg-accent);
309
- font-weight: 750;
337
+ font-weight: 700;
338
+ }
339
+ .rg-steps [data-state="current"] .rg-step-index {
340
+ border-color: var(--rg-accent);
341
+ background: #e3efe9;
342
+ }
343
+ .rg-steps [data-state="done"] {
344
+ color: #20322f;
345
+ }
346
+ .rg-steps [data-state="done"] .rg-step-index {
347
+ border-color: var(--rg-accent);
348
+ background: var(--rg-accent);
349
+ color: white;
310
350
  }
311
351
  .rg-matching-bar {
312
- display: flex;
313
- justify-content: space-between;
352
+ display: grid;
353
+ grid-template-columns: minmax(0, 1fr) auto;
314
354
  align-items: center;
315
- gap: 20px;
316
- padding: 12px 0 4px;
355
+ gap: 8px 24px;
356
+ padding: 14px 0 10px;
357
+ }
358
+ .rg-guided .rg-matching-bar {
359
+ position: sticky;
360
+ top: 0;
361
+ z-index: 5;
362
+ background: var(--rg-paper);
363
+ border-bottom: 1px solid var(--rg-border);
317
364
  }
318
365
  .rg-instruction strong {
319
366
  font-size: 16px;
320
367
  }
321
368
  .rg-instruction p {
322
- max-width: 600px;
369
+ max-width: 640px;
370
+ margin: 4px 0 0;
323
371
  font-size: 12px;
324
372
  color: #5b6c65;
325
373
  }
374
+ .rg-editor .rg-link {
375
+ padding: 0;
376
+ border: 0;
377
+ background: none;
378
+ color: var(--rg-accent);
379
+ font-size: inherit;
380
+ text-decoration: underline;
381
+ }
382
+ .rg-step-actions {
383
+ display: flex;
384
+ flex-wrap: wrap;
385
+ align-items: center;
386
+ justify-content: flex-end;
387
+ gap: 8px;
388
+ }
389
+ .rg-preview-controls {
390
+ display: flex;
391
+ align-items: center;
392
+ gap: 16px;
393
+ }
394
+ .rg-preview-controls label {
395
+ display: flex;
396
+ align-items: center;
397
+ gap: 8px;
398
+ margin: 0;
399
+ }
400
+ .rg-preview-controls > div {
401
+ text-align: right;
402
+ }
403
+ .rg-readiness {
404
+ display: block;
405
+ margin-top: 4px;
406
+ }
326
407
  .rg-match-tools {
327
- margin: 0 0 12px;
408
+ margin: 10px 0 12px;
328
409
  }
329
- .rg-match-tools label {
410
+ .rg-tools-end {
330
411
  display: flex;
331
412
  align-items: center;
332
- gap: 10px;
413
+ gap: 14px;
333
414
  margin-left: auto;
334
415
  }
335
- .rg-match-tools select {
416
+ .rg-editor .rg-inline-field {
417
+ display: flex;
418
+ align-items: center;
419
+ gap: 8px;
420
+ margin: 0;
421
+ white-space: nowrap;
422
+ }
423
+ .rg-editor .rg-inline-field select {
424
+ width: auto;
336
425
  max-width: 225px;
337
426
  }
427
+ .rg-guided .rg-dirty {
428
+ line-height: 1.6;
429
+ }
338
430
  .rg-paired-views {
339
431
  display: grid;
340
432
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
@@ -355,54 +447,124 @@
355
447
  }
356
448
  .rg-guided .rg-image-panel .rg-panel-heading {
357
449
  min-height: 39px;
450
+ align-items: center;
358
451
  }
359
452
  .rg-guided .rg-image-panel .rg-panel-heading h2 {
360
453
  margin: 0;
361
454
  }
455
+ .rg-guided .rg-image-panel .rg-panel-heading .rg-file {
456
+ margin: 0;
457
+ }
458
+ .rg-guided .rg-image-panel[data-empty] .rg-panel-heading .rg-file {
459
+ display: none;
460
+ }
362
461
  .rg-guided .rg-image-meta {
363
462
  margin-top: 4px;
364
463
  }
464
+ .rg-empty-actions {
465
+ justify-content: center;
466
+ margin-top: 16px;
467
+ }
365
468
  .rg-view-tabs {
366
469
  display: none;
470
+ border: 0;
471
+ margin: 0;
472
+ padding: 0;
367
473
  }
368
474
  .rg-review-panels {
369
475
  display: grid;
370
476
  grid-template-columns: repeat(2, minmax(0, 1fr));
371
- gap: 24px;
372
- padding: 18px 0;
477
+ gap: 18px;
478
+ padding: 18px 0 0;
373
479
  }
374
- .rg-review-panels section + section {
480
+ .rg-editor .rg-review-panels section + section {
375
481
  margin-top: 0;
376
482
  padding-top: 0;
377
483
  border-top: none;
378
484
  }
485
+ .rg-card {
486
+ min-width: 0;
487
+ padding: 16px;
488
+ border: 1px solid var(--rg-border);
489
+ border-radius: 6px;
490
+ background: white;
491
+ }
492
+ .rg-editor .rg-review-panels .rg-card {
493
+ padding: 16px;
494
+ }
495
+ .rg-editor .rg-card h3 {
496
+ margin: 16px 0 0;
497
+ font-size: 13px;
498
+ font-weight: 650;
499
+ }
500
+ .rg-accuracy {
501
+ margin-bottom: 12px;
502
+ padding: 10px 12px;
503
+ border-left: 4px solid #9dab9e;
504
+ background: #f4f6f1;
505
+ }
506
+ .rg-accuracy[data-quality="good"] {
507
+ border-left-color: #2f8a5b;
508
+ }
509
+ .rg-accuracy[data-quality="fair"] {
510
+ border-left-color: #c48e45;
511
+ }
512
+ .rg-accuracy[data-quality="poor"] {
513
+ border-left-color: #b4452d;
514
+ }
515
+ .rg-accuracy p {
516
+ margin: 4px 0;
517
+ font-size: 12px;
518
+ }
379
519
  .rg-guided .rg-gcp-panel {
380
520
  border-top: 1px solid var(--rg-border);
381
521
  margin-top: 18px;
382
522
  padding-top: 16px;
383
523
  }
384
- .rg-guided .rg-gcp-panel > .rg-toolbar,
385
- .rg-guided .rg-gcp-panel > .rg-hint {
386
- display: none;
524
+ .rg-editor .rg-review-panels .rg-gcp-panel {
525
+ margin-top: 0;
526
+ padding-top: 0;
527
+ border-top: none;
528
+ }
529
+ .rg-editor .rg-point-list {
530
+ width: auto;
531
+ min-width: 280px;
387
532
  }
388
533
  .rg-advanced {
389
534
  margin-top: 16px;
390
535
  }
536
+ .rg-export-progress {
537
+ display: flex;
538
+ align-items: center;
539
+ gap: 12px;
540
+ margin: 10px 0;
541
+ }
542
+ .rg-status:empty {
543
+ margin: 0;
544
+ padding: 0;
545
+ border: 0;
546
+ }
391
547
  @media (max-width: 720px) {
392
548
  .rg-guided {
393
549
  padding: 12px;
394
550
  }
551
+ .rg-steps li:not([data-state="current"]) .rg-step-label {
552
+ display: none;
553
+ }
395
554
  .rg-matching-bar {
396
- flex-direction: column;
397
- align-items: stretch;
398
- gap: 4px;
555
+ grid-template-columns: minmax(0, 1fr);
399
556
  }
557
+ .rg-step-actions,
400
558
  .rg-preview-controls {
401
- justify-content: space-between;
559
+ justify-content: flex-start;
402
560
  }
403
- .rg-match-tools label {
561
+ .rg-preview-controls > div {
562
+ text-align: left;
563
+ }
564
+ .rg-tools-end {
404
565
  margin-left: 0;
405
566
  width: 100%;
567
+ flex-wrap: wrap;
406
568
  }
407
569
  .rg-paired-views {
408
570
  grid-template-columns: minmax(0, 1fr);
@@ -423,18 +585,10 @@
423
585
  grid-template-columns: minmax(0, 1fr);
424
586
  }
425
587
  }
426
-
427
- .rg-guided .rg-image-panel .rg-panel-heading .rg-file {
428
- margin: 0;
429
- }
430
- .rg-view-tabs {
431
- border: 0;
432
- margin: 0;
433
- padding: 0;
434
- }
435
- .rg-guided .rg-matching-bar {
436
- position: sticky;
437
- top: 0;
438
- z-index: 5;
439
- background: var(--rg-paper);
588
+ .rg-editor .rg-error {
589
+ padding: 8px 12px;
590
+ border-left: 3px solid #b4452d;
591
+ background: #fbefea;
592
+ color: #7a2614;
593
+ font-size: 12px;
440
594
  }
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.0",
2
+ "version": "0.2.0",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
5
  "engines": {
@@ -32,7 +32,7 @@
32
32
  "./styles.css": "./dist/styles.css"
33
33
  },
34
34
  "dependencies": {
35
- "@georeferencing/core": "^0.1.0"
35
+ "@georeferencing/core": "^0.2.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "react": ">=19.3.0 <20",