Files
RenewCMS/docs/design/source/IntegrationImages.dc.html

125 lines
16 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<div style="display:flex;flex-direction:column;gap:{{ gap }};max-width:880px;min-width:0;font-family:'IBM Plex Sans',sans-serif;font-size:13px;line-height:20px;color:var(--ui-text)">
<div style="display:flex;flex-direction:column;gap:8px">
<h2 style="margin:0;font:600 18px/26px 'IBM Plex Sans',sans-serif;color:var(--ui-text-highlighted)">Images and variants</h2>
<p style="margin:0;color:var(--ui-text-toned);text-wrap:pretty;max-width:720px">Every image in the media library can be requested as a named variant. Add the <code style="font:400 12px 'IBM Plex Mono',monospace;color:var(--ui-text-highlighted)">?v=&lt;variant-name&gt;</code> parameter to the image URL. The original file is never changed, and a variant is generated the first time it is requested. Variants are managed in Settings, Media, Variants.</p>
</div>
<sc-if value="{{ loading }}">
<div style="display:flex;flex-direction:column;gap:12px" aria-busy="true">
<span style="display:block;height:140px;background:var(--ui-bg-accented)"></span>
<span style="display:block;height:200px;background:var(--ui-bg-accented)"></span>
<span style="display:block;height:120px;background:var(--ui-bg-accented)"></span>
</div>
</sc-if>
<sc-if value="{{ failed }}">
<div role="alert" style="display:flex;gap:10px;padding:12px 14px;background:var(--rc-error-soft);color:var(--ui-error)"><span aria-hidden="true" style="padding-top:2px;display:flex"><span style="display:inline-block;width:16px;height:16px;background-color:currentColor;-webkit-mask:url('https://api.iconify.design/lucide/circle-x.svg') center / contain no-repeat;mask:url('https://api.iconify.design/lucide/circle-x.svg') center / contain no-repeat"></span></span><div style="flex:1;display:flex;flex-direction:column;gap:8px"><div style="display:flex;flex-direction:column;gap:2px"><span style="font-weight:600">Couldn't load image variants</span><span style="color:var(--ui-text-toned)">The snippets below still work. The variant list and the URL builder need a connection.</span></div><span style="align-self:flex-start;height:{{ btn }};padding:0 12px;display:flex;align-items:center;gap:6px;border:1px solid var(--ui-border-accented);background:var(--ui-bg);color:var(--ui-text-highlighted);font-weight:500">Try again</span></div></div>
</sc-if>
<sc-if value="{{ showSnippets }}">
<section style="display:flex;flex-direction:column;gap:8px">
<h3 style="margin:0;font:600 14px/22px 'IBM Plex Sans',sans-serif;color:var(--ui-text-highlighted)">Request an image</h3>
<dc-import name="CodeSnippet" tabs="{{ snippets }}" active="{{ tab }}" copied="{{ copied }}" hint-size="100%,200px"></dc-import>
</section>
</sc-if>
<sc-if value="{{ noneAdmin }}">
<div style="background:var(--ui-bg);border:1px solid var(--ui-border);padding:32px 24px;display:flex;flex-direction:column;align-items:center;gap:12px;text-align:center"><span style="width:44px;height:44px;display:flex;align-items:center;justify-content:center;background:var(--ui-bg-elevated);color:var(--ui-text-toned)"><span aria-hidden="true" style="display:inline-block;width:20px;height:20px;background-color:currentColor;-webkit-mask:url('https://api.iconify.design/lucide/layers.svg') center / contain no-repeat;mask:url('https://api.iconify.design/lucide/layers.svg') center / contain no-repeat"></span></span><span style="font-weight:600;font-size:15px;color:var(--ui-text-highlighted)">No image variants yet</span><span style="color:var(--ui-text-muted);max-width:420px;text-wrap:pretty">Until you add a variant, the ?v= parameter is not accepted and every request returns the original image.</span><span style="height:{{ btn }};padding:0 16px;display:flex;align-items:center;gap:6px;background:var(--ui-primary);color:var(--ui-bg);border:1px solid var(--ui-primary);font-weight:500">Go to image variants</span></div>
</sc-if>
<sc-if value="{{ nonePlain }}">
<div style="background:var(--ui-bg);border:1px solid var(--ui-border);padding:32px 24px;display:flex;flex-direction:column;align-items:center;gap:12px;text-align:center"><span style="width:44px;height:44px;display:flex;align-items:center;justify-content:center;background:var(--ui-bg-elevated);color:var(--ui-text-toned)"><span aria-hidden="true" style="display:inline-block;width:20px;height:20px;background-color:currentColor;-webkit-mask:url('https://api.iconify.design/lucide/layers.svg') center / contain no-repeat;mask:url('https://api.iconify.design/lucide/layers.svg') center / contain no-repeat"></span></span><span style="font-weight:600;font-size:15px;color:var(--ui-text-highlighted)">No image variants yet</span><span style="color:var(--ui-text-muted);max-width:420px;text-wrap:pretty">Ask an owner or admin to add one. Until then, every request returns the original image.</span></div>
</sc-if>
<sc-if value="{{ hasVariants }}">
<section style="display:flex;flex-direction:column;gap:8px">
<div style="display:flex;align-items:baseline;gap:12px;flex-wrap:wrap"><h3 style="margin:0;font:600 14px/22px 'IBM Plex Sans',sans-serif;color:var(--ui-text-highlighted)">Variants</h3><span style="color:var(--ui-text-muted)">{{ countText }}</span></div>
<div style="border:1px solid var(--ui-border);background:var(--ui-bg);overflow-x:auto">
<div style="min-width:{{ tblMin }}">
<div style="display:grid;grid-template-columns:96px 112px 72px minmax(0,1fr);gap:12px;height:36px;align-items:center;padding:0 12px;background:var(--ui-bg-elevated);color:var(--ui-text-muted);font-weight:500"><span>Name</span><span>Dimensions</span><span>Format</span><span>Example URL</span></div>
<div style="max-height:{{ tblMax }};overflow-y:auto">
<sc-for list="{{ rows }}" as="r" hint-placeholder-count="5">
<div style="display:grid;grid-template-columns:96px 112px 72px minmax(0,1fr);gap:12px;min-height:40px;align-items:center;padding:0 12px;border-top:1px solid var(--ui-border);background:{{ r.bg }}"><span style="font:500 12px 'IBM Plex Mono',monospace;color:var(--ui-text-highlighted)">{{ r.n }}</span><span style="color:var(--ui-text-toned)">{{ r.dim }}</span><span style="color:var(--ui-text-toned)">{{ r.fmt }}</span><span style="font:400 12px 'IBM Plex Mono',monospace;color:var(--ui-text-toned);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ r.url }}</span></div>
</sc-for>
</div>
</div>
</div>
<sc-if value="{{ disabledNote }}"><span style="color:var(--ui-text-muted)">Disabled variants are not served. A request for one returns 400 "Unknown variant".</span></sc-if>
</section>
<section style="display:flex;flex-direction:column;gap:8px">
<h3 style="margin:0;font:600 14px/22px 'IBM Plex Sans',sans-serif;color:var(--ui-text-highlighted)">URL builder</h3>
<div style="background:var(--ui-bg);border:1px solid var(--ui-border);padding:{{ pad }};display:grid;grid-template-columns:{{ bCols }};gap:16px;align-items:start">
<div style="display:flex;flex-direction:column;gap:12px;min-width:0">
<div style="display:flex;flex-direction:column;gap:6px"><span style="font-weight:500;color:var(--ui-text-highlighted)">Image</span><span style="height:{{ inH }};padding:0 10px;display:flex;align-items:center;gap:8px;border:1px solid var(--ui-border-accented);background:var(--ui-bg);color:var(--ui-text-highlighted)"><span style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ image }}</span><span aria-hidden="true" style="display:inline-block;width:14px;height:14px;background-color:var(--ui-text-muted);-webkit-mask:url('https://api.iconify.design/lucide/chevron-down.svg') center / contain no-repeat;mask:url('https://api.iconify.design/lucide/chevron-down.svg') center / contain no-repeat"></span></span></div>
<div style="display:flex;flex-direction:column;gap:6px"><span style="font-weight:500;color:var(--ui-text-highlighted)">Variant</span><span style="height:{{ inH }};padding:0 10px;display:flex;align-items:center;gap:8px;border:1px solid {{ vBd }};background:var(--ui-bg);color:var(--ui-text-highlighted)"><span style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ vname }}</span><span aria-hidden="true" style="display:inline-block;width:14px;height:14px;background-color:var(--ui-text-muted);-webkit-mask:url('https://api.iconify.design/lucide/chevron-down.svg') center / contain no-repeat;mask:url('https://api.iconify.design/lucide/chevron-down.svg') center / contain no-repeat"></span></span></div>
<div style="display:flex;flex-direction:column;gap:6px"><span style="font-weight:500;color:var(--ui-text-highlighted)">URL</span>
<dc-import name="CodeSnippet" tabs="{{ urlSnips }}" copied="{{ urlCopied }}" hint-size="100%,96px"></dc-import></div>
<sc-if value="{{ removed }}"><div role="alert" style="display:flex;gap:10px;padding:12px 14px;background:var(--rc-warning-soft);color:var(--ui-warning)"><span aria-hidden="true" style="padding-top:2px;display:flex"><span style="display:inline-block;width:16px;height:16px;background-color:currentColor;-webkit-mask:url('https://api.iconify.design/lucide/triangle-alert.svg') center / contain no-repeat;mask:url('https://api.iconify.design/lucide/triangle-alert.svg') center / contain no-repeat"></span></span><div style="display:flex;flex-direction:column;gap:2px"><span style="font-weight:600">The variant "hero" no longer exists</span><span style="color:var(--ui-text-toned);text-wrap:pretty">There is no fallback to the original. Requests with ?v=hero return status 400 with the body <code style="font:400 12px 'IBM Plex Mono',monospace">{{ fallbackBody }}</code>. Remove the parameter to get the original image, or pick another variant.</span></div></div></sc-if>
</div>
<div style="display:flex;flex-direction:column;gap:6px;min-width:0"><span style="font-weight:500;color:var(--ui-text-highlighted)">Preview</span>
<div role="img" aria-label="{{ previewLabel }}" style="aspect-ratio:{{ ratio }};max-height:240px;width:100%;display:flex;align-items:center;justify-content:center;background:repeating-linear-gradient(135deg,var(--ui-bg-elevated) 0 8px,var(--ui-bg-accented) 8px 16px);border:1px solid var(--ui-border);color:var(--ui-text-toned);opacity:{{ prevOp }}"><span style="padding:4px 8px;background:var(--ui-bg);border:1px solid var(--ui-border);font:500 12px 'IBM Plex Mono',monospace">{{ previewLabel }}</span></div></div>
</div>
</section>
</sc-if>
<sc-if value="{{ showResponses }}">
<section style="display:flex;flex-direction:column;gap:8px">
<h3 style="margin:0;font:600 14px/22px 'IBM Plex Sans',sans-serif;color:var(--ui-text-highlighted)">Responses</h3>
<div style="border:1px solid var(--ui-border);background:var(--ui-bg);overflow-x:auto"><div style="min-width:{{ respMin }}">
<sc-for list="{{ resp }}" as="r" hint-placeholder-count="4">
<div style="display:grid;grid-template-columns:minmax(0,1.3fr) 56px minmax(0,1fr);gap:12px;min-height:40px;align-items:center;padding:6px 12px;border-top:{{ r.bt }}"><span style="font:400 12px 'IBM Plex Mono',monospace;color:var(--ui-text-highlighted)">{{ r.req }}</span><span style="font:500 12px 'IBM Plex Mono',monospace;color:var(--ui-text-highlighted)">{{ r.st }}</span><span style="display:flex;flex-direction:column;gap:2px;min-width:0;color:var(--ui-text-toned)">{{ r.res }}<sc-if value="{{ r.body }}"><span style="font:400 12px/18px 'IBM Plex Mono',monospace;color:var(--ui-text-muted);overflow-wrap:anywhere">{{ r.body }}</span></sc-if></span></div>
</sc-for></div></div>
</section>
</sc-if>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props="{&quot;$preview&quot;:{&quot;width&quot;:880,&quot;height&quot;:900},&quot;s&quot;:{&quot;editor&quot;:null,&quot;tsType&quot;:&quot;object&quot;},&quot;mobile&quot;:{&quot;editor&quot;:&quot;boolean&quot;,&quot;default&quot;:false,&quot;tsType&quot;:&quot;boolean&quot;}}">
class Component extends DCLogic {
renderVals() {
const s = this.props.s || {}, m = !!this.props.mobile
const B = 'https://cms.renew.example', st = s.v || 'many', admin = s.admin !== false
const ALL = [['thumb', '400×300', 'WebP'], ['card', '800 × auto', 'WebP'], ['hero', '1200×630', 'WebP'], ['square', '600×600', 'JPEG'], ['og', '1200×630', 'JPEG'], ['banner', '1600×400', 'WebP'], ['teaser', '640×360', 'WebP'], ['sidebar', '320 × auto', 'WebP'], ['gallery', '1000×750', 'WebP'], ['avatar', '96×96', 'WebP'], ['wide', '1920×800', 'WebP'], ['mini', '120×90', 'WebP']]
let list = st === 'one' ? ALL.slice(0, 1) : st === 'none' || st === 'loading' || st === 'error' ? [] : s.removed ? ALL.filter(v => v[0] !== 'hero').slice(0, 5) : st === 'many' ? ALL : ALL.slice(0, 5)
const img = (s.image || 'north-wall.jpg')
const vn = s.variant || (list[0] ? list[0][0] : '<variant-name>')
const real = list.length ? vn : '<variant-name>'
const u = n => B + '/media/' + img + '?v=' + n
const snippets = [
{ k: 'curl', label: 'curl', code: 'curl -O "' + u(real) + '"' },
{ k: 'html', label: 'HTML', code: '<img\n src="' + u(list[1] ? list[1][0] : real) + '"\n srcset="' + u(real) + ' 400w,\n ' + u(list[1] ? list[1][0] : real) + ' 800w,\n ' + u(list[2] ? list[2][0] : real) + ' 1200w"\n sizes="(min-width: 800px) 800px, 100vw"\n alt="North wall">' },
{ k: 'js', label: 'JavaScript', code: 'const res = await fetch("' + u(real) + '")\nif (!res.ok) throw new Error(await res.text())\nconst blob = await res.blob()' }
]
const missing = !!s.removed
const sel = missing ? 'hero' : vn
const cur = ALL.find(v => v[0] === sel) || ALL[0]
const dims = cur[1].split('×')
const ratio = dims.length === 2 && !missing ? dims[0] + ' / ' + dims[1] : '4 / 3'
const rows = list.map((v, i) => ({ n: v[0], dim: v[1], fmt: v[2], url: '/media/' + img + '?v=' + v[0], bg: sel === v[0] ? 'var(--ui-bg-elevated)' : 'var(--ui-bg)' }))
const resp = [['/media/photo.png?v=thumb', '200', 'The variant image. Generated on first request.'], ['/media/photo.png?v=unknown', '400', 'Unknown variant', '{ "status": 400, "error": "unknown_variant", "message": "Unknown variant", "detail": "unknown" }'], ['/media/photo.png?width=400', '400', 'Unsupported parameter', '{ "status": 400, "error": "unsupported_parameter", "message": "Unsupported parameter", "detail": "width" }'], ['/media/missing.png?v=thumb', '404', 'Not found', '']].map((r, i) => ({ req: r[0], st: r[1], res: r[2], body: r[3], bt: i ? '1px solid var(--ui-border)' : '0' }))
const hasVariants = list.length > 0
return {
gap: m ? '20px' : '24px', btn: m ? '44px' : '32px', inH: m ? '44px' : '32px', pad: m ? '16px' : '20px', bCols: m ? 'minmax(0,1fr)' : 'minmax(0,1fr) minmax(0,1fr)',
loading: st === 'loading', failed: st === 'error', showSnippets: st !== 'loading', noneAdmin: st === 'none' && admin, nonePlain: st === 'none' && !admin,
snippets, tab: s.tab || 'html', copied: !!s.copied, hasVariants, rows, resp, showResponses: st !== 'loading' && st !== 'error',
countText: list.length === 1 ? '1 enabled variant' : list.length + ' enabled variants', disabledNote: st === 'many' && !missing,
tblMin: '520px', tblMax: list.length > 6 ? '264px' : 'none', respMin: '560px',
image: img, vname: missing ? 'hero (removed)' : sel, vBd: missing ? 'var(--ui-warning)' : 'var(--ui-border-accented)',
urlSnips: [{ k: 'url', label: 'URL', code: u(missing ? 'hero' : sel) }], urlCopied: !!s.urlCopied, builtUrl: u(missing ? 'hero' : sel), urlCopyIcon: s.urlCopied ? 'check' : 'copy', urlCopyT: s.urlCopied ? 'Copied' : 'Copy', urlCopyFg: s.urlCopied ? 'var(--ui-success)' : 'var(--ui-text-highlighted)',
removed: missing, fallbackBody: '{ "status": 400, "error": "unknown_variant", "message": "Unknown variant", "detail": "hero" }',
previewLabel: missing ? 'No preview: unknown variant' : sel + ' · ' + cur[1] + ' · ' + cur[2], ratio, prevOp: missing ? 0.5 : 1
}
}
}
</script>
</body>
</html>