basedpl 0.1.24 → 0.1.27

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.
Binary file
package/layout.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "o": {"state": "circle"},
26
26
  "p": "π",
27
27
  "+": "≠",
28
- "\\": {"state": "stile"},
28
+ "\\": "⍭",
29
29
  "a": {"state": "alpha"},
30
30
  "s": "⌈",
31
31
  "d": "⌊",
@@ -68,7 +68,6 @@
68
68
  "epsilon": {"terminator": "∊", "keys": {"_": "⍷", "-": "⍷"}},
69
69
  "iota": {"terminator": "⍳", "keys": {"_": "⍸", "-": "⍸"}},
70
70
  "circle": {"terminator": "○", "keys": {"-": "⊖", "\\": "⍉", "/": "⊘", "*": "⍟", "m": "⊗", "u": "⨸", "t": "⌾", "8": "⍟"}},
71
- "stile": {"terminator": "|", "keys": {"~": "⍭", "`": "⍭"}},
72
71
  "alpha": {"terminator": "⍺", "keys": {"_": "⍶", "-": "⍶"}},
73
72
  "del": {"terminator": "∇", "keys": {"|": "⍒", "\\": "⍒"}},
74
73
  "take": {"terminator": "↑", "keys": {"j": "↕"}},
package/lb.js CHANGED
@@ -80,6 +80,9 @@
80
80
  .bpl_choices{position:fixed;max-height:240px;max-width:calc(100vw - 16px);overflow:auto;border:1px solid #888;border-radius:4px;box-shadow:0 3px 12px #0003;padding:4px}
81
81
  .bpl_choices button{display:block;width:100%;text-align:left;white-space:nowrap}
82
82
  .bpl_choices small{display:block;padding:4px}
83
+ .ngn_lb .bpl_dead::after{content:'▾';font-size:9px;margin-left:1px}
84
+ .bpl_choices.bpl_menu{max-width:min(320px,calc(100vw - 16px))}
85
+ .bpl_menu button{display:inline-block;width:auto;min-width:28px;text-align:center}
83
86
  @media(prefers-color-scheme:dark){.ngn_lb,.bpl_choices{background:#222;color:#ddd}.ngn_lb button:hover,.bpl_choices button:hover{background:#bbb;color:#111}}
84
87
  </style>`;
85
88
  d.body.append(host);
@@ -91,32 +94,60 @@
91
94
  toggle.textContent = overlay ? '▼' : '▲';
92
95
  d.body.style.paddingTop = overlay || bar.hidden ? originalPadding : `${bar.offsetHeight}px`;
93
96
  }
97
+ const names = new Map(symbols.map(({glyph, name, monad, dyad, aliases}) =>
98
+ [glyph, [...new Set([name, monad, dyad, aliases].join(' ').split(' ').filter(Boolean))].join(' ')]));
99
+ function stateGlyphs(state) {
100
+ const {terminator, keys} = layout.states[state];
101
+ return [terminator, ...Object.values(keys).flatMap(value => typeof value === 'string' ? [value] : stateGlyphs(value.state))];
102
+ }
103
+ const groups = new Map(Object.values(layout.option).filter(value => value.state).map(({state}) =>
104
+ [layout.states[state].terminator, [...new Set(stateGlyphs(state))]]));
105
+ const variants = new Set([...groups.values()].flatMap(glyphs => glyphs.slice(1)));
94
106
  function button(glyph, name) {
95
107
  const b = d.createElement('button');
96
- b.type = 'button'; b.textContent = glyph; b.title = name + shortcuts.get(glyph); b.dataset.glyph = glyph;
108
+ b.type = 'button'; b.textContent = glyph; b.title = (name || glyph) + (shortcuts.get(glyph) || ''); b.dataset.glyph = glyph;
97
109
  return b;
98
110
  }
99
- for (const {glyph, name, monad, dyad, aliases} of symbols)
100
- bar.append(button(glyph, [...new Set([name, monad, dyad, aliases].join(' ').split(' ').filter(Boolean))].join(' ')));
111
+ // Related glyphs stay together; arrows meet at the boundary between menus and plain buttons.
112
+ const order = [...new Set([
113
+ ...'^_⍺⍵∇∆⋄¯¨○⎕∨∧≡∊⍳⊂∩⊥⊤↑↓←→↢↣⇄⊣⊢×÷⌊⌈≤≥≠√π⍭', ...groups.keys(), ...names.keys()
114
+ ])];
115
+ for (const glyph of [...order.filter(g => groups.has(g)), ...order.filter(g => !groups.has(g))]) {
116
+ if (!groups.has(glyph) && (variants.has(glyph) || /^[\x20-\x7e]+$/.test(glyph))) continue;
117
+ const b = button(glyph, names.get(glyph));
118
+ if (groups.has(glyph)) {
119
+ b.textContent = {'^': '²', '_': '₂'}[glyph] || glyph;
120
+ b.classList.add('bpl_dead');
121
+ b.setAttribute('aria-expanded', 'false');
122
+ b.title += ' (variants)';
123
+ }
124
+ bar.append(b);
125
+ }
101
126
  new ResizeObserver(reflow).observe(bar);
102
127
  reflow();
103
128
 
104
- function cancel() { active = undefined; choice = undefined; tip.hidden = true; }
105
- function show(e, item) {
106
- if (choice?.item.query !== item.query) {
129
+ function cancel() {
130
+ choice?.anchor?.setAttribute('aria-expanded', 'false');
131
+ active = undefined; choice = undefined; tip.hidden = true;
132
+ }
133
+ function show(e, item, anchor) {
134
+ tip.classList.toggle('bpl_menu', !!anchor);
135
+ tip.setAttribute('aria-label', anchor ? 'BPL symbol variants' : 'BPL symbol completions');
136
+ anchor?.setAttribute('aria-expanded', 'true');
137
+ if (choice?.item.query !== item.query || choice?.anchor !== anchor) {
107
138
  tip.replaceChildren();
108
139
  for (const [glyph, name] of item.found) {
109
140
  const b = button(glyph, name);
110
- b.textContent = `${glyph} ${b.title}`;
141
+ if (!anchor) b.textContent = `${glyph} ${b.title}`;
111
142
  tip.append(b);
112
143
  }
113
144
  if (!item.found.length) {
114
145
  const note = d.createElement('small'); note.textContent = 'Unknown symbol'; tip.append(note);
115
146
  }
116
147
  }
117
- choice = {e, item};
148
+ choice = {e, item, anchor};
118
149
  tip.hidden = false;
119
- const r = e.rect();
150
+ const r = anchor ? anchor.getBoundingClientRect() : e.rect();
120
151
  tip.style.left = `${Math.max(4, Math.min(r.left, innerWidth - tip.offsetWidth - 8))}px`;
121
152
  tip.style.top = `${Math.max(bar.hidden || overlay ? 4 : bar.offsetHeight, Math.min(r.bottom + 4, innerHeight - tip.offsetHeight - 8))}px`;
122
153
  }
@@ -128,13 +159,19 @@
128
159
  bar.addEventListener('mousedown', ev => {
129
160
  ev.preventDefault();
130
161
  const b = ev.target.closest('button');
162
+ const opened = choice?.anchor === b;
163
+ cancel();
131
164
  if (b?.classList.contains('ngn_x')) { bar.hidden = true; reflow(); }
132
165
  else if (b === toggle) {
133
166
  overlay = !overlay;
134
167
  try { localStorage.setItem('ngn_lb_overlay', overlay ? '1' : '0'); } catch {}
135
168
  reflow();
136
- } else if (b?.dataset.glyph && lastEditor) lastEditor.insert(b.dataset.glyph);
137
- cancel();
169
+ } else if (b?.dataset.glyph && lastEditor) {
170
+ const glyph = b.dataset.glyph, group = groups.get(glyph);
171
+ if (group) {
172
+ if (!opened) show(lastEditor, {query: glyph, found: group.map(g => [g, names.get(g)])}, b);
173
+ } else lastEditor.insert(glyph);
174
+ }
138
175
  });
139
176
  tip.addEventListener('mousedown', ev => {
140
177
  ev.preventDefault();
@@ -159,6 +196,9 @@
159
196
  keyInput = false; lastEditor = editor(ev.target); if (active) refresh(ev.target);
160
197
  }, true);
161
198
  window.addEventListener('keydown', ev => {
199
+ if (ev.key === 'Escape' && choice?.anchor) {
200
+ cancel(); ev.preventDefault(); ev.stopImmediatePropagation(); return;
201
+ }
162
202
  keyInput = false;
163
203
  if (ev.code === 'AltLeft') leftAlt = true;
164
204
  if (ev.code === 'AltRight') rightAlt = true;
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "basedpl",
3
- "version": "0.1.24",
3
+ "version": "0.1.27",
4
4
  "description": "BasedPL, the Based-array Programming Language, is an APL-derived array language, borrowing ideas from J and BQN, with an emphasis on simple, consistent notation.",
5
5
  "license": "Apache-2.0",
6
- "repository": "https://github.com/AnswerDotAI/basedpl",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/AnswerDotAI/basedpl.git"
9
+ },
7
10
  "type": "module",
8
11
  "main": "basedpl_wasm.js"
9
12
  }