kireji 0.8.0 → 0.8.2

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
@@ -1,7 +1,7 @@
1
1
  # Kireji - *Web Framework*
2
2
  > **<sub>Part of the Kireji Project</sub>**<br><sup><i>omnia ex una linea</i></sup>
3
3
 
4
- The **Kireji Web Framework** is a reactive full-stack web framework that uses the MPHF Coordinate System and the MVC paradigm to build multi-origin web app ecosystems. It offers a routing system that achieves the information-theoretic lower bound of data compression, enabling comprehensive deep linking, session bookmarking without user accounts or local storage, peer-to-peer data sharing without uploads or accounts, and cross-origin communication without cookies or CORS.
4
+ The **Kireji Web Framework** is a reactive full-stack web framework that can build and serve web apps and multi-origin web app ecosystems. It stores its runtime state in its routing system, achieving the information-theoretic lower bound of data compression. This enables comprehensive deep linking, session bookmarking without user accounts or local storage, peer-to-peer data sharing without uploads or accounts, and cross-origin communication without cookies or CORS.
5
5
  ## The Kireji Project
6
6
  The Kireji Project poses a question: **What if we could treat every web page as a point in a unified, mathematically mapped space?**
7
7
 
@@ -13,7 +13,7 @@ The Kireji Project poses a question: **What if we could treat every web page as
13
13
 
14
14
  ## Usage
15
15
 
