solarite 0.1.0 → 0.2.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 (48) hide show
  1. package/benchmarks/naive/Solarite.min.js +4 -0
  2. package/benchmarks/naive/index.html +14 -0
  3. package/benchmarks/naive/main.js +339 -0
  4. package/benchmarks/naive/package-lock.json +13 -0
  5. package/benchmarks/naive/package.json +23 -0
  6. package/benchmarks/readme.md +48 -0
  7. package/build/build.bat +3 -2
  8. package/build/build.js +1 -1
  9. package/dist/Solarite-debug.js +1941 -2791
  10. package/dist/Solarite.js +1697 -2511
  11. package/dist/Solarite.min.js +3 -3
  12. package/dist/udomdiff-license.txt +18 -0
  13. package/docs/index.md +695 -235
  14. package/docs/js/Playground.js +1 -1
  15. package/docs/js/codemirror/codemirror6.js +3683 -3206
  16. package/docs/js/codemirror/themeSolarIce.js +2 -2
  17. package/docs/js/documentation.js +2 -2
  18. package/docs/js/ui/CodeEditor.js +183 -45
  19. package/docs/js/ui/FlexResizer.js +15 -5
  20. package/docs/js/util/Errors.js +11 -0
  21. package/docs/media/documentation.css +6 -3
  22. package/index.html +462 -26
  23. package/package.json +1 -1
  24. package/readme.md +11 -1
  25. package/src/solarite/ExprPath.js +422 -135
  26. package/src/solarite/Globals.js +53 -0
  27. package/src/solarite/NodeGroup.js +300 -388
  28. package/src/solarite/Shell.js +31 -33
  29. package/src/solarite/Solarite.js +17 -2
  30. package/src/solarite/Template.js +75 -25
  31. package/src/solarite/Util.js +131 -7
  32. package/src/solarite/createSolarite.js +32 -25
  33. package/src/solarite/getArg.js +12 -12
  34. package/src/solarite/hash.js +18 -35
  35. package/src/solarite/r.js +128 -118
  36. package/src/solarite/watch3.js +98 -0
  37. package/src/{solarite → unused}/NodeGroupManager.js +86 -224
  38. package/src/unused/onConnect.js +79 -0
  39. package/src/{solarite → unused}/watch.js +2 -2
  40. package/src/{solarite → unused}/watch2.js +4 -4
  41. package/src/{solarite → util}/MultiValueMap.js +23 -12
  42. package/src/util/WeakArray.js +33 -0
  43. package/tests/Solarite.test.js +1108 -166
  44. package/tests/Testimony.js +274 -67
  45. package/tests/index.html +6 -35
  46. package/tests/run.bat +2 -0
  47. package/tests/NodeGroup.test.js +0 -115
  48. package/tests/Shell.test.js +0 -75
