shipseal 0.1.0 → 0.1.1

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.
package/README.md CHANGED
@@ -49,7 +49,7 @@ jobs:
49
49
  steps:
50
50
  - uses: actions/checkout@v7
51
51
  with: { fetch-depth: 0 }
52
- - uses: akii09/shipseal@v0.1.0
52
+ - uses: akii09/shipseal@v0.1.1
53
53
  env: { GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" }
54
54
  ```
55
55
 
package/dist/cli.js CHANGED
@@ -409,8 +409,22 @@ function firstSentence(text) {
409
409
  * sentences, and the fitting pass comfortably handles more.
410
410
  */
411
411
  const HEADLINE_MAX_CHARS = 72;
412
+ /**
413
+ * Emoji ranges, removed from anything that reaches a card.
414
+ *
415
+ * No font we register carries these glyphs, so `\u{1F310} Human-friendly ...` (got's real
416
+ * GitHub description) rendered a tofu box on shipseal.dev. Takumi ships an emoji helper, but it
417
+ * rewrites each emoji into an <img> pointing at jsDelivr, and `generate()` does no network.
418
+ *
419
+ * The ranges deliberately start at U+2600, which keeps the textual symbols people put in real
420
+ * product names: (c) U+00A9, (r) U+00AE and (tm) U+2122 all sit below it.
421
+ */
422
+ const EMOJI = /[\u{1F000}-\u{1FAFF}]|[\u{2600}-\u{27BF}]|[\u{FE00}-\u{FE0F}]|\u200D/gu;
423
+ function stripEmoji(text) {
424
+ return text.replaceAll(EMOJI, "").replace(/\s+/g, " ").trim();
425
+ }
412
426
  function cleanLine(text) {
413
- const stripped = text.replace(/\s*\u2014\s*/g, ": ").replace(/\s+\u2013\s+/g, ", ").replaceAll("–", "-").replaceAll("!", ".").replace(/\s+/g, " ").trim();
427
+ const stripped = stripEmoji(text).replace(/\s*\u2014\s*/g, ": ").replace(/\s+\u2013\s+/g, ", ").replaceAll("–", "-").replaceAll("!", ".").replace(/\s+/g, " ").trim();
414
428
  const noTrail = stripped.endsWith(".") ? stripped.slice(0, -1) : stripped;
415
429
  const first = noTrail.at(0);
416
430
  if (first === void 0) return noTrail;
@@ -1820,7 +1834,7 @@ const storyPage = {
1820
1834
  title: {
1821
1835
  maxLines: tall ? 3 : 2,
1822
1836
  maxFontSize: tall ? 64 : 48,
1823
- minFontSize: 28,
1837
+ minFontSize: tall ? 36 : 30,
1824
1838
  step: 2,
1825
1839
  box: {
1826
1840
  width: width - 32,
@@ -1877,7 +1891,7 @@ const storyPage = {
1877
1891
  render(raw, ctx) {
1878
1892
  const props = propsSchema.parse(raw);
1879
1893
  const colors = themeColors(ctx.brand, ctx.theme);
1880
- const { tall, titleHeight, bodyHeight, width } = geometry(ctx.format);
1894
+ const { tall, bodyHeight, width } = geometry(ctx.format);
1881
1895
  const editorial = ctx.brand.style === "editorial";
1882
1896
  const terminal = ctx.brand.style === "terminal";
1883
1897
  const code = isCommand(props.page);
@@ -1911,6 +1925,7 @@ const storyPage = {
1911
1925
  justifyContent: "space-between",
1912
1926
  alignItems: "center",
1913
1927
  height: HEADER_HEIGHT,
1928
+ flexShrink: 0,
1914
1929
  borderBottom: terminal ? `1px solid ${colors.muted}` : NO_BORDER,
1915
1930
  paddingBottom: terminal ? 12 : 0
1916
1931
  },
@@ -1943,83 +1958,91 @@ const storyPage = {
1943
1958
  children: ctx.fitted.version?.text ?? props.version
1944
1959
  })]
1945
1960
  }),
1946
- /* @__PURE__ */ jsx("div", {
1961
+ /* @__PURE__ */ jsxs("div", {
1947
1962
  style: {
1948
1963
  display: "flex",
1949
- alignItems: "center",
1950
- height: titleHeight,
1951
- flexShrink: 0,
1952
- paddingLeft: editorial ? 24 : 0,
1953
- borderLeft: editorial ? `8px solid ${colors.primary}` : NO_BORDER
1964
+ flexDirection: "column",
1965
+ flexGrow: 1,
1966
+ justifyContent: "center",
1967
+ gap: 24,
1968
+ minHeight: 0
1954
1969
  },
1955
- children: /* @__PURE__ */ jsx("div", {
1970
+ children: [/* @__PURE__ */ jsx("div", {
1956
1971
  style: {
1957
- fontFamily: props.heading,
1958
- fontWeight: props.weight,
1959
- fontSize: title?.fontSize ?? 48,
1960
- lineHeight: 1.2,
1961
- maxWidth: width - (editorial ? 32 : 0)
1972
+ display: "flex",
1973
+ alignItems: "center",
1974
+ flexShrink: 0,
1975
+ paddingLeft: editorial ? 24 : 0,
1976
+ borderLeft: editorial ? `8px solid ${colors.primary}` : NO_BORDER
1962
1977
  },
1963
- children: title?.text ?? props.page.title.value
1964
- })
1965
- }),
1966
- comparison ? /* @__PURE__ */ jsx("div", {
1967
- style: {
1968
- display: "flex",
1969
- flexDirection: tall ? "column" : "row",
1970
- gap: 16,
1971
- height: bodyHeight
1972
- },
1973
- children: ["before", "after"].map((label) => /* @__PURE__ */ jsxs("div", {
1978
+ children: /* @__PURE__ */ jsx("div", {
1979
+ style: {
1980
+ fontFamily: props.heading,
1981
+ fontWeight: props.weight,
1982
+ fontSize: title?.fontSize ?? 48,
1983
+ lineHeight: 1.2,
1984
+ maxWidth: width - (editorial ? 32 : 0)
1985
+ },
1986
+ children: title?.text ?? props.page.title.value
1987
+ })
1988
+ }), comparison ? /* @__PURE__ */ jsx("div", {
1974
1989
  style: {
1975
1990
  display: "flex",
1976
- flexDirection: "column",
1977
- gap: 8,
1978
- width: paneWidth,
1979
- height: paneHeight
1991
+ flexDirection: tall ? "column" : "row",
1992
+ gap: 16,
1993
+ height: bodyHeight
1980
1994
  },
1981
- children: [/* @__PURE__ */ jsx("div", {
1995
+ children: ["before", "after"].map((label) => /* @__PURE__ */ jsxs("div", {
1982
1996
  style: {
1983
- fontFamily: props.body,
1984
- fontSize: 20,
1985
- color: colors.muted
1997
+ display: "flex",
1998
+ flexDirection: "column",
1999
+ gap: 8,
2000
+ width: paneWidth,
2001
+ height: paneHeight
1986
2002
  },
1987
- children: label === "before" ? "Before" : "After"
1988
- }), /* @__PURE__ */ jsx("img", {
1989
- src: `story-${label}`,
1990
- width: paneWidth,
1991
- height: paneHeight - HEADER_HEIGHT,
1992
- style: { objectFit: "contain" }
1993
- })]
1994
- }, label))
1995
- }) : hasBody ? /* @__PURE__ */ jsx("div", {
1996
- style: {
1997
- display: "flex",
1998
- flexDirection: "column",
1999
- justifyContent: "flex-start",
2000
- height: bodyHeight,
2001
- padding: BODY_PAD,
2002
- backgroundColor: terminal || code ? colors.card : "transparent",
2003
- borderRadius: terminal ? 0 : ctx.brand.radius,
2004
- borderLeft: terminal ? `3px solid ${colors.primary}` : NO_BORDER
2005
- },
2006
- children: /* @__PURE__ */ jsx("div", {
2003
+ children: [/* @__PURE__ */ jsx("div", {
2004
+ style: {
2005
+ fontFamily: props.body,
2006
+ fontSize: 20,
2007
+ color: colors.muted
2008
+ },
2009
+ children: label === "before" ? "Before" : "After"
2010
+ }), /* @__PURE__ */ jsx("img", {
2011
+ src: `story-${label}`,
2012
+ width: paneWidth,
2013
+ height: paneHeight - HEADER_HEIGHT,
2014
+ style: { objectFit: "contain" }
2015
+ })]
2016
+ }, label))
2017
+ }) : hasBody ? /* @__PURE__ */ jsx("div", {
2007
2018
  style: {
2008
- fontFamily: code ? props.mono : props.body,
2009
- fontWeight: 400,
2010
- fontSize: bodyFontSize,
2011
- lineHeight: code ? CODE_LINE_HEIGHT : 1.2,
2012
- whiteSpace: code ? "pre" : "normal",
2013
- color: colors.foreground
2019
+ display: "flex",
2020
+ flexDirection: "column",
2021
+ justifyContent: "flex-start",
2022
+ maxHeight: bodyHeight,
2023
+ padding: terminal || code ? BODY_PAD : 0,
2024
+ backgroundColor: terminal || code ? colors.card : "transparent",
2025
+ borderRadius: terminal ? 0 : ctx.brand.radius,
2026
+ borderLeft: terminal ? `3px solid ${colors.primary}` : NO_BORDER
2014
2027
  },
2015
- children: bodyText
2016
- })
2017
- }) : void 0,
2028
+ children: /* @__PURE__ */ jsx("div", {
2029
+ style: {
2030
+ fontFamily: code ? props.mono : props.body,
2031
+ fontWeight: 400,
2032
+ fontSize: bodyFontSize,
2033
+ lineHeight: code ? CODE_LINE_HEIGHT : 1.2,
2034
+ whiteSpace: code ? "pre" : "normal",
2035
+ color: colors.foreground
2036
+ },
2037
+ children: bodyText
2038
+ })
2039
+ }) : void 0]
2040
+ }),
2018
2041
  /* @__PURE__ */ jsxs("div", {
2019
2042
  style: {
2020
2043
  display: "flex",
2021
2044
  justifyContent: "space-between",
2022
- marginTop: "auto",
2045
+ flexShrink: 0,
2023
2046
  fontFamily: props.mono,
2024
2047
  fontWeight: 400,
2025
2048
  fontSize: 16,
@@ -2408,7 +2431,7 @@ async function collectReadme(cwd) {
2408
2431
  ref: "README.md H1",
2409
2432
  fetchedAt
2410
2433
  });
2411
- if (tagline !== void 0) project.tagline = fact(tagline, {
2434
+ if (tagline !== void 0) project.tagline = fact(stripEmoji(tagline), {
2412
2435
  source: "readme",
2413
2436
  ref: "README.md first paragraph",
2414
2437
  fetchedAt
@@ -2887,7 +2910,7 @@ async function collectGithub(options) {
2887
2910
  };
2888
2911
  if (repo.description !== void 0 && repo.description !== null && repo.description.length > 0) out.project = {
2889
2912
  ...out.project,
2890
- tagline: fact(repo.description, {
2913
+ tagline: fact(stripEmoji(repo.description), {
2891
2914
  source: "github-api",
2892
2915
  ref: `GET /repos/${slug.owner}/${slug.repo} description`,
2893
2916
  fetchedAt
@@ -3083,7 +3106,7 @@ async function collectPackageJson(cwd, packagePath = "package.json") {
3083
3106
  ref: rawName === void 0 ? `${packagePath} directory name` : `${packagePath}#name`,
3084
3107
  fetchedAt
3085
3108
  }) };
