@typeroll/mcp-server 0.45.79 → 0.45.81

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.
@@ -0,0 +1,64 @@
1
+ """Convert a reviewed local original to an sRGB JPEG; never upload or change CMS content.
2
+ Requires Pillow with ImageCms. Preserve the untouched original in the source archive.
3
+ CMYK needs its actual embedded or supplied ICC profile. Never guess a CMYK profile.
4
+ Unprofiled RGB/gray requires --assume-srgb after review. Output must not exist.
5
+ """
6
+ import argparse
7
+ import hashlib
8
+ import io
9
+ import json
10
+ from pathlib import Path
11
+
12
+
13
+ def prepare(source, output, source_profile=None, assume_srgb=False, quality=95):
14
+ from PIL import Image, ImageCms, ImageOps
15
+ source, output = Path(source), Path(output)
16
+ if output.exists() or source.resolve() == output.resolve():
17
+ raise ValueError('Output already exists; choose a new derivative path.')
18
+ if output.suffix.lower() not in ('.jpg', '.jpeg'):
19
+ raise ValueError('Choose a .jpg or .jpeg output.')
20
+ if not 1 <= quality <= 100:
21
+ raise ValueError('JPEG quality must be from 1 to 100.')
22
+ with Image.open(source) as original:
23
+ if getattr(original, 'n_frames', 1) != 1:
24
+ raise ValueError('Multi-page/animated originals require an explicit frame decision.')
25
+ if 'A' in original.getbands() or 'transparency' in original.info:
26
+ raise ValueError('Transparent originals need a reviewed background or a PNG workflow.')
27
+ profile_bytes = Path(source_profile).read_bytes() if source_profile else original.info.get('icc_profile')
28
+ image = ImageOps.exif_transpose(original)
29
+ target = ImageCms.ImageCmsProfile(ImageCms.createProfile('sRGB'))
30
+ if profile_bytes:
31
+ profile = ImageCms.ImageCmsProfile(io.BytesIO(profile_bytes))
32
+ converted = ImageCms.profileToProfile(image, profile, target, outputMode='RGB')
33
+ elif image.mode in ('RGB', 'L') and assume_srgb:
34
+ converted = image.convert('RGB')
35
+ else:
36
+ raise ValueError('Missing source ICC profile. CMYK cannot be safely guessed; supply the correct --source-profile. For reviewed unprofiled RGB/gray only, use --assume-srgb.')
37
+ # No source EXIF/GPS/caption metadata is copied into the public derivative.
38
+ with output.open('xb') as destination:
39
+ converted.save(destination, format='JPEG', quality=quality, subsampling=0, icc_profile=target.tobytes())
40
+ with Image.open(output) as check:
41
+ check.load()
42
+ if check.mode != 'RGB' or not check.info.get('icc_profile'):
43
+ raise ValueError('Output verification failed; do not upload the derivative.')
44
+ dimensions = list(check.size)
45
+ return {'source_sha256': hashlib.sha256(source.read_bytes()).hexdigest(),
46
+ 'output_sha256': hashlib.sha256(output.read_bytes()).hexdigest(),
47
+ 'width': dimensions[0], 'height': dimensions[1], 'colour_space': 'sRGB',
48
+ 'source_profile': 'supplied' if source_profile else 'embedded' if profile_bytes else 'assumed sRGB',
49
+ 'uploaded': False}
50
+
51
+
52
+ def main():
53
+ parser = argparse.ArgumentParser(description=__doc__)
54
+ parser.add_argument('source', type=Path)
55
+ parser.add_argument('output', type=Path)
56
+ parser.add_argument('--source-profile', type=Path)
57
+ parser.add_argument('--assume-srgb', action='store_true')
58
+ parser.add_argument('--quality', type=int, default=95)
59
+ args = parser.parse_args()
60
+ print(json.dumps(prepare(args.source, args.output, args.source_profile, args.assume_srgb, args.quality), indent=2))
61
+
62
+
63
+ if __name__ == '__main__':
64
+ main()
@@ -28,3 +28,38 @@ Record deliberate departures in decisions.md with scope and owner approval.
28
28
  Use a QA round per review: method, pages/viewports, expected differences, results,
29
29
  unverified items and remaining defects. Record platform findings in
30
30
  qa/typeroll-platform-feedback.md; do not bury them in workaround CSS.