16
- > <sub>Note: This framework is currently under heavy development in early alpha. Expect large, breaking changes with each version and some awkward features. Check out the [Demo App Ecosystem](https://github.com/kireji-app/demo#readme) to see the framework in action. Check back later to see if the package has been updated.</sub>
16
+ > <sub>Note: This framework is currently in alpha. Expect breaking changes with each version. Check out the [Demo App Ecosystem](https://github.com/kireji-app/demo#readme) to see a live project that uses this framework. Check back later to see if the framework has advanced into beta.</sub>
17
17
 
18
18
  ```bash
19
19
  # In an empty git repo.
@@ -104,8 +104,8 @@ The Kireji Project is in **early research and development**.
104
104
 
105
105
  [![kireji on npm](https://img.shields.io/npm/v/kireji?style=for-the-badge&labelColor=CB3837&logo=npm&logoColor=white&label=NPM+package&color=212121)](https://www.npmjs.com/kireji)
106
106
  <br>[![Project Status: Alpha](https://img.shields.io/badge/status-alpha-212121?style=for-the-badge&labelColor=181717&logo=github&logoColor=white)](https://www.repostatus.org/#alpha)
107
- <br>[![Commits](https://img.shields.io/github/commit-activity/t/kireji-app/kireji?style=for-the-badge&labelColor=181717&color=212121&logo=github&logoColor=white)](https://github.com/kireji-app/demo/commits/)
108
- <br>[![Last Commit](https://img.shields.io/github/last-commit/kireji-app/kireji?style=for-the-badge&labelColor=181717&color=212121&logo=github&logoColor=white)](https://github.com/kireji-app/demo)
107
+ <br>[![Commits](https://img.shields.io/github/commit-activity/t/kireji-app/kireji?style=for-the-badge&labelColor=181717&color=212121&logo=github&logoColor=white)](https://github.com/kireji-app/kireji/commits/)
108
+ <br>[![Last Commit](https://img.shields.io/github/last-commit/kireji-app/kireji?style=for-the-badge&labelColor=181717&color=212121&logo=github&logoColor=white)](https://github.com/kireji-app/kireji)
109
109
  <br>[![Copyright © 2023-2025 <a href="https://www.ejaugust.com">Eric Augustinowicz</a>](https://img.shields.io/badge/2023%20--%202025-Eric_Augustinowicz-212121?labelColor=007ec6&style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTk3cHgiIGhlaWdodD0iMTk3cHgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmVyc2lvbj0iMS4xIj4KIDxkZWZzPgogIDxtYXNrIGlkPSJtYXNrIj4KICAgPGNpcmNsZSBjeD0iOTgiIGN5PSI5OCIgcj0iOTgiIGZpbGw9IndoaXRlIiAvPgogICA8Y2lyY2xlIGN4PSI5OCIgY3k9Ijk4IiByPSI3OCIgZmlsbD0iYmxhY2siIC8+CiAgIDxjaXJjbGUgY3g9Ijk4IiBjeT0iOTgiIHI9IjU1IiBmaWxsPSJ3aGl0ZSIgLz4KICAgPGNpcmNsZSBjeD0iOTgiIGN5PSI5OCIgcj0iMzAiIGZpbGw9ImJsYWNrIiAvPgogICA8cmVjdCB4PSIxMTUiIHk9Ijg1IiB3aWR0aD0iNDUiIGhlaWdodD0iMjUiIGZpbGw9ImJsYWNrIiAvPgogIDwvbWFzaz4KIDwvZGVmcz4KIDxwYXRoIGQ9Ik0gOTgsMCBBIDk4LDk4IDAgMSAxIDk4LDE5NiBBIDk4LDk4IDAgMSAxIDk4LDAgWiIgZmlsbD0id2hpdGUiIG1hc2s9InVybCgjbWFzaykiIC8+Cjwvc3ZnPg==)](http://www.ejaugust.com/)
110
110
  <br>[![Released under MIT License](https://img.shields.io/badge/License-MIT-212121?labelColor=007ec6&style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDEyIDEyIj48cGF0aCBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTp3aGl0ZTtzdHJva2Utd2lkdGg6Ljk5OTc1MDAyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjYuNDAwMDAwMTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIgZD0iTTQuMjUgOS41SDZNNC4yNSA3Ljc1SDZtMi4wMjMtNS41YzAgLjY5LS45MDQgMS4yNS0yLjAyMyAxLjI1LTEuMTE5IDAtMi4wMjItLjU1OS0yLjAyMi0xLjI1QzMuOTc4IDEuNTU4IDQuODggMSA2IDFjMS4xMTkgMCAyLjAyMy41NTggMi4wMjMgMS4yNXpNNiAxMVYzLjUiLz48L3N2Zz4=&logoColor=white)](https://github.com/kireji-app/kireji/LICENSE.md)
111
111
  <br>[![Sponsor this Project](https://img.shields.io/badge/Sponsor-212121?labelColor=red&style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OTciIGhlaWdodD0iNDcwIj48cGF0aCBkPSJNMTQwIDIwQzczIDIwIDIwIDc0IDIwIDE0MGMwIDEzNSAxMzYgMTcwIDIyOCAzMDMgODgtMTMyIDIyOS0xNzMgMjI5LTMwMyAwLTY2LTU0LTEyMC0xMjAtMTIwLTQ4IDAtOTAgMjgtMTA5IDY5LTE5LTQxLTYwLTY5LTEwOC02OXoiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iNDAiIGZpbGw9Im5vbmUiLz48L3N2Zz4=&logoColor=white)](https://github.com/sponsors/EJAugust)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kireji",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "A web framework for stateful, entropy-perfect, multi-origin web applications. Currently in alpha. Expect breaking changes for version 0. Use with caution!",
5
5
  "files": [
6
6
  "src/",
@@ -1,11 +1,14 @@
1
1
  const targetLocation = (+_.local ? `http://${HOST}.localhost:${_.port}` : `https://${HOST}`) + encodePathname(_.routeID)
2
2
  document.body.classList.add("unhydrated")
3
3
 
4
- // Press the button before navigation starts.
5
- const instanceIndex = desktop.windows.instances.findIndex(window => window.application.host === HOST)
6
- if (instanceIndex !== -1) {
7
- const task = document.querySelectorAll(`task-bar>button.task`)[instanceIndex]
8
- task.classList.add("preview-pressed", "pressed")
4
+ if (_.includeDesktop === "demo" || (!production && (_.includeDesktop === "full" || _.includeDesktop === 'local-only'))) {
5
+
6
+ // Press the task-bar button before navigation starts.
7
+ const instanceIndex = desktop.windows.instances.findIndex(window => window.application.host === HOST)
8
+ if (instanceIndex !== -1) {
9
+ const task = document.querySelectorAll(`task-bar>button.task`)[instanceIndex]
10
+ task.classList.add("preview-pressed", "pressed")
11
+ }
9
12
  }
10
13
 
11
14
  location = targetLocation
package/src/build.js CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+
3
+ /** @type {(_: IEcosystem, compressedSubjectOrigins: string) => void} */
2
4
  function ƒ(_, compressedSubjectOrigins) {
3
5
 
4
6
  // Initialization
@@ -520,7 +522,7 @@ function ƒ(_, compressedSubjectOrigins) {
520
522
  packAndMap(url) {
521
523
  const sourceFile = this
522
524
  const script = sourceFile.lines.join("\n")
523
- return +_.mapping
525
+ return _.mapping === "enabled"
524
526
  ? script +
525
527
  `
526
528
  //${"#"} sourceMappingURL=data:application/json;charset=utf-8;base64,${btoaUnicode(sourceFile.getMap())}${url
@@ -591,7 +593,6 @@ function ƒ(_, compressedSubjectOrigins) {
591
593
  imageSources = [],
592
594
  earlyImageSources = [],
593
595
  partsByHost = {},
594
- preHydrationArchive = serialize(_),
595
596
  resolveRelativeHost = (relativeHost, base) => {
596
597
 
597
598
  if (typeof relativeHost !== "string")
@@ -891,8 +892,74 @@ function ƒ(_, compressedSubjectOrigins) {
891
892
  part.inheritors.sort((a, b) => (b.totalInheritors - a.totalInheritors) || (a.host > b.host ? 1 : -1))
892
893
 
893
894
  return totalInheritors
895
+ },
896
+ configureArchiveContent = () => {
897
+ // TODO: Consider merging user landing model with stock landing model instead of replacing it.
898
+ const landingModel = JSON.parse(_["landing-model.json"])
899
+
900
+ let desktopFeatures = 3
901
+
902
+ if (_.includeColor === "none") {
903
+ delete _.parts.desktop.color
904
+ delete landingModel.parts.desktop.color
905
+ desktopFeatures--
906
+ } else if (_.includeColor === "dark" || (_.includeColor === "debug-light" && production)) {
907
+ delete _.parts.desktop.color.light
908
+ landingModel.parts.desktop.color = "light"
909
+ } else if (_.includeColor === "light" || (_.includeColor === "debug-dark" && production)) {
910
+ delete _.parts.desktop.color.dark
911
+ landingModel.parts.desktop.color = "dark"
912
+ }
913
+
914
+ if (_.includeEra === "none") {
915
+ delete _.parts.desktop.era
916
+ delete landingModel.parts.desktop.era
917
+ desktopFeatures--
918
+ } else if (_.includeEra === "vintage" || (_.includeEra === "debug-modern" && production)) {
919
+ delete _.parts.desktop.era.modern
920
+ landingModel.parts.desktop.era = "vintage"
921
+ } else if (_.includeEra === "modern" || (_.includeEra === "debug-vintage" && production)) {
922
+ delete _.parts.desktop.era.vintage
923
+ landingModel.parts.desktop.era = "modern"
924
+ }
925
+
926
+ if (_.includeKirejiApp === "none" || (_.includeKirejiApp === "full" && production)) {
927
+ delete _.app
928
+ delete landingModel.app
929
+ }
930
+
931
+ const removeDesktopFeatures = () => {
932
+ delete _.parts.desktop.icons
933
+ delete landingModel.parts.desktop.icons
934
+ delete _.parts.desktop.windows
935
+ delete landingModel.parts.desktop.windows
936
+ delete _.parts.desktop["task-bar"].tray
937
+ }
938
+
939
+ if (_.includeDesktop === "none" || (production && _.includeDesktop === "local-only")) {
940
+ removeDesktopFeatures()
941
+ delete _.parts.desktop["task-bar"]
942
+ delete landingModel.parts.desktop.taskBar
943
+ desktopFeatures--
944
+ } else if (_.includeDesktop === "menu-only" || (production && _.includeDesktop === "full")) {
945
+ removeDesktopFeatures()
946
+ delete _.parts.desktop["task-bar"].tray
947
+ }
948
+
949
+ if (!desktopFeatures) {
950
+ delete _.parts.desktop
951
+ delete landingModel.parts.desktop
952
+ }
953
+
954
+ _["landing-model.json"] = JSON.stringify(landingModel)
955
+
956
+ Object.defineProperties(_, {
957
+ landingModel: { value: landingModel },
958
+ preHydrationArchive: { value: serialize(_) }
959
+ })
894
960
  }
895
961
 
962
+ configureArchiveContent()
896
963
  hydratePartsRecursive(_)
897
964
  hydrateSubjectOrigins()
898
965
  countAndSortInheritorsRecursive(_.parts.abstract.part)
@@ -954,14 +1021,15 @@ function ƒ(_, compressedSubjectOrigins) {
954
1021
 
955
1022
  logScope(1, "\nComputing Landing Hash & Route ID", hashLog => {
956
1023
 
957
- const landingModel = JSON.parse(_["landing-model.json"])
958
- const landingRouteID = _.modelToRouteID(landingModel)
959
- const landingHash = encodeSegment(landingRouteID)
960
-
961
1024
  _.define({
962
- landingHash: { value: landingHash },
963
- landingModel: { value: landingModel },
964
- landingRouteID: { value: landingRouteID },
1025
+ landingRouteID: {
1026
+ value: _.modelToRouteID(_.landingModel)
1027
+ },
1028
+ landingHash: {
1029
+ resolve() {
1030
+ return encodeSegment(this.landingRouteID)
1031
+ }
1032
+ }
965
1033
  })
966
1034
 
967
1035
  hashLog("Landing hash: " + _.landingHash)
@@ -982,13 +1050,20 @@ function ƒ(_, compressedSubjectOrigins) {
982
1050
  }
983
1051
 
984
1052
  ƒ({
1053
+ // TODO: fix source mapping bugs.
985
1054
  verbosity: "1",
986
- // TODO: Fix source mapping bugs.
987
- mapping: "0",
988
1055
  change: "major",
989
1056
  hangHydration: "0",
990
- haltHydration: "0",
991
1057
  defaultApplicationHost: "kireji.app",
992
1058
  port: "3000",
993
- showWarning: "1"
1059
+ mapping: "disabled",
1060
+ resetLocalState: "enabled",
1061
+ haltHydration: "disabled",
1062
+ includeWarning: "enabled",
1063
+ includeColor: "full",
1064
+ includeEra: "full",
1065
+ includeMenuApps: "full",
1066
+ includeUpdates: "full",
1067
+ includeKirejiApp: "full",
1068
+ includeDesktop: "full"
994
1069
  })
@@ -0,0 +1,205 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "KirejiConfig",
4
+ "description": "Schema for the kireji.json configuration file.",
5
+ "type": "object",
6
+ "properties": {
7
+ "includeColor": {
8
+ "description": "Determines which color mode features will be included in the ecosystem.",
9
+ "oneOf": [
10
+ {
11
+ "const": "none",
12
+ "description": "The color component will never be included."
13
+ },
14
+ {
15
+ "const": "light",
16
+ "description": "Dark mode will never be included."
17
+ },
18
+ {
19
+ "const": "dark",
20
+ "description": "Light mode will never be included."
21
+ },
22
+ {
23
+ "const": "debug-dark",
24
+ "description": "Dark mode will only be included in local builds."
25
+ },
26
+ {
27
+ "const": "debug-light",
28
+ "description": "Light mode will only be included in local builds."
29
+ },
30
+ {
31
+ "const": "full",
32
+ "description": "The full color component will always be included."
33
+ }
34
+ ]
35
+ },
36
+ "includeEra": {
37
+ "description": "Determines which era mode features will be included in the ecosystem.",
38
+ "oneOf": [
39
+ {
40
+ "const": "none",
41
+ "description": "The era component will never be included."
42
+ },
43
+ {
44
+ "const": "vintage",
45
+ "description": "Modern mode will never be included."
46
+ },
47
+ {
48
+ "const": "modern",
49
+ "description": "Vintage mode will never be included."
50
+ },
51
+ {
52
+ "const": "debug-modern",
53
+ "description": "Modern mode will only be included in local builds."
54
+ },
55
+ {
56
+ "const": "debug-vintage",
57
+ "description": "Vintage mode will only be included in local builds."
58
+ },
59
+ {
60
+ "const": "full",
61
+ "description": "The full era component will always be included."
62
+ }
63
+ ]
64
+ },
65
+ "includeMenuApps": {
66
+ "description": "Determines whether a list of apps should be included in the menu.",
67
+ "oneOf": [
68
+ {
69
+ "const": "none",
70
+ "description": "Apps will never appear in the menu."
71
+ },
72
+ {
73
+ "const": "local-only",
74
+ "description": "Apps will only appear in the menu in local builds."
75
+ },
76
+ {
77
+ "const": "full",
78
+ "description": "Apps will always appear in the menu."
79
+ }
80
+ ]
81
+ },
82
+ "includeUpdates": {
83
+ "description": "Determines whether or not the version updating features should be included in the menu.",
84
+ "oneOf": [
85
+ {
86
+ "const": "none",
87
+ "description": "Update features will never appear in the menu."
88
+ },
89
+ {
90
+ "const": "local-only",
91
+ "description": "Update features will only appear in the menu in local builds."
92
+ },
93
+ {
94
+ "const": "full",
95
+ "description": "Update features will always appear in the menu."
96
+ }
97
+ ]
98
+ },
99
+ "includeKirejiApp": {
100
+ "description": "Determines whether or not the Kireji IDE should be included in the ecosystem.",
101
+ "oneOf": [
102
+ {
103
+ "const": "none",
104
+ "description": "kireji.app will never be included."
105
+ },
106
+ {
107
+ "const": "full",
108
+ "description": "kireji.app will only be included in local builds."
109
+ },
110
+ {
111
+ "const": "demo",
112
+ "description": "kireji.app will always be included (used only for Demo App Ecosystem)."
113
+ }
114
+ ]
115
+ },
116
+ "includeDesktop": {
117
+ "description": "Determines whether or not the ecosystem should include desktop-like features.",
118
+ "oneOf": [
119
+ {
120
+ "const": "none",
121
+ "description": "The desktop experience will never be included."
122
+ },
123
+ {
124
+ "const": "menu-only",
125
+ "description": "Only the menu will be included, and it will always be included."
126
+ },
127
+ {
128
+ "const": "local-only",
129
+ "description": "The full desktop experience will only be included in local builds."
130
+ },
131
+ {
132
+ "const": "full",
133
+ "description": "The menu will always be included. The desktop experience will only be included in local builds."
134
+ },
135
+ {
136
+ "const": "demo",
137
+ "description": "The full desktop experience will always be included (used only for Demo App Ecosystem)."
138
+ }
139
+ ]
140
+ },
141
+ "change": {
142
+ "description": "Represents the severity of the API change for semantic versioning.",
143
+ "oneOf": [
144
+ {
145
+ "const": "major",
146
+ "description": "A breaking change which breaks existing hash assignments."
147
+ },
148
+ {
149
+ "const": "minor",
150
+ "description": "A non-breaking change which adds new hash assignments."
151
+ },
152
+ {
153
+ "const": "patch",
154
+ "description": "A non-breaking change which does not impact hash assignments at all."
155
+ }
156
+ ]
157
+ },
158
+ "verbosity": {
159
+ "description": "A number used to control the detail in logs. Only messages with a priority <= this number will be logged.",
160
+ "type": "string"
161
+ },
162
+ "defaultApplicationHost": {
163
+ "description": "The host of the desired default app. The server will redirect to this when testing locally.",
164
+ "type": "string"
165
+ },
166
+ "port": {
167
+ "description": "The internal port (typically 3000-4000) where the server will be hosted.",
168
+ "type": "string"
169
+ },
170
+ "mapping": {
171
+ "description": "Determines whether or not the artifact should be output with embedded source map data.",
172
+ "enum": [
173
+ "disabled",
174
+ "enabled"
175
+ ]
176
+ },
177
+ "haltHydration": {
178
+ "description": "If enabled, halts the hydration of the application completely. Useful for debugging FOUC.",
179
+ "enum": [
180
+ "disabled",
181
+ "enabled"
182
+ ]
183
+ },
184
+ "includeWarning": {
185
+ "description": "If enabled, a warning line appears at the top of all applications regarding alpha status.",
186
+ "enum": [
187
+ "disabled",
188
+ "enabled"
189
+ ]
190
+ },
191
+ "resetLocalState": {
192
+ "description": "If enabled, applications running locally will reset to their landing hash when the service worker is replaced.",
193
+ "enum": [
194
+ "disabled",
195
+ "enabled"
196
+ ]
197
+ },
198
+ "hangHydration": {
199
+ "description": "A string representing how long the main thread should hang to simulate loading. Useful for debugging FOUC.",
200
+ "type": "string"
201
+ }
202
+ },
203
+ "required": [],
204
+ "additionalProperties": false
205
+ }
package/src/part.css_.js CHANGED
@@ -1,8 +1,14 @@
1
+ const
2
+ includeWarning = _.includeWarning === "enabled",
3
+ includeDesktop = _.includeDesktop === "demo" || (!production && (_.includeDesktop === "full" || _.includeDesktop === "local-only"))
4
+
1
5
  return /* css */`
2
6
 
3
7
  html, body {
4
- --warning-height: ${+_.showWarning ? "26px" : "0px"};
5
- }${+_.showWarning ? `
8
+ --warning-height: ${includeWarning ? "26px" : "0px"};
9
+ ${includeDesktop ? "" : `--title-bar-height: 0px !important;
10
+ --task-bar-height: 0px !important;`}
11
+ }${includeWarning ? `
6
12
 
7
13
  @media (width < 390px) {
8
14
 
package/src/part.html_.js CHANGED
@@ -1,6 +1,18 @@
1
1
  const
2
2
  title = sanitizeAttr(application.title ?? "Untitled App"),
3
- icon = application.placeholderImage("part.png")
3
+ icon = application.placeholderImage("part.png"),
4
+ classes = [...application.classes],
5
+ includeDesktop = _.includeDesktop === "demo" || (!production && (_.includeDesktop === "full" || _.includeDesktop === "local-only")),
6
+ includeMenu = includeDesktop || _.includeDesktop === "menu-only" || _.includeDesktop === "full"
7
+
8
+ if (_.includeEra !== "none")
9
+ classes.push(era.arm.key)
10
+
11
+ if (_.includeColor !== 'none')
12
+ classes.push(color.arm.key)
13
+
14
+ if (includeMenu)
15
+ classes.push(taskBar.menu.classes)
4
16
 
5
17
  return _.injectImages(/* html */`<!DOCTYPE html>
6
18
  <html lang=en>
@@ -15,25 +27,25 @@ return _.injectImages(/* html */`<!DOCTYPE html>
15
27
  <link rel="canonical" href="https://${application.host}${application.canonicalPathname ?? "/"}" />
16
28
  <!-- <link rel="manifest"${worker.manifestLink}/> -->
17
29
  <style id="user-css">${_["part.css"]}</style>
18
- <style id="era-css">${era["part.css"]}</style>
19
- <style id="color-css">${color["part.css"]}</style>
30
+ ${_.includeEra === "none" ? "" : /*html*/`<style id="era-css">${era["part.css"]}</style>`}
31
+ ${_.includeColor === "none" ? "" : /*html*/`<style id="color-css">${color["part.css"]}</style>`}
20
32
  <style id="application-css">${application["part.css"]}</style>
21
33
  </head>
22
- <body class="unhydrated ${[era.arm.key, color.arm.key, ...taskBar.menu.classes, ...application.classes].join(" ")}">
23
- ${+_.showWarning ? `<warning->🚧 App in Alpha. Features subject to change/break without notice.</warning->` : ""}
24
- <title-bar autofocus tabIndex=0>
34
+ <body class="unhydrated ${classes.join(" ")}">
35
+ ${_.includeWarning === "enabled" ? /*html*/`<warning->🚧 App in Alpha. Features subject to change/break without notice.</warning->` : ""}
36
+ ${includeDesktop ? `<title-bar autofocus tabIndex=0>
25
37
  <img class="part-icon" src="${icon}"/>
26
38
  <span id=application-title>${title}</span>
27
39
  <flex-spacer></flex-spacer>
28
40
  <button class=hide ${windows.pointAttr("hidePoint")}></button>
29
41
  <button class=restore ${windows.pointAttr("restorePoint")} disabled></button>
30
42
  <button class=close ${windows.pointAttr("closePoint")}></button>
31
- </title-bar>
43
+ </title-bar>` : ""}
32
44
  <wallpaper- class=app-container id=${application.host.replaceAll(".", "_")} tabIndex=0 ${application.attributes.join(" ")}>
33
45
  ${application["part.html"]}
34
46
  </wallpaper->
35
47
  <!-- windows -->
36
- ${taskBar["part.html"]}
48
+ ${includeMenu ? taskBar["part.html"] : ""}
37
49
  ${worker["part.html"]}
38
50
  </body>
39
51
  </html>`)
@@ -1,3 +1,3 @@
1
1
  {
2
- "extends": "coming-soon"
2
+ "extends": "mix"
3
3
  }
@@ -4,7 +4,7 @@ const rangeLimit = BigInt(Math.trunc(Number(scrollerLimit) * (1 - scroller.conta
4
4
 
5
5
  pointer.handle({
6
6
  drag(pointerEvent) {
7
- const positionalRouteID = markedRouteID + BigInt(Math.trunc((pointerEvent.clientY - POINTER_EVENT.clientY) / (scroller.scrollBar.clientHeight - (era.arm === era.vintage ? 2 * scroller.scrollBar.clientWidth : 0)) * Number(scrollerLimit)))
7
+ const positionalRouteID = markedRouteID + BigInt(Math.trunc((pointerEvent.clientY - POINTER_EVENT.clientY) / (scroller.scrollBar.clientHeight - (era && era.arm === era.vintage ? 2 * scroller.scrollBar.clientWidth : 0)) * Number(scrollerLimit)))
8
8
  const newRouteID = positionalRouteID < 0n ? 0n : (positionalRouteID > rangeLimit) ? rangeLimit : positionalRouteID
9
9
  if (newRouteID !== scroller.routeID) scroller.setRouteID(newRouteID)
10
10
  },
@@ -6,7 +6,7 @@ await Promise.all([
6
6
  if (!production) {
7
7
 
8
8
  // To debug FOUC.
9
- if (+_.haltHydration) {
9
+ if (_.haltHydration === "enabled") {
10
10
  warn('Intentionally blocked hydration.')
11
11
  return
12
12
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- "extends": "coming-soon"
2
+ "extends": "mix"
3
3
  }
@@ -107,7 +107,7 @@ logScope(1, `\nCreating Deployment Artifact`, log => {
107
107
  })
108
108
 
109
109
  logScope(1, `\nDeployment Artifact Stats`, () => {
110
- logStringSize(1, preHydrationArchive)
110
+ logStringSize(1, _.preHydrationArchive)
111
111
  })
112
112
 
113
113
  const httpServer = require('http').createServer((request, response) => logServerScope(
@@ -19,7 +19,7 @@ await logScope(1, 'Ensuring ServiceWorker Controller', async log => {
19
19
 
20
20
  if (!production) {
21
21
  nav.serviceWorker.oncontrollerchange = () => {
22
- if (+_.resetLocalState) {
22
+ if (_.resetLocalState === "enabled") {
23
23
  /* Reset to the landing hash on service worker update (useful during
24
24
  development that changes the part arrangement frequently. */
25
25
  location.assign(location.origin + `/${_.version}/`)
@@ -3,13 +3,17 @@ body {
3
3
  --bottom-shadow: 0 2px 7px #0002;
4
4
  --icon-size: 29px;
5
5
  --spacing: 14px;
6
- --task-bar-height: calc(var(--icon-size) + 2 * var(--spacing));
7
6
  --title-bar-icon-size: 22px;
8
7
  --title-bar-height: calc(var(--title-bar-icon-size) + var(--spacing));
9
8
  --default-font-size: 13px;
10
9
  font: var(--default-font-size) var(--system-ui);
11
10
  }
12
11
 
12
+ body,
13
+ task-bar {
14
+ --task-bar-height: calc(var(--icon-size) + 2 * var(--spacing));
15
+ }
16
+
13
17
  title-bar {
14
18
  --icon-size: var(--title-bar-icon-size);
15
19
  box-shadow: inset 0 0 0 1px var(--bg-un-mode);
@@ -172,7 +172,6 @@ scroll-bar>thumb- {
172
172
  body {
173
173
  --bottom: calc(var(--task-bar-height) - 4px);
174
174
  --spacing: 12px;
175
- --task-bar-height: 28px;
176
175
  --title-bar-height: 18px;
177
176
  --icon-size: var(--task-bar-height);
178
177
  --deep-inset:
@@ -194,6 +193,11 @@ body {
194
193
  image-rendering: pixelated;
195
194
  }
196
195
 
196
+ body,
197
+ task-bar {
198
+ --task-bar-height: 28px;
199
+ }
200
+
197
201
  body.dark {
198
202
  --deep-inset:
199
203
  inset -1px -1px var(--bg-light-est),
@@ -1,18 +1,29 @@
1
- return `<task-menu style="${menu.arm.style}">
2
- <ul id=application-control>${Object.entries(_.menuApplications).map(([host, application]) => {
3
- const isCurrentApplication = application === _.application
4
- return `
1
+ const
2
+ controls = [],
3
+ sections = []
4
+
5
+ if (_.includeUpdates === "full" || (!production && _.includeUpdates === "local-only"))
6
+ controls.push(update["part.html"])
7
+
8
+ if (_.includeColor === "full" || (!production && _.includeColor.startsWith("debug-")))
9
+ controls.push(color["part.html"])
10
+
11
+ if (_.includeEra === "full" || (!production && _.includeEra.startsWith("debug-")))
12
+ controls.push(era["part.html"])
13
+
14
+ if (_.includeMenuApps === "full" || (!production && _.includeMenuApps === "local-only"))
15
+ sections.push(`<ul id=application-control>${Object.entries(_.menuApplications).map(([host, application]) => {
16
+ const isCurrentApplication = application === _.application
17
+ return `
5
18
  <li class=task-link${isCurrentApplication ? ` data-here` : ""}>
6
19
  <a ${isCurrentApplication ? "" : _.pointAttr()} href=https://${host}>
7
20
  <img src="${application.placeholderImage("part.png")}" class=part-icon />
8
21
  <span class=label>${application.titleMenu ?? application.title}</span>
9
22
  </a>
10
23
  </li>`
11
- }).join("")}</ul>
12
- <hr>
13
- <section id="settings">
14
- ${update["part.html"]}
15
- ${color["part.html"]}
16
- ${era["part.html"]}
17
- </section>
18
- </task-menu>`
24
+ }).join("")}</ul>`)
25
+
26
+ if (controls.length)
27
+ sections.push(`\n <hr>\n <section id="settings">${controls.join(`\n `)}</section>`)
28
+
29
+ return `<task-menu style="${menu.arm.style}">${sections.join(`\n `)}</task-menu>`
@@ -1 +1,3 @@
1
- return `<task-bar tabIndex=0>${taskBar.menu["part.html"]}${windows.instances.map((window, index) => windows.renderTaskHTML(window, index)).join("")}<flex-spacer></flex-spacer>${taskBar.tray["part.html"]}</task-bar>`
1
+ const includeDesktop = _.includeDesktop === "demo" || (!production && (_.includeDesktop === "full" || _.includeDesktop === "local-only"))
2
+
3
+ return `<task-bar tabIndex=0>${taskBar.menu["part.html"]}${includeDesktop ? windows.instances.map((window, index) => windows.renderTaskHTML(window, index)).join("") : ""}<flex-spacer></flex-spacer>${includeDesktop ? taskBar.tray["part.html"] : ""}</task-bar>`
@@ -5,7 +5,7 @@ client.promise.then(() => {
5
5
  if (taskBar.menu.arm !== taskBar.menu.closed)
6
6
  Q("task-bar>button.menu").focus()
7
7
  else
8
- Q("title-bar").focus()
8
+ Q("title-bar")?.focus()
9
9
 
10
10
  document.addEventListener('pointerdown', pointerEvent => {
11
11
  if (taskBar.menu.arm !== taskBar.menu.closed && !inRect(pointerEvent, taskBar.menu.element.getBoundingClientRect())) {
@@ -2,11 +2,11 @@ declare interface IDesktop
2
2
  extends IPartsApplication {
3
3
 
4
4
  // Subparts.
5
- readonly color: IColor
6
- readonly era: IEra
7
- readonly taskBar: ITaskBar
8
- readonly icons: IDesktopIcons
9
- readonly windows: IDesktopWindows
5
+ readonly color?: IColor
6
+ readonly era?: IEra
7
+ readonly taskBar?: ITaskBar
8
+ readonly icons?: IDesktopIcons
9
+ readonly windows?: IDesktopWindows
10
10
  }
11
11
 
12
12
  declare const desktop: IDesktop
@@ -3,7 +3,7 @@ pointer.handle({
3
3
  const window = windows.instances[INSTANCE_INDEX]
4
4
  if (_.application === window.application) {
5
5
  const focusElement = Q(`body>:not(task-bar):focus-within, body>task-bar>button.task.pressed:focus`)
6
- if (focusElement && desktop.era.arm === desktop.era.vintage)
6
+ if (focusElement && desktop.era && desktop.era.arm === desktop.era.vintage)
7
7
  _.gotoApplication("desktop.parts")
8
8
  else TARGET_ELEMENT.focus()
9
9
  } else _.gotoApplication(window.application.host)
package/src/static.css CHANGED
@@ -301,6 +301,7 @@ body.dark warning- {
301
301
  font-size: revert;
302
302
  }
303
303
 
304
+ /* TODO: Make title-bar and other desktop experience css dynamic (because it can be disabled in the config). */
304
305
  title-bar {
305
306
  display: flex;
306
307
  flex-flow: row nowrap;
package/src/type.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  declare interface IEcosystem
2
2
  extends IMix<null, ITopLevelDomainAny>,
3
+ IKirejiConfig,
3
4
  IWebComponent {
4
5
 
5
6
  // Subparts.
@@ -10,10 +11,6 @@ declare interface IEcosystem
10
11
  readonly parts: IParts
11
12
 
12
13
  // Serialized Properties.
13
- /** One of three strings representing the severity of the API change. Used to automatically compute the correct semantic version at build time. */
14
- readonly "change": "major" | "minor" | "patch"
15
- /** A number used to control the detail in logs. Only messages with a priority less than or equal to this number will be logged. */
16
- readonly "verbosity": number
17
14
  /** The git branch for the current build version. */
18
15
  readonly "branch": string
19
16
  /** Returns a packed version of the entire repo as a stand-alone script that boots the ecosystem. */
@@ -26,16 +23,12 @@ declare interface IEcosystem
26
23
  readonly "ETag": string
27
24
  /** The current unix timestamp, acquired using the high-precision performance.now() + performance.timeOrigin. */
28
25
  readonly "now": DOMHighResTimeStamp
29
- /** The host of the desired default app. The server will redirect to this when the user visits localhost at the designated port to test locally. */
30
- readonly "defaultApplicationHost": string
31
26
  /** A stylesheet containing CSS variables with `url()` values that correspond to images. Used to seamlessly hand-off image rendering from the server-rendered page to the client-rendered page without modifying the DOM. */
32
27
  readonly "images.css": string
33
28
  /** The current application's PWA manifest. */
34
29
  readonly "manifest.json": string
35
30
  /** A JSON object serializing the desired landing model of the ecosystem. */
36
31
  readonly "landing-model.json": string
37
- /** The internal port (typically between 3000 and 4000) where the server will be hosted for both the reverse proxy in production and when testing locally via `localhost`. */
38
- readonly "port": number
39
32
  /** Returns the given HTML document with images (using `early-*` compressed images for server- or worker-rendered snapshots) into the head as style tag(s). This is used for enhancing the first page appearance when using server-side rendering. When injecting early images, it scans the given document for image usages to determine which early images the snapshot requires. */
40
33
  readonly injectImages(HTML_DOCUMENT: string): string
41
34
  /** Handles standard anchor link clicks in one of four ways:
@@ -69,7 +62,104 @@ declare interface IEcosystem
69
62
  /** A boolean that is set to `true` as soon as the route ID is set for the first time. */
70
63
  readonly initialized: undefined | true
71
64
  }
65
+ declare interface IKirejiConfig {
66
+ /** * Determines which color mode features will be included in the ecosystem.
67
+ * - `none`: The color component will never be included.
68
+ * - `light`: Dark mode will never be included.
69
+ * - `dark`: Light mode will never be included.
70
+ * - `debug-dark`: Dark mode will only be included in local builds.
71
+ * - `debug-light`: Light mode will only be included in local builds.
72
+ * - `full`: The full color component will always be included.
73
+ * * @remarks When the menu and both colors are included, the color control will appear in the menu.
74
+ */
75
+ readonly "includeColor": "none" | "light" | "dark" | "debug-dark" | "debug-light" | "full";
72
76
 
77
+ /** * Determines which era mode features will be included in the ecosystem.
78
+ * - `none`: The era component will never be included.
79
+ * - `vintage`: Modern mode will never be included.
80
+ * - `modern`: Vintage mode will never be included.
81
+ * - `debug-modern`: Modern mode will only be included in local builds.
82
+ * - `debug-vintage`: Vintage mode will only be included in local builds.
83
+ * - `full`: The full era component will always be included.
84
+ * * @remarks When the menu and both eras are included, the era control will appear in the menu.
85
+ */
86
+ readonly "includeEra": "none" | "vintage" | "modern" | "debug-modern" | "debug-vintage" | "full";
87
+
88
+ /** * Determines whether a list of apps should be included in the menu.
89
+ * - `none`: Apps will never appear in the menu.
90
+ * - `local-only`: Apps will only appear in the menu in local builds.
91
+ * - `full`: Apps will always appear in the menu.
92
+ * * @remarks Has no effect on builds where the menu is not included or when there are no defined menu apps.
93
+ */
94
+ readonly "includeMenuApps": "none" | "local-only" | "full";
95
+
96
+ /** * Determines whether or not the version updating features should be included in the menu.
97
+ * - `none`: Update features will never appear in the menu.
98
+ * - `local-only`: Update features will only appear in the menu in local builds.
99
+ * - `full`: Update features will always appear in the menu.
100
+ * * @remarks Has no effect on builds where the menu is not included.
101
+ */
102
+ readonly "includeUpdates": "none" | "local-only" | "full";
103
+
104
+ /** * Determines whether or not the Kireji IDE should be included in the ecosystem.
105
+ * - `none`: kireji.app will never be included.
106
+ * - `full`: kireji.app will only be included in local builds.
107
+ * - `demo`: kireji.app will always be included (used only for Demo App Ecosystem).
108
+ */
109
+ readonly "includeKirejiApp": "none" | "full" | "demo";
110
+
111
+ /** * Determines whether or not the ecosystem should include desktop-like features.
112
+ * - `none`: The desktop experience will never be included.
113
+ * - `menu-only`: Only the menu will be included, and it will always be included.
114
+ * - `local-only`: The full desktop experience will only be included in local builds.
115
+ * - `full`: The menu will always be included. The desktop experience will only be included in local builds.
116
+ * - `demo`: The full desktop experience will always be included (used only for Demo App Ecosystem).
117
+ */
118
+ readonly "includeDesktop": "none" | "menu-only" | "local-only" | "full" | "demo";
119
+
120
+ /** * One of three strings representing the severity of the API change.
121
+ * - `major`: A breaking change which breaks existing hash assignments.
122
+ * - `minor`: A non-breaking change which adds new hash assignments.
123
+ * - `patch`: A non-breaking change which does not impact hash assignments.
124
+ */
125
+ readonly "change": "major" | "minor" | "patch";
126
+
127
+ /** A number used to control the detail in logs. Only messages with a priority <= this number will be logged. */
128
+ readonly "verbosity": string;
129
+
130
+ /** The host of the desired default app. The server will redirect to this when testing locally. */
131
+ readonly "defaultApplicationHost": string;
132
+
133
+ /** The internal port (typically 3000-4000) where the server will be hosted. */
134
+ readonly "port": string;
135
+
136
+ /** * Determines whether or not the artifact should be output with embedded source map data.
137
+ * - `0`: Disabled
138
+ * - `1`: Enabled
139
+ */
140
+ readonly "mapping": "disabled" | "enabled";
141
+
142
+ /** * If enabled, halts the hydration of the application completely. Useful for debugging FOUC.
143
+ * - `0`: Disabled
144
+ * - `1`: Enabled
145
+ */
146
+ readonly "haltHydration": "disabled" | "enabled";
147
+
148
+ /** * If enabled, a warning line appears at the top of all applications regarding alpha status.
149
+ * - `0`: Disabled
150
+ * - `1`: Enabled
151
+ */
152
+ readonly "includeWarning": "disabled" | "enabled";
153
+
154
+ /** * If enabled, applications running locally will reset to their landing hash when the service worker is replaced.
155
+ * - `0`: Disabled
156
+ * - `1`: Enabled
157
+ */
158
+ readonly "resetLocalState": "disabled" | "enabled";
159
+
160
+ /** A string representing how long the main thread should hang to simulate loading. Useful for debugging FOUC. */
161
+ readonly "hangHydration": string;
162
+ }
73
163
  /** This is the root part of the ecosystem, considered the ecosystem itself. @remarks When JSON stringified, it should inline all information compiled from the git repo in node by the build process. The serialized version should not include any values that are added during or after recursively hydrating the part tree. This means that all runtime values should be non-enumerable and defined using the `define()` method. */
74
164
  declare const _: IEcosystem
75
165
  /** A shorthand for document.querySelector */
@@ -322,4 +412,76 @@ declare class Vector {
322
412
  }
323
413
 
324
414
  declare type IVector = Record<string, number>
325
- declare type IVector2 = { x: number, y: number }
415
+ declare type IVector2 = { x: number, y: number }
416
+
417
+ declare type KirejiConfigColor =
418
+ /** The color component will never be included. */
419
+ | "none"
420
+ /** Dark mode will never be included. */
421
+ | "light"
422
+ /** Light mode will never be included. */
423
+ | "dark"
424
+ /** Dark mode will only be included in local builds. */
425
+ | "debug-dark"
426
+ /** Light mode will only be included in local builds. */
427
+ | "debug-light"
428
+ /** The full color component will always be included. */
429
+ | "full"
430
+
431
+ declare type KirejiConfigEra =
432
+ /** The era component will never be included. */
433
+ | "none"
434
+ /** Modern mode will never be included. */
435
+ | "vintage"
436
+ /** Vintage mode will never be included. */
437
+ | "modern"
438
+ /** Modern mode will only be included in local builds. */
439
+ | "debug-modern"
440
+ /** Vintage mode will only be included in local builds. */
441
+ | "debug-vintage"
442
+ /** The full era component will always be included. */
443
+ | "full"
444
+
445
+ declare type KirejiConfigMenuApps =
446
+ /** Apps will never appear in the menu. */
447
+ | "none"
448
+ /** Apps will only appear in the menu in local builds. */
449
+ | "local-only"
450
+ /** Apps will always appear in the menu. */
451
+ | "full"
452
+
453
+ declare type KirejiConfigUpdates =
454
+ /** Update features will never appear in the menu. */
455
+ | "none"
456
+ /** Update features will only appear in the menu in local builds. */
457
+ | "local-only"
458
+ /** Update features will always appear in the menu. */
459
+ | "full"
460
+
461
+ declare type KirejiConfigKirejiApp =
462
+ /** kireji.app will never be included. */
463
+ | "none"
464
+ /** kireji.app will only be included in local builds. */
465
+ | "full"
466
+ /** kireji.app will always be included (used only for Demo App Ecosystem). */
467
+ | "demo"
468
+
469
+ declare type KirejiConfigDesktop =
470
+ /** The desktop experience will never be included. */
471
+ | "none"
472
+ /** Only the menu will be included, and it will always be included. */
473
+ | "menu-only"
474
+ /** The full desktop experience will only be included in local builds. */
475
+ | "local-only"
476
+ /** The menu will always be included. The desktop experience will only be included in local builds. */
477
+ | "full"
478
+ /** The full desktop experience will always be included (used only for Demo App Ecosystem). */
479
+ | "demo"
480
+
481
+ declare type KirejiConfigChange =
482
+ /** A breaking change which breaks existing hash assignments. */
483
+ | "major"
484
+ /** A non-breaking change which adds new hash assignments but doesn't break existing ones. */
485
+ | "minor"
486
+ /** A non-breaking change which does not impact hash assignments at all. */
487
+ | "patch"
package/src/validate.js CHANGED
@@ -25,9 +25,9 @@ if (environment === "node-main") {
25
25
  })
26
26
  logScope(1, "\nSerialization Test", log => {
27
27
  const postHydrationArchive = serialize(_)
28
- if (postHydrationArchive !== preHydrationArchive) {
29
- warn(`The post-hydration archive was ${postHydrationArchive.length - preHydrationArchive.length} bytes longer than the pre-hydration archive.`)
30
- const lines1 = preHydrationArchive.split('\n')
28
+ if (postHydrationArchive !== _.preHydrationArchive) {
29
+ warn(`The post-hydration archive was ${postHydrationArchive.length - _.preHydrationArchive.length} bytes longer than the pre-hydration archive.`)
30
+ const lines1 = _.preHydrationArchive.split('\n')
31
31
  const lines2 = postHydrationArchive.split('\n')
32
32
  const maxLength = Math.max(lines1.length, lines2.length)
33
33
  for (let i = 0; i < maxLength; i++) {