3086
- if (pkg.data.description !== void 0 && pkg.data.description.length > 0) project.tagline = fact(pkg.data.description, {
3109
+ if (pkg.data.description !== void 0 && pkg.data.description.length > 0) project.tagline = fact(stripEmoji(pkg.data.description), {
3087
3110
  source: "package-json",
3088
3111
  ref: `${packagePath}#description`,
3089
3112
  fetchedAt
@@ -5596,10 +5619,10 @@ function overlayReleaseConfig(config, flags) {
5596
5619
  //#region src/core/story.ts
5597
5620
  const DEFAULT_CHANGE_PAGES = 4;
5598
5621
  const STORY_FORMATS$1 = "portrait, square, og, github-social, x, linkedin, or producthunt";
5599
- function storyFacts(facts, config, name, generatedAt) {
5622
+ function storyFacts(facts, config, brand, generatedAt) {
5600
5623
  const release = facts.release;
5601
5624
  if (release === void 0) throw new ShipsealError("story.no-release", "No release facts are available for a story.", "Choose a published release or pass --tag to a local repository.");
5602
- const copy = deterministicCopy(facts, config.release?.maxHighlights, name);
5625
+ const copy = deterministicCopy(facts, config.release?.maxHighlights, brand.name);
5603
5626
  const configured = (value, ref) => fact(value, {
5604
5627
  source: "user-config",
5605
5628
  ref,
@@ -5614,7 +5637,11 @@ function storyFacts(facts, config, name, generatedAt) {
5614
5637
  const pages = [{
5615
5638
  kind: "cover",
5616
5639
  title: fact(copy.headline, headlineSource.provenance),
5617
- body: release.subheadline ?? facts.project.tagline ?? fact("Release notes", release.tag.provenance)
5640
+ body: release.subheadline ?? facts.project.tagline ?? (brand.tagline === void 0 ? fact("Release notes", release.tag.provenance) : fact(brand.tagline, {
5641
+ source: "user-config",
5642
+ ref: "brand.json#tagline",
5643
+ fetchedAt: generatedAt
5644
+ }))
5618
5645
  }];
5619
5646
  for (const item of changes.slice(0, config.release?.maxHighlights ?? DEFAULT_CHANGE_PAGES)) {
5620
5647
  const collapsed = item.value.replaceAll(/\s+/g, " ").trim();
@@ -5657,7 +5684,7 @@ function storyFacts(facts, config, name, generatedAt) {
5657
5684
  };
5658
5685
  }
5659
5686
  async function generateStory(input) {
5660
- const facts = storyFacts(input.facts, input.config, input.brand.name, input.generatedAt);
5687
+ const facts = storyFacts(input.facts, input.config, input.brand, input.generatedAt);
5661
5688
  const pages = facts.story ?? [];
5662
5689
  const result = {
5663
5690
  files: [],