31
+
32
+ ## Site tokens before block overrides
33
+
34
+ Core 0.2.106 / template capabilities 0.54.0 adds palette names in all colour
35
+ fields and named typography/spacing steps. Check the connected capability
36
+ version; on older renderers use `var(--color-surface)` rather than a bare name.
37
+
38
+ Set colours and font families in site settings, then use named styles and
39
+ palette names in blocks. Do not write hex, rgb or font-family literals in block
40
+ content. Prefer `primary`, `secondary`, `accent`, `background`, `surface`, `text`,
41
+ `text_light`, and `on_primary`/`on_secondary`/`on_accent` for text on brand colours.
42
+ Use `surface_hover` for a darker interactive surface (derived automatically
43
+ unless explicitly configured). Existing semantic roles should be reused; a
44
+ new visual shade is not automatically a new semantic role. For a derived shade
45
+ use `color-mix(in srgb, var(--color-surface) 92%, var(--color-primary))`.
46
+ For an additional role not in the native palette, define a named site style
47
+ or a site CSS variable derived from existing palette variables, and document it.
48
+
49
+ Choose `heading`, `body` or `mono` for fonts. Font families belong only in the
50
+ site's `fonts` settings. Use type-size steps `xs`, `sm`, `md`, `lg`, `xl`, `2xl`,
51
+ `3xl`; spacing `none`, `xs`, `sm`, `md`, `lg`, `xl`; line height `tight`, `snug`,
52
+ `normal`, `relaxed`, `loose`. Presentation fields accept responsive maps of these
53
+ names, for example `font_size_px: {mobile: "md", desktop: "lg"}`. Read the field
54
+ schema: counts, widths and breakpoints are not spacing steps. Pixel values are
55
+ explicit exceptions, not the default authoring method. Configure scales in
56
+ `design_scales`, then reuse them in role styles and blocks.
57
+
58
+ Record deliberate exceptions in `decisions.md`, with source/block, reason and
59
+ review criteria (for example white text on a photograph). Run `lint_design_tokens`
60
+ before launch and after major design changes. It warns, never blocks launch.
61
+ Use the starter's `scripts/typeroll/color_tokens.py` for an exact-match dry run;
62
+ review the plan before applying it in a dedicated Version. Equal hex values can
63
+ mean different roles: never automatically choose between background and text.
64
+ Verify a primary/surface change in a test Version across header, footer, cards,
65
+ menus and buttons, then restore the test settings. Do not republish automatically.
@@ -82,3 +82,48 @@ Page/partial block writes update working copies; check `saved` and use
82
82
  `commit_working_copy` with `target: {kind: "page"|"partial", id: "..."}`.
83
83
  Page templates save immediately and do not have working copies. REST read envelopes and
84
84
  MCP tool text are different layers; `call.py` returns the decoded tool result.
85
+
86
+ ## Design token check
87
+
88
+ Follow workflows/design.md for palette/font roles and named scales. Run
89
+ `lint_design_tokens` before launch and after major design changes. Review exact
90
+ colour replacements with scripts/typeroll/color_tokens.py; record deliberate
91
+ exceptions in decisions.md. Preserve text independently from design changes.
92
+
93
+ ## Source retirement, analytics and domain preparation
94
+
95
+ Before old hosting is retired, follow workflows/source-media.md: preserve unused
96
+ and print originals in a private archive with independently verified checksums.
97
+ Only approved web assets enter the CMS. Use ICC-aware conversion for CMYK/TIFF.
98
+
99
+ Enable Analytics through the authorized Typeroll API/MCP (`update_app` or the
100
+ Organization analytics tools), then read back settings and verify the beacon and
101
+ cookies on the actual artifact. Resolve missing provider scopes through the
102
+ normal approval flow. A separately authorized provider-side setup with a supplied
103
+ beacon token is an alternative, not a way to bypass Organization permissions.
104
+ Never expose a provider API token as a public beacon token.
105
+
106
+ From Core 0.2.106, saving future hosts does not redirect normal publication.
107
+ Keep using `trigger_deploy` to update and review the current/temporary address.
108
+ Read `next_publication` in `read_publishing_domains` for the destination.
109
+ Shortly before DNS changes, deliberately call `prepare_publishing_domain_change`
110
+ with the current revision. It prepares the last successful public snapshot,
111
+ not later unpublished CMS edits. Review its frozen indexing settings and DNS
112
+ requirements, then approve traffic separately. If another normal publication
113
+ succeeds while the candidate waits, prepare again: an outdated snapshot cannot
114
+ replace newer published content. There is no need to clear saved future hosts.
115
+ To correct candidate indexing, save the intended setting, publish normally, and
116
+ prepare again. Temporary-host protection keeps the review address noindex even
117
+ when the published authored setting permits indexing on the future host.
118
+ Keep MX/SPF and unrelated DNS unchanged; review any validation window and obtain
119
+ the already-defined traffic-switch authorization.
120
+
121
+ ## Owner-supplied facts and photos
122
+
123
+ Record owner statements, source and confirmation date. Preserve approved copy
124
+ under `text: preserve`; do not silently delete dates/prices or rewrite it into
125
+ "timeless" language. Under `text: renew`, propose removing stale details or using
126
+ recurring-event wording only when a source supports recurrence. Flag uncertain
127
+ facts for review or keep new Pages in draft; missing evidence does not prove that
128
+ an event stopped. Link to the actual organiser when relevant. Upload approved
129
+ photos with SHA-256 verification and review focal points at mobile/desktop widths.
@@ -15,3 +15,10 @@ Do not publish, change DNS or send messages without explicit approval.
15
15
 
