Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
7 changes: 5 additions & 2 deletions src/components/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { localeNames } from "../locales";
import type { LocaleCode } from "../locales";
import { mmToUnit } from "../lib/units";
import { useT } from "../lib/useT";
import { kbd } from "../lib/kbd";
import { useGlobalShortcuts } from "../hooks/useGlobalShortcuts";
import { useDesignFileActions } from "../hooks/useDesignFileActions";
import { useZplImportExport } from "../hooks/useZplImportExport";
Expand Down Expand Up @@ -114,15 +115,17 @@ export function AppShell() {
<button
onClick={() => undo()}
disabled={!canUndo}
title="Undo (⌘Z)"
title={`${t.app.undo} (${kbd('Z')})`}
aria-label={t.app.undo}
className="p-1.5 rounded text-muted hover:text-text hover:bg-surface-2 disabled:opacity-25 disabled:cursor-not-allowed transition-colors"
>
<ArrowUturnLeftIcon className="w-3.5 h-3.5" />
</button>
<button
onClick={() => redo()}
disabled={!canRedo}
title="Redo (⌘⇧Z)"
title={`${t.app.redo} (${kbd('Z', { shift: true })})`}
aria-label={t.app.redo}
className="p-1.5 rounded text-muted hover:text-text hover:bg-surface-2 disabled:opacity-25 disabled:cursor-not-allowed transition-colors"
>
<ArrowUturnRightIcon className="w-3.5 h-3.5" />
Expand Down
1 change: 1 addition & 0 deletions src/components/Fonts/FontManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ function FontEntry({ name }: FontEntryProps) {
onClick={() => removeFont(name)}
className="opacity-0 group-hover:opacity-100 font-mono text-[10px] text-muted hover:text-red-400 transition-all px-1"
title={t.fonts.delete}
aria-label={t.fonts.delete}
>
×
</button>
Expand Down
66 changes: 34 additions & 32 deletions src/components/Output/ImportReportModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { useState } from 'react';
import { XMarkIcon, ClipboardDocumentIcon, CheckIcon } from '@heroicons/react/16/solid';
import { partialLoss, formatReportAsText } from '../../lib/importReport';
import type { ImportResult } from '../../lib/importReport';
import { useT } from '../../lib/useT';
import { DialogShell } from '../ui/DialogShell';

export type { ImportResult };

Expand Down Expand Up @@ -55,6 +57,7 @@ interface Props {
}

export function ImportReportModal({ result, onClose }: Props) {
const t = useT();
const [copied, setCopied] = useState(false);

const handleCopy = () => {
Expand All @@ -65,41 +68,40 @@ export function ImportReportModal({ result, onClose }: Props) {
};

return (
<div
className="fixed inset-0 z-50 flex items-center justify-center"
style={{ background: 'rgba(0,0,0,0.6)' }}
onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}
<DialogShell
onClose={onClose}
labelledBy="import-report-title"
boxClassName="bg-surface border border-border rounded-lg w-130 flex flex-col shadow-2xl max-h-[80vh]"
>
<div className="bg-surface border border-border rounded-lg w-130 flex flex-col shadow-2xl max-h-[80vh]">
<div className="flex items-center justify-between px-4 py-3 border-b border-border shrink-0">
<span className="font-mono text-xs text-muted uppercase tracking-widest">Import Report</span>
<button
onClick={onClose}
className="p-0.5 rounded text-muted hover:text-text hover:bg-surface-2 transition-colors"
>
<XMarkIcon className="w-4 h-4" />
</button>
</div>
<div className="flex items-center justify-between px-4 py-3 border-b border-border shrink-0">
<span id="import-report-title" className="font-mono text-xs text-muted uppercase tracking-widest">Import Report</span>
<button
onClick={onClose}
aria-label={t.app.close}
className="p-0.5 rounded text-muted hover:text-text hover:bg-surface-2 transition-colors"
>
<XMarkIcon className="w-4 h-4" />
</button>
</div>

<ImportSummaryBody result={result} />
<ImportSummaryBody result={result} />

<div className="flex justify-between items-center px-4 py-3 border-t border-border shrink-0">
<button
onClick={handleCopy}
className="flex items-center gap-1.5 font-mono text-[10px] text-muted hover:text-text transition-colors"
>
{copied
? <><CheckIcon className="w-3.5 h-3.5" /> Copied</>
: <><ClipboardDocumentIcon className="w-3.5 h-3.5" /> Copy report</>}
</button>
<button
onClick={onClose}
className="px-3 py-1.5 rounded text-xs font-mono bg-accent text-bg hover:opacity-90 transition-opacity"
>
Close
</button>
</div>
<div className="flex justify-between items-center px-4 py-3 border-t border-border shrink-0">
<button
onClick={handleCopy}
className="flex items-center gap-1.5 font-mono text-[10px] text-muted hover:text-text transition-colors"
>
{copied
? <><CheckIcon className="w-3.5 h-3.5" /> Copied</>
: <><ClipboardDocumentIcon className="w-3.5 h-3.5" /> Copy report</>}
</button>
<button
onClick={onClose}
className="px-3 py-1.5 rounded text-xs font-mono bg-accent text-bg hover:opacity-90 transition-opacity"
>
Close
Comment thread
u8array marked this conversation as resolved.
Outdated
</button>
</div>
</div>
</DialogShell>
);
}
118 changes: 56 additions & 62 deletions src/components/Output/LabelPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { generateZPL } from '../../lib/zplGenerator';
import { fetchPreview, labelaryErrorMessage } from '../../lib/labelary';
import { triggerDownload } from '../../lib/triggerDownload';
import { useT } from '../../lib/useT';
import { DialogShell } from '../ui/DialogShell';

interface Props {
onClose: () => void;
Expand Down Expand Up @@ -51,72 +52,65 @@ export function LabelPreviewModal({ onClose }: Props) {
};

return (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
onClick={onClose}
role="dialog"
aria-modal="true"
aria-labelledby="label-preview-title"
<DialogShell
onClose={onClose}
labelledBy="label-preview-title"
boxClassName="bg-surface border border-border-2 rounded shadow-lg flex flex-col overflow-hidden max-w-[90vw] max-h-[90vh]"
>
<div
className="bg-surface border border-border-2 rounded shadow-lg flex flex-col overflow-hidden max-w-[90vw] max-h-[90vh]"
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center justify-between px-3 py-1.5 border-b border-border-2 shrink-0">
<span id="label-preview-title" className="font-mono text-[10px] text-muted uppercase tracking-widest">
{t.output.previewHeading}
</span>
<button
onClick={onClose}
aria-label={t.app.close}
className="p-0.5 rounded text-muted hover:text-text hover:bg-surface-2 transition-colors ml-6"
>
<XMarkIcon className="w-4 h-4" />
</button>
</div>
<div className="flex items-center justify-between px-3 py-1.5 border-b border-border-2 shrink-0">
<span id="label-preview-title" className="font-mono text-[10px] text-muted uppercase tracking-widest">
{t.output.previewHeading}
</span>
<button
onClick={onClose}
aria-label={t.app.close}
className="p-0.5 rounded text-muted hover:text-text hover:bg-surface-2 transition-colors ml-6"
>
<XMarkIcon className="w-4 h-4" />
</button>
</div>

{/* Inset preview area: bg-bg gives a clear edge against the surrounding
surface (especially in light mode where the label image is white).
The outer div scrolls; the inner one stays at least as large as the
viewport so small previews are still centered. */}
<div className="flex-1 overflow-auto bg-bg min-h-24 min-w-48">
<div className="min-h-full min-w-full flex items-center justify-center p-4">
{loading && (
<span className="font-mono text-[10px] text-muted animate-pulse">{t.output.loading}</span>
)}
{!loading && error && (
<div className="flex flex-col items-center gap-3 max-w-64 text-center">
<span className="font-mono text-[10px] text-amber-400 leading-relaxed">{error}</span>
<button
onClick={handleDownloadFallback}
className="flex items-center gap-1.5 px-3 py-1.5 rounded text-[10px] font-mono bg-surface-2 border border-border text-muted hover:text-text hover:border-accent transition-colors"
>
<ArrowDownTrayIcon className="w-3.5 h-3.5" />
Export ZPL instead
</button>
</div>
)}
{!loading && !error && previewUrl && (
<img
src={previewUrl}
alt="Label preview"
className="block shrink-0"
/>
)}
</div>
{/* Inset preview area: bg-bg gives a clear edge against the surrounding
surface (especially in light mode where the label image is white).
The outer div scrolls; the inner one stays at least as large as the
viewport so small previews are still centered. */}
<div className="flex-1 overflow-auto bg-bg min-h-24 min-w-48">
<div className="min-h-full min-w-full flex items-center justify-center p-4">
{loading && (
<span className="font-mono text-[10px] text-muted animate-pulse">{t.output.loading}</span>
)}
{!loading && error && (
<div className="flex flex-col items-center gap-3 max-w-64 text-center">
<span className="font-mono text-[10px] text-amber-400 leading-relaxed">{error}</span>
<button
onClick={handleDownloadFallback}
className="flex items-center gap-1.5 px-3 py-1.5 rounded text-[10px] font-mono bg-surface-2 border border-border text-muted hover:text-text hover:border-accent transition-colors"
>
<ArrowDownTrayIcon className="w-3.5 h-3.5" />
Export ZPL instead
</button>
</div>
)}
{!loading && !error && previewUrl && (
<img
src={previewUrl}
alt="Label preview"
className="block shrink-0"
/>
)}
</div>
</div>

<div className="px-3 py-1 border-t border-border-2 shrink-0 text-center">
<a
href="https://labelary.com/"
target="_blank"
rel="noreferrer"
className="font-mono text-[9px] text-muted hover:text-accent transition-colors"
>
{t.output.previewProvider}
</a>
</div>
<div className="px-3 py-1 border-t border-border-2 shrink-0 text-center">
<a
href="https://labelary.com/"
target="_blank"
rel="noreferrer"
className="font-mono text-[9px] text-muted hover:text-accent transition-colors"
>
{t.output.previewProvider}
</a>
</div>
</div>
</DialogShell>
);
}
74 changes: 34 additions & 40 deletions src/components/Output/LabelaryNoticeModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { XMarkIcon } from '@heroicons/react/16/solid';
import { useLabelStore } from '../../store/labelStore';
import { useT } from '../../lib/useT';
import { DialogShell } from '../ui/DialogShell';

interface Props {
/** Called after the modal flipped the store flag, so callers only need
Expand All @@ -25,48 +26,41 @@ export function LabelaryNoticeModal({ onContinue, onClose }: Props) {
};

return (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
onClick={onClose}
role="dialog"
aria-modal="true"
aria-labelledby="labelary-notice-title"
<DialogShell
onClose={onClose}
labelledBy="labelary-notice-title"
boxClassName="bg-surface border border-border-2 rounded shadow-lg flex flex-col overflow-hidden max-w-[90vw]"
>
<div
className="bg-surface border border-border-2 rounded shadow-lg flex flex-col overflow-hidden max-w-[90vw]"
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center justify-between px-3 py-1.5 border-b border-border-2 shrink-0">
<span id="labelary-notice-title" className="font-mono text-[10px] text-muted uppercase tracking-widest">
{t.output.previewNoticeTitle}
</span>
<button
onClick={onClose}
aria-label={t.app.close}
className="p-0.5 rounded text-muted hover:text-text hover:bg-surface-2 transition-colors ml-6"
>
<XMarkIcon className="w-4 h-4" />
</button>
</div>
<div className="flex items-center justify-between px-3 py-1.5 border-b border-border-2 shrink-0">
<span id="labelary-notice-title" className="font-mono text-[10px] text-muted uppercase tracking-widest">
{t.output.previewNoticeTitle}
</span>
<button
onClick={onClose}
aria-label={t.app.close}
className="p-0.5 rounded text-muted hover:text-text hover:bg-surface-2 transition-colors ml-6"
>
<XMarkIcon className="w-4 h-4" />
</button>
</div>

<div className="flex flex-col gap-3 p-6 max-w-80 text-center font-mono text-[10px] text-muted leading-relaxed">
<span>{t.output.previewNoticeBody}</span>
<a
href="https://labelary.com/privacy.html"
target="_blank"
rel="noreferrer"
className="text-accent hover:underline"
>
{t.output.previewNoticePrivacyLink}
</a>
<button
onClick={handleContinue}
className="self-center mt-1 px-3 py-1.5 rounded text-[10px] font-mono bg-surface-2 border border-border text-text hover:border-accent transition-colors"
>
{t.output.previewNoticeAcknowledge}
</button>
</div>
<div className="flex flex-col gap-3 p-6 max-w-80 text-center font-mono text-[10px] text-muted leading-relaxed">
<span>{t.output.previewNoticeBody}</span>
<a
href="https://labelary.com/privacy.html"
target="_blank"
rel="noreferrer"
className="text-accent hover:underline"
>
{t.output.previewNoticePrivacyLink}
</a>
<button
onClick={handleContinue}
className="self-center mt-1 px-3 py-1.5 rounded text-[10px] font-mono bg-surface-2 border border-border text-text hover:border-accent transition-colors"
>
{t.output.previewNoticeAcknowledge}
</button>
</div>
</div>
</DialogShell>
);
}
Loading