@nebularstreams/statify 3.0.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.
Files changed (193) hide show
  1. package/dist/.DS_Store +0 -0
  2. package/dist/website.simple/webfonts/fa-brands-400.woff2 +0 -0
  3. package/dist/website.simple/webfonts/fa-solid-900.eot +0 -0
  4. package/dist/website.simple/webfonts/fa-solid-900.svg +4649 -0
  5. package/dist/website.simple/webfonts/fa-solid-900.ttf +0 -0
  6. package/dist/website.simple/webfonts/fa-solid-900.woff +0 -0
  7. package/dist/website.simple/webfonts/fa-solid-900.woff2 +0 -0
  8. package/dist/website.simple/website.css +32 -0
  9. package/dist/website.simple/website.min.js +6 -0
  10. package/dist/website.software/webfonts/fa-brands-400.woff2 +0 -0
  11. package/dist/website.software/webfonts/fa-solid-900.eot +0 -0
  12. package/dist/website.software/webfonts/fa-solid-900.svg +4649 -0
  13. package/dist/website.software/webfonts/fa-solid-900.ttf +0 -0
  14. package/dist/website.software/webfonts/fa-solid-900.woff +0 -0
  15. package/dist/website.software/webfonts/fa-solid-900.woff2 +0 -0
  16. package/dist/website.software/website.css +32 -0
  17. package/dist/website.software/website.min.js +6 -0
  18. package/dist/website.worlds/webfonts/fa-brands-400.woff2 +0 -0
  19. package/dist/website.worlds/webfonts/fa-solid-900.eot +0 -0
  20. package/dist/website.worlds/webfonts/fa-solid-900.svg +4649 -0
  21. package/dist/website.worlds/webfonts/fa-solid-900.ttf +0 -0
  22. package/dist/website.worlds/webfonts/fa-solid-900.woff +0 -0
  23. package/dist/website.worlds/webfonts/fa-solid-900.woff2 +0 -0
  24. package/dist/website.worlds/website.css +32 -0
  25. package/dist/website.worlds/website.min.js +6 -0
  26. package/package.json +49 -0
  27. package/readme.md +105 -0
  28. package/src/publisher-lib.js +179 -0
  29. package/src/publisher-list.js +91 -0
  30. package/src/publisher.js +38 -0
  31. package/src/util/cdn/nobj.cdn.js +88 -0
  32. package/src/util/htmlroute.js +39 -0
  33. package/src/util/markdown/lib.showdown.icon.js +62 -0
  34. package/src/util/markdown/lib.showdown.yt.js +54 -0
  35. package/src/util/markdown/markdown.js +144 -0
  36. package/src/util/navibar/util.navibar.js +63 -0
  37. package/src/util/sha.js +20 -0
  38. package/src/util/tokenizer.js +234 -0
  39. package/src/util/widgets/NoticeWidget.js +30 -0
  40. package/src/util/widgets/app.widgets.js +70 -0
  41. package/src/util/widgets/util.definewidget.js +14 -0
  42. package/src/widgets/Cookies.js +55 -0
  43. package/src/widgets/Frameset-simple.js +114 -0
  44. package/src/widgets/Frameset.js +124 -0
  45. package/src/widgets/Page.js +356 -0
  46. package/src/widgets/RightSide.js +93 -0
  47. package/src/widgets/Snippets.js +75 -0
  48. package/src/widgets/Tail.js +11 -0
  49. package/src/widgets/Widgets.js +10 -0
  50. package/src/widgets.client/Client.js +22 -0
  51. package/src/widgets.features/FeatureList.js +364 -0
  52. package/src/widgets.features/Youtube.js +23 -0
  53. package/src/widgets.features/bootcol.js +29 -0
  54. package/src/widgets.features/css/feature.css +112 -0
  55. package/src/widgets.features/css/tail.css +4 -0
  56. package/src/widgets.howto/Faq.js +201 -0
  57. package/src/widgets.howto/HowTo.js +419 -0
  58. package/src/widgets.howto/Tiles.js +51 -0
  59. package/src/widgets.howto/script-contractable.js +31 -0
  60. package/src/widgets.markdown/Document.js +84 -0
  61. package/src/widgets.market/MarketItem.js +278 -0
  62. package/src/widgets.market/MarketItem.orig.js +268 -0
  63. package/src/widgets.market/MarketItem.strips.js +276 -0
  64. package/src/widgets.market/StoryLicenseWIdget.js +45 -0
  65. package/src/widgets.market/app.utils.js +238 -0
  66. package/src.website/css/app/animations.css +27 -0
  67. package/src.website/css/app/ismobile.css +7 -0
  68. package/src.website/css/app/style.css +51 -0
  69. package/src.website/css/app/style.fullheight.css +9 -0
  70. package/src.website/css/app/style.mdblocks.css +59 -0
  71. package/src.website/css/app/style.putosafari.css +3 -0
  72. package/src.website/css/bootstrap/bootstrap-tiny-core.css +47 -0
  73. package/src.website/css/bootstrap/bootstrap-tiny-media-columns.css +1276 -0
  74. package/src.website/css/bootstrap/bootstrap-tiny-media-custom.css +59 -0
  75. package/src.website/css/bootstrap/bootstrap-tiny-media-display.css +279 -0
  76. package/src.website/css/bootstrap/bootstrap-tiny-media-flex.css +881 -0
  77. package/src.website/css/bootstrap/bootstrap-tiny-media-fonts.css +39 -0
  78. package/src.website/css/bootstrap/bootstrap-tiny-media-paddings.css +1348 -0
  79. package/src.website/css/css-core/core.ext.css +65 -0
  80. package/src.website/css/css-core/frontpage.css +28 -0
  81. package/src.website/css/css-core/root.css +34 -0
  82. package/src.website/css/css-core/root.fullheight.css +12 -0
  83. package/src.website/css/css-core/ui.dialog.round.css +14 -0
  84. package/src.website/css/css-core/ui.navibar.css +28 -0
  85. package/src.website/css/css-core/ui.notifications.css +12 -0
  86. package/src.website/css/css-featurelist/feature.css +196 -0
  87. package/src.website/css/css-featurelist/tail.css +4 -0
  88. package/src.website/css/css-screens/doc.css +22 -0
  89. package/src.website/css/css-screens/screens-meta.css +3 -0
  90. package/src.website/css/css-unsplash/unsplash.css +32 -0
  91. package/src.website/css/css-videos/youtube.css +28 -0
  92. package/src.website/css/css-wiki/wiki.css +206 -0
  93. package/src.website/css/css-wiki/wiki.sidepane.css +16 -0
  94. package/src.website/css/fontawesome/fa-brands.css +18 -0
  95. package/src.website/css/fontawesome/fa-solid.css +16 -0
  96. package/src.website/css/fontawesome/fa.css +4359 -0
  97. package/src.website/css/skin/mierda.css +15 -0
  98. package/src.website/css/skin/skin.dark.css +85 -0
  99. package/src.website/css/skin/skin.light.css +27 -0
  100. package/src.website/css/skin/vars.font.css +8 -0
  101. package/src.website/css/skin/vars.sizes.css +4 -0
  102. package/src.website/package-lock.json +2160 -0
  103. package/src.website/package.json +28 -0
  104. package/src.website/public/webfonts/fa-brands-400.woff2 +0 -0
  105. package/src.website/public/webfonts/fa-solid-900.eot +0 -0
  106. package/src.website/public/webfonts/fa-solid-900.svg +4649 -0
  107. package/src.website/public/webfonts/fa-solid-900.ttf +0 -0
  108. package/src.website/public/webfonts/fa-solid-900.woff +0 -0
  109. package/src.website/public/webfonts/fa-solid-900.woff2 +0 -0
  110. package/src.website/rollup.simple.mjs +36 -0
  111. package/src.website/rollup.software.mjs +36 -0
  112. package/src.website/rollup.terser.mjs +24 -0
  113. package/src.website/rollup.worlds.mjs +36 -0
  114. package/src.website/src/app/app.style.mjs +36 -0
  115. package/src.website/src/app/index.mjs +118 -0
  116. package/src.website/src/config.js +8 -0
  117. package/src.website/src/core/cdnmodel.mjs +7 -0
  118. package/src.website/src/core/clientruntime.mjs +41 -0
  119. package/src.website/src/core.cookies/analytics.mjs +96 -0
  120. package/src.website/src/core.cookies/cookiemanager.mjs +128 -0
  121. package/src.website/src/core.cookies/css/cookies.css +80 -0
  122. package/src.website/src/core.cookies/css/nocookies.css +48 -0
  123. package/src.website/src/core.extra/css/contractable.css +12 -0
  124. package/src.website/src/core.extra/css/faq.css +42 -0
  125. package/src.website/src/core.extra/css/howto.css +81 -0
  126. package/src.website/src/core.extra/extra.document.mjs +83 -0
  127. package/src.website/src/core.extra/extra.faq.mjs +30 -0
  128. package/src.website/src/core.extra/extra.mjs +62 -0
  129. package/src.website/src/main.simple.mjs +4 -0
  130. package/src.website/src/main.software.mjs +10 -0
  131. package/src.website/src/main.worlds.mjs +9 -0
  132. package/src.website/src/support/ismobile.mjs +4 -0
  133. package/src.website/src/support/nonce.mjs +4 -0
  134. package/src.website/src/support/persistsettings.mjs +41 -0
  135. package/src.website/src/support/qrcode.mjs +1269 -0
  136. package/src.website/src/support/timeutils.mjs +237 -0
  137. package/src.website/src/widgets/index.mjs +22 -0
  138. package/src.website/src.widgets/widgets.market/index.mjs +11 -0
  139. package/src.website/src.widgets/widgets.market/widgets/market/DiscoverWidget.mjs +401 -0
  140. package/src.website/src.widgets/widgets.market/widgets/market/EmptyNew.mjs +28 -0
  141. package/src.website/src.widgets/widgets.market/widgets/market/MarketDetail.mjs +78 -0
  142. package/src.website/src.widgets/widgets.market/widgets/market/MarketItem.mjs +349 -0
  143. package/src.website/src.widgets/widgets.market/widgets/market/MarketWidget.mjs +437 -0
  144. package/src.website/src.widgets/widgets.market/widgets/market/StoryLicenseWidget.mjs +34 -0
  145. package/src.website/src.widgets/widgets.market/widgets/market/css/market.css +394 -0
  146. package/src.website/src.widgets/widgets.market/widgets/market/css/notice.css +6 -0
  147. package/src.website/src.widgets/widgets.market/widgets/navibar/NaviBarWidget.mjs +58 -0
  148. package/src.website/src.widgets/widgets.market/widgets/navibar/css/titlebar.css +68 -0
  149. package/src.website/src.widgets/widgets.market/widgets/navibar/css/ui.navibar.css +28 -0
  150. package/src.website/src.widgets/widgets.software/index.mjs +9 -0
  151. package/src.website/src.widgets/widgets.software/widgets/BootStrapColumnWidget.mjs +17 -0
  152. package/src.website/src.widgets/widgets.software/widgets/DownloadWidget.mjs +210 -0
  153. package/src.website/src.widgets/widgets.software/widgets/LicenseWidget.js +103 -0
  154. package/src.website/src.widgets/widgets.software/widgets/css/download/download.css +110 -0
  155. package/src.website/src.widgets/widgets.software/widgets/css/license/beta.css +28 -0
  156. package/src.website/src.widgets/widgets.software/widgets/css/license/button.css +30 -0
  157. package/src.website/src.widgets/widgets.software/widgets/css/license/front.css +46 -0
  158. package/src.website/src.widgets/widgets.software/widgets/css/license/licenses.css +13 -0
  159. package/src.website/src.widgets/widgets.software/widgets/css/license/overstate.css +15 -0
  160. package/src.website/src.widgets/widgets.software/widgets/css/license/story.css +8 -0
  161. package/src.website/src.widgets/widgets.software/widgets/css/license/toolbar.css +9 -0
  162. package/src.website/src.widgets/widgets.software/widgets/support/version.mjs +88 -0
  163. package/src.website/src.widgets/widgets.worlds/index.mjs +11 -0
  164. package/src.website/src.widgets/widgets.worlds/widgets/LocalBrowser.mjs +386 -0
  165. package/src.website/src.widgets/widgets.worlds/widgets/LocalLauncher.code.mjs +247 -0
  166. package/src.website/src.widgets/widgets.worlds/widgets/LocalLauncher.mjs +239 -0
  167. package/src.website/src.widgets/widgets.worlds/widgets/css/input.css +32 -0
  168. package/src.website/src.widgets/widgets.worlds/widgets/css/localprojects.css +188 -0
  169. package/templates/documentation/assets/docs.css +7 -0
  170. package/templates/documentation/assets/images/right.webp +0 -0
  171. package/templates/documentation/images.json +12 -0
  172. package/templates/documentation/metadata.json +120 -0
  173. package/templates/documentation/pages/cookies.md +25 -0
  174. package/templates/documentation/pages/index.md +13 -0
  175. package/templates/documentation/pages/privacy.md +50 -0
  176. package/templates/documentation/pages/tos.md +49 -0
  177. package/templates/landing/assets/res/carousel/image-1.webp +0 -0
  178. package/templates/landing/assets/res/carousel/image-2.webp +0 -0
  179. package/templates/landing/assets/res/carousel/image-3.webp +0 -0
  180. package/templates/landing/assets/res/custom.css +122 -0
  181. package/templates/landing/assets/res/logos/favicon.ico +0 -0
  182. package/templates/landing/assets/res/logos/favicon.png +0 -0
  183. package/templates/landing/assets/res/logos/logo.medallion.png +0 -0
  184. package/templates/landing/assets/res/logos/logo.white.png +0 -0
  185. package/templates/landing/assets/res/proj/banda-tech-client.webp +0 -0
  186. package/templates/landing/assets/res/proj/banda-tech-server.webp +0 -0
  187. package/templates/landing/assets/res/proj/proj-common-ground.webp +0 -0
  188. package/templates/landing/assets/res/proj/proj-fieldnote.webp +0 -0
  189. package/templates/landing/assets/res/proj/proj-tribes.webp +0 -0
  190. package/templates/landing/images.json +932 -0
  191. package/templates/landing/metadata.json +115 -0
  192. package/templates/landing/pages/index.json +268 -0
  193. package/tool/cli.js +183 -0