16
16
  The CMS owns current content and settings. The repository owns the brief, rules,
17
17
  decisions and evidence. Load recipes when needed, not the whole manual.
18
+
19
+ ## Design token check
20
+
21
+ Follow workflows/design.md for palette/font roles and named scales. Run
22
+ `lint_design_tokens` before launch and after major design changes. Review exact
23
+ colour replacements with scripts/typeroll/color_tokens.py; record deliberate
24
+ exceptions in decisions.md. Preserve text independently from design changes.
@@ -0,0 +1,50 @@
1
+ # Preserve source media before retirement
2
+
3
+ This is a separate owner-approved archive, not an import of every old file into
4
+ the CMS. Do not delete source files or cancel old hosting as part of this routine.
5
+
6
+ 1. Inventory originals and downloads from the source API/export, including files
7
+ absent from published pages. Include drafts, private records and historical
8
+ attachments when authorized. A public sitemap or REST response cannot prove
9
+ the library is complete. Keep the export under ignored `sources/private/`.
10
+ 2. Compare with the new site's references, including templates, partials and
11
+ retained URLs. Record used, unused and uncertain files. "Unused" is not an
12
+ instruction to delete. Preserve print originals even when a web derivative exists.
13
+ 3. Agree a private archive destination, its owning account, retention and access.
14
+ Verify account/bucket identity and that public bucket/custom-domain access is
15
+ disabled. Use the owner's credentials through their secret manager; never put
16
+ credentials in scripts or manifests. Do not provision or expose a bucket silently.
17
+ 4. Copy with bounded concurrency and bounded reads, stream large files and use
18
+ resumable transfers when supported. Preserve a collision-safe key derived from
19
+ the full source host/path, not just the filename. Never overwrite a different
20
+ object merely because the basename matches.
21
+ 5. Record source URL, destination key, source ID, byte count, SHA-256, media type,
22
+ title/alt/caption, copy time and verification state in a private manifest.
23
+ ETag is not universally MD5 (multipart objects and providers differ). Use a
24
+ documented checksum API or stream the destination back and compare SHA-256
25
+ and size. Metadata claiming a hash is not proof of the uploaded bytes.
26
+ 6. Resume only verified matches; report every unavailable source and hash conflict.
27
+ Verify a restore from the archive, including a large file. Keep an independent
28
+ private copy of the manifest. The owner reviews completeness and grants a
29
+ separate old-host retirement decision. This archive does not preserve old
30
+ public URLs by itself: keep URL parity/redirect acceptance separate.
31
+
32
+ ## Prepare print images for the web
33
+
34
+ Use `scripts/typeroll/prepare_image.py` for a reviewed local TIFF or other original.
35
+ Install Pillow with ImageCms in an isolated environment. It applies EXIF
36
+ orientation, converts through the embedded (or explicitly supplied) ICC profile
37
+ to sRGB, embeds the sRGB profile, strips source EXIF metadata and writes a new
38
+ JPEG. It never changes the original, uploads files or edits a Page.
39
+
40
+ ```sh
41
+ python3 scripts/typeroll/prepare_image.py sources/private/original.tif sources/private/web.jpg
42
+ ```
43
+
44
+ CMYK without the correct profile fails; obtain the actual source profile instead
45
+ of guessing. `--source-profile sources/private/original.icc` supplies it.
46
+ Unprofiled RGB/gray requires an explicit `--assume-srgb` decision. Multi-page,
47
+ animated and transparent images require a separate reviewed workflow. Compare
48
+ colour, subject framing and sharpness visually before upload. Upload through the
49
+ normal integrity-checked media flow and choose alt/caption text independently.
50
+ Store the tool's hash report privately; never commit customer images or archives.