@@ -14,7 +14,7 @@ function themeSolarIce(lang, isDark=false) {
14
14
  text = isDark ? "#aaa" : 'black',
15
15
  darkBackground = "#21252b",
16
16
  highlightBackground = isDark ? "#2c313a" : '#ddd',
17
- background = isDark ? "#06090c" : 'white',
17
+ background = isDark ? "transparent" : 'white',
18
18
  tooltipBackground = "#353a42",
19
19
  selection = isDark ? "#234" : '#ccc',
20
20
  cursor = "#528bff"
@@ -146,7 +146,7 @@ function themeSolarIce(lang, isDark=false) {
146
146
  tag: [t.atom], // Css value like 'Arial' or 'none'. Keyword 'super' in javascript
147
147
  color: lang === 'js'
148
148
  ? (isDark ? '#26f' : '#00f')
149
- : (isDark ? '#75f' : '')
149
+ : (isDark ? '#26f' : '')
150
150
  },
151
151
  {
152
152
  tag: [t.unit],
@@ -21,12 +21,12 @@ document.addEventListener('DOMContentLoaded', () => {
21
21
 
22
22
  let prefix = `
23
23
  <style>
24
- html[dark] { background: black; color: white }
24
+ html[dark] { background: #0f1318; color: white }
25
25
  body { font: 12px Arial; background: transparent !important }
26
26
  </style>`;
27
27
  let value = lines.join('\n');
28
28
 
29
- let pg = new Playground({value, language: pre.getAttribute('lang'), width: 75, maxHeight: 620, prefix, limitCodeEditorHeight: true})
29
+ let pg = new Playground({value, language: pre.getAttribute('lang'), width: 75, maxHeight: 1000, prefix, limitCodeEditorHeight: true})
30
30
  pre.replaceWith(pg);
31
31
  }
32
32
  })
@@ -246,7 +246,7 @@ export default class CodeEditor extends Solarite {
246
246
  let doc = this.ownerDocument || window.top.document;
247
247
  if (isDark === undefined)
248
248
  isDark = doc.documentElement.hasAttribute('dark');
249
-
249
+
250
250
  let languageExtensions = this.getLanguageExtension(this.language, this.languageConfig)
251
251
  return [
252
252
  //basicSetup,
@@ -258,8 +258,6 @@ export default class CodeEditor extends Solarite {
258
258
  //foldGutter(),
259
259
  drawSelection(),
260
260
  indentUnit.of("\t"),
261
- this.compartments.tabSize.of(EditorState.tabSize.of(this.tabSize)),
262
- this.compartments.lineWrapping.of(wordWrap ? EditorView.lineWrapping : []),
263
261
  EditorState.allowMultipleSelections.of(true),
264
262
  indentOnInput(),
265
263
  bracketMatching(),
@@ -285,11 +283,14 @@ export default class CodeEditor extends Solarite {
285
283
 
286
284
  Extensions.changeDetection(this),
287
285
  Extensions.asyncField,
286
+
288
287
 
288
+ syntaxHighlighting(defaultHighlightStyle, {fallback: true}), // fallback: true lets us use our own theme.
289
+
290
+ // Compartments let options be changed after init.
289
291
  this.compartments.theme.of(themeSolarIce(this.language, isDark)),
290
-
291
- syntaxHighlighting(defaultHighlightStyle, {fallback: true}),
292
-
292
+ this.compartments.tabSize.of(EditorState.tabSize.of(this.tabSize)),
293
+ this.compartments.lineWrapping.of(wordWrap ? EditorView.lineWrapping : []),
293
294
  this.compartments.language.of(languageExtensions)
294
295
  ];
295
296
  }
@@ -516,7 +517,7 @@ export default class CodeEditor extends Solarite {
516
517
 
517
518
 
518
519
  render() {
519
- this.html = r`
520
+ r(this)`
520
521
  <code-editor>
521
522
  <style>
522
523
  :host { position: relative; display: flex; flex-direction: column; min-width: 0; min-height: 0 }
@@ -609,18 +610,38 @@ var ParseUtil = {
609
610
  wrap: parseMixed((node, docInput) => {
610
611
  if (node.name == "StyleText")
611
612
  return {parser: cssParser}
612
- if (node.name == "ScriptText")
613
+ if (node.name == "ScriptText"/* || node.name == "Interpolation"*/)
613
614
  return {parser: ParseUtil.jsParser}
614
615
 
615
616
  // Use javascript parser for template expressions
616
617
  if (node.type.name === 'Document' && docInput.doc) {
617
618
  let code = docInput.doc.sliceString(node.from, node.to);
618
619
  let overlay = [];
619
- code.replace(/\${/g, (a, startIndex, c) => {
620
- let endIndex = ParseUtil.findMatchingBrace(code, startIndex+2);
621
- if (endIndex !== -1)
622
- overlay.push({from: node.from + startIndex, to: node.from + endIndex + 1});
623
- })
620
+ //code.replace(/\${/g, (a, startIndex) => {
621
+
622
+ let ranges = ParseUtil.findJavaScriptRanges(code, 0);
623
+
624
+ if (ranges.length) {
625
+
626
+ ranges = ranges.map(item => ({
627
+ from: node.from + item[0],
628
+ to: node.from + item[1]
629
+ }));
630
+
631
+ overlay.push(...ranges);
632
+ }
633
+ //});
634
+
635
+ // Make sure overlays don't overlap.
636
+ // TODO: Is this still necessary?
637
+ overlay.sort((a, b) => a.from - b.from);
638
+ for (let i=0; i<overlay.length-1; i++) {
639
+ let cur = overlay[i];
640
+ let next = overlay[i+1];
641
+ if (cur.to >= next.from)
642
+ cur.to = next.from;
643
+ }
644
+
624
645
  return {
625
646
  parser: ParseUtil.jsParser,
626
647
  overlay
@@ -637,8 +658,11 @@ var ParseUtil = {
637
658
  let code = docInput.doc.sliceString(node.from, node.to);
638
659
 
639
660
  // Only if node text starts and ends with tags, allowing for spaces.
640
- if (code.match(/^`\s*</) && code.match(/>\s*`$/))
641
- return {parser: ParseUtil.htmlParser}
661
+ if (code.match(/^`\s*</)/* && code.match(/>\s*`$/)*/) {
662
+ return {
663
+ parser: ParseUtil.htmlParser,
664
+ }
665
+ }
642
666
  }
643
667
  return null;
644
668
  })
@@ -661,10 +685,107 @@ var ParseUtil = {
661
685
  top: "Template"
662
686
 
663
687
  }),
664
-
665
-
688
+
689
+ /**
690
+ * Used by javascriptWithHtml()
691
+ * Handles single and multiline comments and all three string types.
692
+ * TODO: This needs tests!
693
+ * @param str {string}
694
+ * @param startIndex {int}
695
+ * @returns {int[][]} */
696
+ findJavaScriptRanges(str, startIndex) {
697
+ let depth = 0;
698
+ let depthStack = [];
699
+ let inString = null;
700
+ let stringStack = [];
701
+ let inComment = null;
702
+
703
+ let result = [];
704
+
705
+ for (let i = startIndex; i < str.length; i++) {
706
+
707
+ let char = str[i];
708
+ if (inString && char === '\\') { // Skip escaped characters in strings.
709
+ i++;
710
+ continue;
711
+ }
712
+ let nextChar = i + 1 < str.length ? str[i + 1] : '';
713
+
714
+ // String start/end
715
+ if (!inComment) {
716
+ if (!inString && (char === '`' || char === '`' || char === '`')) {
717
+ inString = char;
718
+ if (char === '`') {
719
+ if (i > startIndex) {
720
+ result.push([startIndex, i]);
721
+ }
722
+ }
723
+ }
724
+ else if (inString === char) {
725
+ if (inString === '`')
726
+ startIndex = i + 1;
727
+ inString = null;
728
+ }
729
+ }
730
+
731
+
732
+ if (!inString) {
733
+ // Comment start/end
734
+ if (char === '/' && (nextChar === '*' || nextChar === '/')) {
735
+ inComment = nextChar === '*' ? 'block' : 'line';
736
+ i++; // Skip the next character as it's part of the comment syntax
737
+ }
738
+
739
+ // End block comment
740
+ else if (inComment === 'block' && char === '*' && nextChar === '/') {
741
+ inComment = null;
742
+ i++; // Skip the '/' character
743
+ }
744
+
745
+ // End line comment
746
+ else if (inComment === 'line' && char === '\n')
747
+ inComment = null;
748
+ }
749
+
750
+ // Template literal boundaries
751
+ if (!inComment) {
752
+
753
+ if (inString) {
754
+ if (char === '$' && nextChar === '{') {
755
+ startIndex = i;
756
+ i++; // Skip the '{' character
757
+ stringStack.push(inString);
758
+ inString = null;
759
+ depthStack.push(depth);
760
+ depth = 0;
761
+ }
762
+ }
763
+
764
+ else {
765
+ if (char === '{')
766
+ depth++;
767
+
768
+ else if (char === '}') {
769
+ if (depth === 0) {
770
+ inString = stringStack.pop();
771
+ depth = depthStack.pop();
772
+ if (i > startIndex)
773
+ result.push([startIndex, i+1]);
774
+ }
775
+ else
776
+ depth--;
777
+ }
778
+ }
779
+ }
780
+ }
781
+
782
+ return result;
783
+ },
784
+
666
785
  /**
786
+ * @deprecated
667
787
  * Used by javascriptWithHtml()
788
+ * TODO: This needs tests!
668
789
  * @param str {string}
669
790
  * @param startIndex {int}
670
791
  * @returns {int} -1 if no match. */
@@ -672,56 +793,73 @@ var ParseUtil = {
672
793
  let stack = 0;
673
794
  let inString = null;
674
795
  let inComment = null;
675
-
676
- for (let i=startIndex; i<str.length; i++) {
796
+
797
+ for (let i = startIndex; i < str.length; i++) {
677
798
  let char = str[i];
678
799
  if (inString && char === '\\') { // Skip escaped characters in strings.
679
800
  i++;
680
801
  continue;
681
802
  }
682
- let nextChar = i + 1 < str.length ? str[i+1] : '';
683
-
803
+ let nextChar = i + 1 < str.length ? str[i + 1] : '';
804
+
684
805
  // String start/end
685
- if (char === '`' && !inComment)
686
- inString = inString ? null : '`';
687
- else if ((char === '"' || char === "'") && !inComment)
688
- inString = inString === char ? null : char;
689
-
690
- // Comment start/end
691
- if (!inString && char === '/' && (nextChar === '*' || nextChar === '/')) {
692
- inComment = nextChar === '*' ? 'block' : 'line';
693
- i++; // Skip the next character as it's part of the comment syntax
806
+ if (!inComment) {
807
+ if (!inString && (char === '`' || char === '`' || char === '`'))
808
+ inString = char;
809
+ else if (inString === char)
810
+ inString = null;
694
811
  }
695
- else if (inComment === 'block' && char === '*' && nextChar === '/') {
696
- inComment = null;
697
- i++; // Skip the '/' character
812
+
813
+ // Comment start/end
814
+ if (!inString) {
815
+ if (char === '/' && (nextChar === '*' || nextChar === '/')) {
816
+ inComment = nextChar === '*' ? 'block' : 'line';
817
+ i++; // Skip the next character as it's part of the comment syntax
818
+ }
819
+
820
+ // End block comment
821
+ else if (inComment === 'block' && char === '*' && nextChar === '/') {
822
+ inComment = null;
823
+ i++; // Skip the '/' character
824
+ }
825
+
826
+ // End line comment
827
+ else if (inComment === 'line' && char === '\n')
828
+ inComment = null;
698
829
  }
699
- else if (inComment === 'line' && char === '\n')
700
- inComment = null;
701
-
830
+
702
831
  // Template literal boundaries
703
832
  if (!inString && !inComment) {
704
- if (char === '$' && nextChar === '{') {
833
+ if (char === '{')
834
+ stack++;
835
+
836
+ else if (char === '$' && nextChar === '{') {
705
837
  stack++;
706
838
  i++; // Skip the '{' character
707
- } else if (char === '}') {
839
+ }
840
+
841
+ else if (char === '}') {
708
842
  if (stack === 0)
709
843
  return i; // Matching closing brace found
710
844
  stack--;
711
845
  }
712
846
  }
713
847
  }
714
-
848
+
715
849
  return -1;
716
850
  }
717
851
  };
718
852
 
719
853
 
720
854
  /**
721
- * @typedef ToolbarButton
855
+ * @typedef {Object} ToolbarButton
722
856
  * @property {string} html
723
- * @property {function(el:Node|HTMLElement)} update */
724
- export class CodeEditorToolbar extends Solarite {
857
+ * @property {function(el:Node|HTMLElement)=} update */
858
+
859
+ /**
860
+ *
861
+ */
862
+ export class CodeEditorToolbar extends HTMLElement {
725
863
 
726
864
  /** @type {object} Should have functions for everything the buttons array uses. */
727
865
  ed;
@@ -770,7 +908,7 @@ export class CodeEditorToolbar extends Solarite {
770
908
 
771
909
  if (typeof buttons === 'string')
772
910
  buttons = buttons.split(/[,\s]+/g);
773
-
911
+
774
912
  for (let button of buttons||[]) {
775
913
  let item = typeof button === 'string'
776
914
  ? this.buttonTemplates[button]
@@ -812,7 +950,7 @@ export class CodeEditorToolbar extends Solarite {
812
950
  }
813
951
 
814
952
  render() {
815
- this.html = r`
953
+ r(this)`
816
954
  <code-editor-toolbar>
817
955
  <style>
818
956
  :host { display: block }
@@ -836,5 +974,5 @@ export class CodeEditorToolbar extends Solarite {
836
974
  }
837
975
 
838
976
  }
839
- CodeEditorToolbar.define();
977
+ customElements.define('code-editor-toolbar', CodeEditorToolbar);
840
978
 
@@ -3,8 +3,7 @@ import Draggable2 from "../util/Draggable2.js";
3
3
  import Util from "../util/Util.js";
4
4
 
5
5
  /**
6
- * Put this element between flex children to allow resizing them.
7
- * TODO: Allow vertical resizing. */
6
+ * Put this element between flex children to allow resizing them. */
8
7
  export default class FlexResizer extends Solarite {
9
8
 
10
9
  onStart = Util.callback();
@@ -61,9 +60,15 @@ export default class FlexResizer extends Solarite {
61
60
  }
62
61
  });
63
62
  }
64
-
63
+
64
+ /**
65
+ * It's vertical if the user drags the resizer vertically to resize.
66
+ * When vertical, the resizer will be stretched horizontally to fill the available space.
67
+ * @returns {boolean} */
65
68
  isVertical() {
66
- return getComputedStyle(this.parentNode).flexDirection !== 'column';
69
+ if (this.parentNode) {
70
+ return getComputedStyle(this.parentNode).flexDirection === 'row';
71
+ }
67
72
  }
68
73
 
69
74
  /** @return {Number} */
@@ -127,10 +132,15 @@ export default class FlexResizer extends Solarite {
127
132
  :host {
128
133
  display: block; z-index: 1;
129
134
 
130
- /* We put the resize region (the margin thickness) all to the right / top bc vertical scrollbars are often to the left, and tab bars below the top. */
135
+ /* We put the resize region (the margin thickness) all to the right / top bc vertical scrollbars
136
+ * are often to the left, and tab bars below the top.*/
131
137
  ${this.isVertical()
138
+
139
+ /* Stretch horizontally */
132
140
  ? `min-width: ${this.thickness-1}px !important; max-width: ${this.thickness-1}px !important;
133
141
  margin-left: -1px; margin-right: -${this.thickness-1}px; cursor: ew-resize`
142
+
143
+ /* Stretch vertically */
134
144
  : `min-height: ${this.thickness}px !important; max-height: ${this.thickness}px !important;
135
145
  margin-top: -${this.thickness/2}px; margin-bottom: -${this.thickness/2}px; cursor: ns-resize`
136
146
  };
@@ -4,6 +4,17 @@ export function assert(val) {
4
4
  if (!val) {
5
5
  debugger;
6
6
  throw new Error('Assertion failed: ' + val);
7
+ }
8
+
9
+ var Errors = {
10
+ getStack(ignoreTop=0, ignoreRegex) {
11
+ var e = new Error();
12
+ if (!e.stack) return []; // There is no stack in IE.
13
+ return e.stack
14
+ .split('\n')
15
+ .slice(ignoreTop+2)
16
+ .map(line => line.trim().replace(/^at /, ''))
17
+ .filter(line => !ignoreRegex || !line.match(ignoreRegex));
7
18
  }
8
19
  }
9
20
  //#ENDIF
@@ -8,8 +8,11 @@
8
8
  font-weight: 400;
9
9
  src: url("FiraCode400.woff2") format("woff")
10
10
  }
11
-
12
- html { background: var(--background) !important }
11
+ html[dark] {
12
+ /*--card-background: #0b0e12 !important;*/
13
+ --card-border: #1a1c1e !important;
14
+ }
15
+ html { background: var(--background) !important; font-size: 18px !important }
13
16
  body::before { content: ''; position: fixed; inset: 0;
14
17
  opacity: .03; filter: grayscale(75%);
15
18
  background: url('solarite-machine.webp') no-repeat -200px -200px / 140% auto ; }
@@ -41,7 +44,7 @@ html:not([dark]) #write h3 { color: #678; }
41
44
  #write h6 { font-size: 100%; font-weight: normal; margin-top: .75em; margin-bottom: 0; text-decoration: underline }
42
45
 
43
46
 
44
- #write :is(p, h2, h3, h4, h5, h6, ol, ul) { max-width: 600px }
47
+ #write :is(p, h2, h3, h4, h5, h6, ol, ul) { max-width: 800px }
45
48
  #write p { text-align: justify }
46
49
  #write a, #write a:visited { color: #08c; text-decoration: none}
47
50
  #write a:hover { color: #08c; text-decoration: underline }