@@ -0,0 +1,115 @@
1
+ {
2
+ "title": "Brightfield Labs",
3
+ "description": "Thoughtful digital products for growing organizations",
4
+ "canonical": "https://example.com",
5
+ "image": {
6
+ "url": "https://example.com/res/nebular.color.png",
7
+ "alt": "Brightfield Labs"
8
+ },
9
+ "ogtype": "website",
10
+ "site": {
11
+ "name": "Brightfield Labs",
12
+ "url": "https://example.com",
13
+ "domain": "example.com",
14
+ "cookies": "none",
15
+ "nocookies": false,
16
+ "simplepages": true
17
+ },
18
+ "author": {
19
+ "@type": "Organization",
20
+ "name": "Brightfield Labs",
21
+ "url": "https://example.com",
22
+ "logo": "https://example.com/res/logos/logo.white.png"
23
+ },
24
+ "publisher": {
25
+ "@type": "Organization",
26
+ "name": "Brightfield Labs",
27
+ "url": "https://example.com",
28
+ "logo": "https://example.com/res/logos/logo.white.png"
29
+ },
30
+ "head": [
31
+ {
32
+ "t": "link",
33
+ "rel": "icon",
34
+ "type": "image/png",
35
+ "href": "res/logos/favicon.png"
36
+ },
37
+ {
38
+ "t": "link",
39
+ "rel": "stylesheet",
40
+ "href": "app/website.css"
41
+ },
42
+ {
43
+ "t": "link",
44
+ "rel": "stylesheet",
45
+ "href": "res/custom.css"
46
+ },
47
+ {
48
+ "t": "link",
49
+ "rel": "apple-touch-icon",
50
+ "href": "res/logos/favicon.png"
51
+ },
52
+ {
53
+ "t": "script",
54
+ "src": "app/website.min.js"
55
+ }
56
+ ],
57
+ "meta": [
58
+ {
59
+ "charSet": "utf-8"
60
+ },
61
+ {
62
+ "name": "robots",
63
+ "content": "all"
64
+ },
65
+ {
66
+ "lang": "en"
67
+ },
68
+ {
69
+ "name": "viewport",
70
+ "content": "width=device-width, maximum-scale=5, user-scalable=yes, initial-scale=1"
71
+ },
72
+ {
73
+ "name": "theme-color",
74
+ "content": "#111827"
75
+ },
76
+ {
77
+ "name": "mobile-web-app-capable",
78
+ "content": "yes"
79
+ },
80
+ {
81
+ "name": "apple-mobile-web-app-capable",
82
+ "content": "yes"
83
+ },
84
+ {
85
+ "name": "apple-mobile-web-app-status-bar-style",
86
+ "content": "black"
87
+ }
88
+ ],
89
+ "navibar": {
90
+ "home": "/",
91
+ "logo": "res/nebular.medallion.transparent.png",
92
+ "displayTitle": true,
93
+ "autoHide": true,
94
+ "links": {
95
+ "SPACER": {
96
+ "separator": true
97
+ },
98
+ "Documentation": {
99
+ "url": "/docs/index",
100
+ "icon": "book"
101
+ },
102
+ "Contact": {
103
+ "url": "mailto:hello@example.com",
104
+ "icon": "envelope"
105
+ }
106
+ }
107
+ },
108
+ "links.doc": {
109
+ "documents": {
110
+ "static": true,
111
+ "class": "inter"
112
+ }
113
+ },
114
+ "links.metadata": {}
115
+ }
@@ -0,0 +1,268 @@
1
+ {
2
+ "widget": "FeatureList",
3
+ "title": "Brightfield Labs",
4
+ "titleRaw": true,
5
+ "notitlebar": true,
6
+ "canonical": "https://example.com",
7
+ "class": "notoolbar",
8
+ "features": {
9
+ "mainClass": "text-al-left col-md-10 offset-md-1 col-xl-8 offset-xl-2 col-xxl-6 offset-xxl-3",
10
+ "containerClass": "pad-0",
11
+ "itemsClass": "pad-1",
12
+ "title": {
13
+ "containerClass": "fullrow back-one",
14
+ "class": "newbox",
15
+ "items": [
16
+ {
17
+ "image": "res/logos/logo.white.png",
18
+ "class": "col-6 mainlogo",
19
+ "imageClass": "w-100",
20
+ "alt": "Brightfield Labs logo placeholder",
21
+ "el": "div"
22
+ },
23
+ {
24
+ "el": "h1",
25
+ "text": "Thoughtful digital products<br>for growing organizations",
26
+ "class": "motto"
27
+ }
28
+ ]
29
+ },
30
+ "cards": [
31
+ {
32
+ "title": "About Brightfield Labs",
33
+ "containerClass": "bigpad back-zero",
34
+ "class": "flexrow wrap",
35
+ "items": [
36
+ {
37
+ "el": "h2",
38
+ "text": "From promising idea to dependable product",
39
+ "class": "accent"
40
+ },
41
+ {
42
+ "el": "div",
43
+ "class": "flexbreak"
44
+ },
45
+ {
46
+ "class": "col-12 col-md-offset-1 col-md-10",
47
+ "text": "Brightfield Labs is a fictional product studio used to demonstrate this Statify landing-page template. We help organizations clarify ideas, design useful experiences, and build software that is ready for everyday work."
48
+ },
49
+ {
50
+ "bootstrapClass": "col-lg-4 col-xxl-4",
51
+ "cardsClass": "pad-2",
52
+ "cards": [
53
+ {
54
+ "icon": "compass",
55
+ "iconClass": "icon-outline",
56
+ "subclass": "text-al-center pad-4",
57
+ "title": "Product Strategy",
58
+ "text": "Turn an open-ended challenge into a focused roadmap, clear priorities, and practical measures of success."
59
+ },
60
+ {
61
+ "icon": "pencil-ruler",
62
+ "iconClass": "icon-outline",
63
+ "subclass": "text-al-center pad-4",
64
+ "title": "Experience Design",
65
+ "text": "Shape accessible interfaces and workflows around the people who will use them every day."
66
+ },
67
+ {
68
+ "icon": "code",
69
+ "iconClass": "icon-outline",
70
+ "subclass": "text-al-center pad-4",
71
+ "title": "Engineering",
72
+ "text": "Build maintainable applications and services with a strong foundation for security, performance, and growth."
73
+ }
74
+ ]
75
+ }
76
+ ]
77
+ },
78
+ {
79
+ "title": "Selected Work",
80
+ "containerClass": "pad-0 back-one",
81
+ "class": "flexrow wrap",
82
+ "items": [
83
+ {
84
+ "el": "h2",
85
+ "text": "Selected Work",
86
+ "class": "w-100 accent heading text-al-center"
87
+ }
88
+ ]
89
+ },
90
+ {
91
+ "title": "Atlas Operations",
92
+ "containerClass": "pad-0 back-one",
93
+ "class": "flexrow wrap nearbox",
94
+ "items": [
95
+ {
96
+ "el": "h2",
97
+ "text": "Atlas Operations",
98
+ "class": "project-heading col-12"
99
+ },
100
+ {
101
+ "el": "h3",
102
+ "text": "A shared workspace for fast-moving teams",
103
+ "class": "accent col-12"
104
+ },
105
+ {
106
+ "class": "project-description",
107
+ "text": "Atlas brings planning, live status, and operational knowledge into one adaptable workspace. Teams can configure their own views while working from the same source of truth."
108
+ },
109
+ {
110
+ "imageCarousel": [
111
+ {"url": "res/carousel/image-1.webp"},
112
+ {"url": "res/carousel/image-2.webp"},
113
+ {"url": "res/carousel/image-3.webp"}
114
+ ],
115
+ "alt": "Atlas Operations interface examples",
116
+ "class": "col-12 asp169 marg-v marg-4",
117
+ "imageClass": "framed w-100 fadein"
118
+ },
119
+ {
120
+ "bootstrapClass": "col-lg-4 col-xxl-4",
121
+ "cardsClass": "pad-2",
122
+ "cards": [
123
+ {
124
+ "icon": "layer-group",
125
+ "iconClass": "icon-outline",
126
+ "subclass": "text-al-center pad-4",
127
+ "title": "Flexible Workspaces",
128
+ "text": "Reusable modules let each team organize information around its own responsibilities."
129
+ },
130
+ {
131
+ "icon": "bolt",
132
+ "iconClass": "icon-outline",
133
+ "subclass": "text-al-center pad-4",
134
+ "title": "Live Updates",
135
+ "text": "Changes appear as work happens, keeping decisions and status visible across the organization."
136
+ },
137
+ {
138
+ "icon": "shield-alt",
139
+ "iconClass": "icon-outline",
140
+ "subclass": "text-al-center pad-4",
141
+ "title": "Built for Trust",
142
+ "text": "Clear permissions, reliable history, and resilient infrastructure support critical workflows."
143
+ }
144
+ ]
145
+ }
146
+ ]
147
+ },
148
+ {
149
+ "title": "Fieldnote",
150
+ "containerClass": "pad-0 back-one",
151
+ "class": "flexrow wrap nearbox",
152
+ "items": [
153
+ {
154
+ "el": "h2",
155
+ "text": "Fieldnote",
156
+ "class": "project-heading col-12"
157
+ },
158
+ {
159
+ "el": "h3",
160
+ "text": "Simple reporting for teams away from the desk",
161
+ "class": "accent col-12"
162
+ },
163
+ {
164
+ "class": "project-description",
165
+ "text": "Fieldnote helps distributed teams capture observations, photos, and follow-up actions on any device. A streamlined review flow turns field reports into useful decisions."
166
+ },
167
+ {
168
+ "image": "res/proj/proj-fieldnote.webp",
169
+ "alt": "Fieldnote product interface placeholder"
170
+ }
171
+ ]
172
+ },
173
+ {
174
+ "title": "Common Ground",
175
+ "containerClass": "pad-0 back-one",
176
+ "class": "flexrow wrap nearbox",
177
+ "items": [
178
+ {
179
+ "el": "h2",
180
+ "text": "Common Ground",
181
+ "class": "project-heading col-12"
182
+ },
183
+ {
184
+ "class": "project-description",
185
+ "text": "Common Ground is a prototype for shared digital spaces where remote groups can meet, explore ideas, and work together in a more natural setting."
186
+ },
187
+ {
188
+ "image": "res/proj/proj-common-ground.webp",
189
+ "alt": "Common Ground collaborative environment placeholder"
190
+ }
191
+ ]
192
+ },
193
+ {
194
+ "title": "Experiments",
195
+ "containerClass": "pad-0 back-two",
196
+ "class": "flexrow wrap nearbox",
197
+ "items": [
198
+ {
199
+ "el": "h2",
200
+ "text": "Experiments",
201
+ "class": "col-12"
202
+ },
203
+ {
204
+ "class": "project-description",
205
+ "text": "Small experiments keep our practice curious. These placeholders show how a Statify page can mix a short introduction with a flexible image gallery."
206
+ },
207
+ {
208
+ "image": "res/proj/banda-tech-client.webp",
209
+ "alt": "Mobile product experiment placeholder"
210
+ },
211
+ {
212
+ "image": "res/proj/banda-tech-server.webp",
213
+ "alt": "Creative tool experiment placeholder"
214
+ }
215
+ ]
216
+ },
217
+ {
218
+ "title": "Work With Us",
219
+ "containerClass": "bigpad back-zero",
220
+ "class": "flexrow wrap nearbox",
221
+ "items": [
222
+ {
223
+ "el": "h2",
224
+ "text": "Have something useful to build?",
225
+ "class": "accent col-12"
226
+ },
227
+ {
228
+ "class": "project-description",
229
+ "text": "Tell us what you are trying to improve, who it is for, and where the idea stands today. We will help you find a practical next step."
230
+ },
231
+ {
232
+ "link": {
233
+ "href": "mailto:hello@example.com",
234
+ "text": "hello@example.com",
235
+ "class": "accent w-100"
236
+ }
237
+ }
238
+ ]
239
+ }
240
+ ]
241
+ },
242
+ "footer": {
243
+ "title": "Brightfield Labs",
244
+ "copyright": "Brightfield Labs &copy; 2026",
245
+ "items": [],
246
+ "links": [
247
+ {
248
+ "title": "Contact",
249
+ "href": "mailto:hello@example.com"
250
+ },
251
+ {
252
+ "title": "Cookies",
253
+ "href": "/docs/cookies",
254
+ "target": "doc"
255
+ },
256
+ {
257
+ "title": "Privacy",
258
+ "href": "/docs/privacy",
259
+ "target": "doc"
260
+ },
261
+ {
262
+ "title": "Terms",
263
+ "href": "/docs/tos",
264
+ "target": "doc"
265
+ }
266
+ ]
267
+ }
268
+ }
package/tool/cli.js ADDED
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict";
4
+
5
+ const fs = require("node:fs");
6
+ const path = require("node:path");
7
+ const { spawnSync } = require("node:child_process");
8
+ const publish = require("../src/publisher-lib");
9
+
10
+ const PACKAGE_ROOT = path.resolve(__dirname, "..");
11
+ const COMMANDS = new Set(["init", "app", "list", "folder", "file"]);
12
+
13
+ function fail(message, code = 1) {
14
+ const error = new Error(message);
15
+ error.exitCode = code;
16
+ throw error;
17
+ }
18
+
19
+ function usage() {
20
+ return `Usage:
21
+ statify init <destination> <template>
22
+ statify folder <source> [destination]
23
+ statify file <file> [destination] <source>
24
+ statify list <list-file> <source> [destination] [filter-id]
25
+ statify app [simple|software|worlds]`;
26
+ }
27
+
28
+ function existingDirectory(value, label) {
29
+ const result = path.resolve(value);
30
+ if (!fs.existsSync(result) || !fs.statSync(result).isDirectory()) {
31
+ fail(`${label} does not exist or is not a directory: ${value}`);
32
+ }
33
+ return result;
34
+ }
35
+
36
+ function availableTemplates() {
37
+ return fs.readdirSync(path.join(PACKAGE_ROOT, "templates"), { withFileTypes: true })
38
+ .filter((entry) => entry.isDirectory())
39
+ .map((entry) => entry.name)
40
+ .sort();
41
+ }
42
+
43
+ function init(destinationArg, templateName) {
44
+ if (!destinationArg || !templateName) {
45
+ fail(`Usage: statify init <destination> <template>\nAvailable templates: ${availableTemplates().join(", ")}`);
46
+ }
47
+
48
+ const destination = path.resolve(destinationArg);
49
+ const template = path.join(PACKAGE_ROOT, "templates", templateName);
50
+ if (fs.existsSync(destination)) fail(`Destination already exists: ${destinationArg}`);
51
+ if (!fs.existsSync(template) || !fs.statSync(template).isDirectory()) {
52
+ fail(`Unknown template: ${templateName}\nAvailable templates: ${availableTemplates().join(", ")}`);
53
+ }
54
+
55
+ fs.cpSync(template, destination, { recursive: true, errorOnExist: true });
56
+ console.log(`Initialized ${templateName} template in ${destination}`);
57
+ }
58
+
59
+ async function publishFile(filename, destination, source) {
60
+ const html = await publish(`./${filename.split(path.sep).join("/")}`, source);
61
+ const relativeOutput = filename.slice(0, -path.extname(filename).length) + ".html";
62
+ const output = path.join(destination, relativeOutput);
63
+ fs.mkdirSync(path.dirname(output), { recursive: true });
64
+ fs.writeFileSync(output, html, "utf8");
65
+ return output;
66
+ }
67
+
68
+ async function file(fileArg, destinationArg, sourceArg) {
69
+ if (fileArg && destinationArg && !sourceArg) {
70
+ sourceArg = destinationArg;
71
+ destinationArg = undefined;
72
+ }
73
+ if (!fileArg || !sourceArg) fail("Usage: statify file <file> [destination] <source>");
74
+
75
+ const source = existingDirectory(sourceArg, "Source");
76
+ const pages = path.join(source, "pages");
77
+ const absoluteFile = path.resolve(fileArg);
78
+ const filename = path.relative(pages, absoluteFile);
79
+ if (filename.startsWith("..") || path.isAbsolute(filename)) {
80
+ fail(`File must be inside ${pages}: ${fileArg}`);
81
+ }
82
+ if (!fs.existsSync(absoluteFile) || !fs.statSync(absoluteFile).isFile()) {
83
+ fail(`File does not exist: ${fileArg}`);
84
+ }
85
+ if (![".json", ".md"].includes(path.extname(absoluteFile).toLowerCase())) {
86
+ fail(`Page must be a .json or .md file: ${fileArg}`);
87
+ }
88
+
89
+ const destination = path.resolve(destinationArg || "out");
90
+ const output = await publishFile(filename, destination, source);
91
+ console.log(`Generated ${output}`);
92
+ }
93
+
94
+ function walk(directory, relative = "") {
95
+ return fs.readdirSync(directory, { withFileTypes: true })
96
+ .filter((entry) => !entry.name.startsWith("."))
97
+ .flatMap((entry) => {
98
+ const item = path.join(relative, entry.name);
99
+ return entry.isDirectory() ? walk(directory, item) : [item];
100
+ });
101
+ }
102
+
103
+ async function folder(sourceArg, destinationArg) {
104
+ if (!sourceArg) fail("Usage: statify folder <source> [destination]");
105
+
106
+ const source = existingDirectory(sourceArg, "Source");
107
+ const pages = existingDirectory(path.join(source, "pages"), "Pages directory");
108
+ const destination = path.resolve(destinationArg || "out");
109
+ fs.mkdirSync(destination, { recursive: true });
110
+
111
+ for (const filename of walk(pages)) {
112
+ const extension = path.extname(filename).toLowerCase();
113
+ if (extension === ".json" || extension === ".md") {
114
+ console.log(`Converting ${filename}`);
115
+ await publishFile(filename, destination, source);
116
+ } else {
117
+ console.log(`Copying ${filename}`);
118
+ const output = path.join(destination, filename);
119
+ fs.mkdirSync(path.dirname(output), { recursive: true });
120
+ fs.copyFileSync(path.join(pages, filename), output);
121
+ }
122
+ }
123
+
124
+ const assets = path.join(source, "assets");
125
+ if (fs.existsSync(assets) && fs.statSync(assets).isDirectory()) {
126
+ fs.cpSync(assets, destination, { recursive: true });
127
+ }
128
+ console.log(`Output written to ${destination}`);
129
+ }
130
+
131
+ function runNode(script, args, cwd = process.cwd()) {
132
+ const result = spawnSync(process.execPath, [script, ...args], { cwd, stdio: "inherit" });
133
+ if (result.error) fail(result.error.message);
134
+ if (result.status !== 0) fail(`Command failed with exit code ${result.status}`, result.status || 1);
135
+ }
136
+
137
+ function list(listFile, sourceArg, destinationArg, filterId) {
138
+ if (!listFile || !sourceArg) {
139
+ fail("Usage: statify list <list-file> <source> [destination] [filter-id]");
140
+ }
141
+
142
+ const source = existingDirectory(sourceArg, "Source");
143
+ const destination = path.resolve(destinationArg || "out");
144
+ fs.mkdirSync(destination, { recursive: true });
145
+ const args = [listFile, source, destination];
146
+ if (filterId) args.push(filterId);
147
+ return runNode(path.join(PACKAGE_ROOT, "src", "publisher-list.js"), args, path.join(source, "lists"));
148
+ }
149
+
150
+ function app(application = "simple") {
151
+ if (!new Set(["simple", "software", "worlds"]).has(application)) {
152
+ fail(`Unknown application: ${application}. Expected simple, software, or worlds.`);
153
+ }
154
+
155
+ const websiteRoot = path.join(PACKAGE_ROOT, "src.website");
156
+ const npm = process.platform === "win32" ? "npm.cmd" : "npm";
157
+ const result = spawnSync(npm, ["run", `build:${application}`], {
158
+ cwd: websiteRoot,
159
+ stdio: "inherit"
160
+ });
161
+ if (result.error) fail(result.error.message);
162
+ if (result.status !== 0) fail(`Application build failed with exit code ${result.status}`, result.status || 1);
163
+ }
164
+
165
+ function invocation(argv) {
166
+ if (COMMANDS.has(argv[0])) return [argv[0], argv.slice(1)];
167
+ fail(`Unknown command: ${argv[0]}\n\n${usage()}`);
168
+ }
169
+
170
+ async function main(argv) {
171
+ if (argv.includes("--help") || argv.includes("-h") || argv.length === 0) {
172
+ console.log(usage());
173
+ return;
174
+ }
175
+
176
+ const [command, args] = invocation(argv);
177
+ await { init, folder, file, list, app }[command](...args);
178
+ }
179
+
180
+ main(process.argv.slice(2)).catch((error) => {
181
+ console.error(`statify: ${error.message}`);
182
+ process.exitCode = error.exitCode || 1;
183
+ });