x4js 2.3.23 → 2.3.24

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/aicontext.md CHANGED
@@ -285,6 +285,9 @@ static instance<P extends Application>(): P
285
285
  setEnv(name: string, value: any): void
286
286
  getEnv(name: string, def_value?: any): any
287
287
  static fireGlobal(msg: string, params?: any): void
288
+ static setTheme(name: string): void // the class x4theme-<name> on <html>: "" the light theme (default, no class),
289
+ // "dark" the dark theme of x4, another name a theme of the application
290
+ static getTheme(): string // "" for the light theme, "dark", or the name given to setTheme
288
291
  focusNext(next: boolean): boolean
289
292
  addShortcut(keys: string | string[], callback: (ev: KeyboardEvent) => void, options?: ShortcutOptions): void // see Shortcuts
290
293
  setupSocketMessaging(path?: string, looseCallback?: () => void): void
@@ -1510,6 +1513,24 @@ isLanguage(name: string): boolean
1510
1513
 
1511
1514
  ---
1512
1515
 
1516
+ ## Theme (`themes.scss`)
1517
+
1518
+ ```css
1519
+ --color-primary-a0 … --color-primary-a100 /* the scale: a0 the background, a100 the text, the accent between */
1520
+ /* the globals the components use: --background-primary / -secondary / -ternary / -hover / -active / -input,
1521
+ --text-primary / -secondary / -ternary, --border / -light / -hover / -focus, --accent-background / -hover /
1522
+ -focus / -active / -light, --accent-color (the text on the accent), --disabled-*, --icon-color / -active,
1523
+ --alert-*, --warning-*, --success-* */
1524
+ ```
1525
+ // The greys are mixes of a0 and a100: a theme that reverses the scale gets them light on dark.
1526
+ // Each component reads its own variables (the @cssvar of its class), which point to these globals:
1527
+ // a theme redefines the globals, or one variable of a component.
1528
+ // Themes: Application.setTheme(name) puts the class x4theme-<name> on <html>. "" is the light theme (by default),
1529
+ // "dark" the dark theme of x4 (the colours of VS Code Dark Modern). An application adds its own theme in its css,
1530
+ // html.x4theme-pink { --color-primary-a0: ...; ... }, then Application.setTheme("pink").
1531
+
1532
+ ---
1533
+
1513
1534
  ## Utilities (`core_tools`)
1514
1535
 
1515
1536
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x4js",
3
- "version": "2.3.23",
3
+ "version": "2.3.24",
4
4
  "type": "module",
5
5
  "main": "src/x4.ts",
6
6
  "module": "src/x4.ts",
@@ -17,8 +17,8 @@
17
17
  @use "../shared";
18
18
 
19
19
  :root {
20
- --color-dialog-caption: var( --color-primary-a80 );
21
- --color-dialog-caption-text: var( --color-primary-a0 );
20
+ --color-dialog-caption: var( --accent-background-active );
21
+ --color-dialog-caption-text: var( --accent-color );
22
22
  --color-dialog-background: var( --background-primary );
23
23
  }
24
24
 
@@ -221,7 +221,6 @@
221
221
  width: 16px;
222
222
  height: 16px;
223
223
  line-height: 16px;
224
- border-radius: 3px;
225
224
  text-align: center;
226
225
  font-weight: 400;
227
226
  cursor: pointer;
@@ -269,7 +268,6 @@
269
268
 
270
269
  background: var( --dock-float-background );
271
270
  border: 1px solid var( --dock-line );
272
- border-radius: 4px;
273
271
  box-shadow: var( --dock-float-shadow );
274
272
  }
275
273
 
@@ -282,7 +280,6 @@
282
280
 
283
281
  background: var( --dock-preview-background );
284
282
  border: 1px solid var( --dock-accent );
285
- border-radius: 3px;
286
283
  }
287
284
 
288
285
  // It holds the pointer during a drag: its cursor is the one that shows.
@@ -284,7 +284,10 @@ export class Keyboard extends HBox<KeyboardProps>
284
284
  keyboard: Box;
285
285
  visible: boolean;
286
286
  input: HTMLInputElement;
287
-
287
+
288
+ // the pointerdown handled last (see handleKeyEvent)
289
+ private handled: UIEvent = null;
290
+
288
291
  constructor( props: KeyboardProps ) {
289
292
  super( { ...props, id: 'v-keyboard' } );
290
293
 
@@ -355,6 +358,13 @@ export class Keyboard extends HBox<KeyboardProps>
355
358
  */
356
359
 
357
360
  private handleKeyEvent( e: UIEvent ) {
361
+ // the key backspace listens to pointerdown too (its autorepeat): this handler is
362
+ // called for its node and for the keyboard, the same event twice; one key only
363
+ if( e===this.handled ) {
364
+ return;
365
+ }
366
+ this.handled = e;
367
+
358
368
  let target = e.target as HTMLElement;
359
369
  let key;
360
370
 
@@ -30,8 +30,8 @@
30
30
  background-color: var( --label-background );
31
31
  gap: 0.2em;
32
32
 
33
- &.al-right
34
- &.right {
33
+ &.al-right,
34
+ &.right {
35
35
  text-align: right;
36
36
  justify-content: end;
37
37
  }
@@ -126,21 +126,22 @@
126
126
  line-height: 1.8em;
127
127
  }
128
128
 
129
+ // on the right, the bar and the lines end where the button OK does: the bar of
130
+ // buttons has a padding of 20px and the button a margin of 2px, the form 16px
129
131
  .x4progress {
130
- margin: 0 16px;
132
+ margin: 0 6px 0 16px;
131
133
  }
132
134
 
135
+ // the lines: a textarea read only, 10 lines high at least, a line is not wrapped
133
136
  #sub-text {
134
137
  flex-grow: 1;
138
+ min-height: calc( 10lh + 10px ); // its padding and its border too
135
139
  max-height: 400px;
136
140
  overflow: auto;
141
+ white-space: pre;
137
142
  background-color: var( --msgbox-log-background );
138
143
  padding: 4px 8px;
139
- margin: 0 16px;
140
-
141
- .x4simpletext {
142
- white-space: nowrap;
143
- }
144
+ margin: 0 6px 0 16px;
144
145
  }
145
146
  }
146
147
  }
@@ -11,6 +11,8 @@ import { Dialog, DialogProps } from "../dialog/dialog"
11
11
  import { Form } from '../form/form';
12
12
  import { BtnGroupItem } from '../btngroup/btngroup';
13
13
  import { Input } from '../input/input';
14
+ import { SimpleTextArea } from '../textarea/textarea';
15
+ import { Button } from '../button/button';
14
16
  import { Component } from '../../core/component';
15
17
  import { Progress } from '../components.js';
16
18
 
@@ -275,6 +277,13 @@ export class PromptBox extends Dialog<DialogProps>
275
277
 
276
278
  // :: PROGRESS ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
277
279
 
280
+ /**
281
+ * a modal box that follows a long task: a title, a bar, and the lines of what is done
282
+ * in a textarea read only (they are text: a markup is shown as it is written).
283
+ * done( ) shows its button OK: after an error it waits for it, else it closes the box
284
+ * after 5s, the seconds left in the button.
285
+ */
286
+
278
287
  @class_ns( "x4" )
279
288
  export class ProgressionBox extends Dialog {
280
289
 
@@ -294,7 +303,7 @@ export class ProgressionBox extends Dialog {
294
303
  new VBox( { flex: 1, cls: "right", content: [
295
304
  new SimpleText( { id: "title", text: title } ),
296
305
  new Progress( { id:"prog", min: 0, max: 100, value: 0 } ),
297
- new VBox( { id: "sub-text" } ),
306
+ new SimpleTextArea( { id: "sub-text", readonly: true, trim: false } ),
298
307
  ]})
299
308
  ]
300
309
  }),
@@ -305,37 +314,80 @@ export class ProgressionBox extends Dialog {
305
314
 
306
315
  this.query("#btnbar").show( false );
307
316
 
308
- this.on("btnclick", ( ) => this.show( false ) );
317
+ // OK: closed at once, the countdown stopped
318
+ this.on("btnclick", ( ) => {
319
+ this.clearInterval( "close" );
320
+ this.show( false );
321
+ } );
309
322
  }
310
323
 
324
+ /** a line at the end */
325
+
311
326
  addText( text: string | UnsafeHtml, perc: number ) {
312
- this.query<Label>( "#sub-text").appendContent( new SimpleText( { text } ) );
327
+ this._addLine( text );
313
328
  this.query<Progress>( "#prog").setValue( perc );
314
329
  }
315
330
 
331
+ /** a line at the end: done( ) then waits for the button OK */
332
+
316
333
  addError( text: string | UnsafeHtml, perc: number ) {
317
- this.query<Label>( "#sub-text").appendContent( new SimpleText( { cls:"error", text } ) );
334
+ this._addLine( text );
318
335
  this.query<Progress>( "#prog").setValue( perc );
319
336
 
320
337
  this.#has_errors = true;
321
338
  }
322
339
 
340
+ /** the text in place of all the lines */
341
+
323
342
  setText( text: string | UnsafeHtml, perc: number ) {
324
- this.query<Label>( "#sub-text").setContent( new SimpleText( { text } ) );
343
+ this.query<SimpleTextArea>( "#sub-text" ).setText( text.toString( ) );
325
344
  this.query<Progress>( "#prog").setValue( perc );
326
345
  }
327
346
 
347
+ /** as at the start: no line, no error, no button OK */
348
+
328
349
  clear( ) {
329
- this.#has_errors = true;
330
- this.query<Label>( "#sub-text").clearContent( );
350
+ this.#has_errors = false;
351
+ this.clearInterval( "close" );
352
+ this.query( "#btnbar" ).show( false );
353
+ this.query<SimpleTextArea>( "#sub-text" ).setText( "" );
354
+ }
355
+
356
+ // the line after the others, the last one shown
357
+ private _addLine( text: string | UnsafeHtml ) {
358
+ const log = this.query<SimpleTextArea>( "#sub-text" );
359
+ const lines = log.getText( );
360
+
361
+ log.setText( lines ? lines + "\n" + text : text.toString( ) );
362
+ log.scrollToBottom( );
331
363
  }
332
364
 
365
+ /**
366
+ * the task is over: the button OK is shown. After an error it waits for a click;
367
+ * without one it counts the seconds left, "OK (5s)", and closes the box at 0
368
+ */
369
+
333
370
  done( ) {
371
+ const ok = this.query<Button>( "#ok" );
372
+ this.query( "#btnbar" ).show( true );
373
+
334
374
  if( this.#has_errors ) {
335
- this.query("#btnbar").show( true );
336
- }
337
- else {
338
- this.setTimeout( "close", 5000, ( ) => { this.show(false);} );
375
+ ok.setText( _tr.global.ok );
376
+ return;
339
377
  }
378
+
379
+ let left = 5;
380
+ ok.setText( `${_tr.global.ok} (${left}s)` );
381
+
382
+ this.setInterval( "close", 1000, ( ) => {
383
+ left--;
384
+ if( left>0 ) {
385
+ ok.setText( `${_tr.global.ok} (${left}s)` );
386
+ }
387
+ else {
388
+ this.clearInterval( "close" );
389
+ this.show( false );
390
+ }
391
+ } );
340
392
  }
341
393
  }
@@ -36,7 +36,6 @@
36
36
  margin: 0;
37
37
  padding: 6px 10px;
38
38
  border: 1px solid var( --border );
39
- border-radius: 4px;
40
39
 
41
40
  &:focus {
42
41
  border-color: var( --settingsview-marker );
@@ -100,7 +99,6 @@
100
99
  max-width: 900px;
101
100
  padding: 0 8px 10px;
102
101
  border: 1px solid var( --border );
103
- border-radius: 8px;
104
102
  background: var( --settingsview-section-background );
105
103
  overflow: hidden;
106
104
  scroll-margin-top: 20px; // brought to the top: below the padding of the view
@@ -144,7 +142,6 @@
144
142
  gap: 4px;
145
143
  padding: 10px 8px 12px;
146
144
  border-left: 2px solid transparent;
147
- border-radius: 4px;
148
145
 
149
146
  &:hover {
150
147
  background: var( --settingsview-hover-background );
@@ -100,4 +100,58 @@ $color--interval: 8.4%;
100
100
  --success-background: var(--color-success-a50);
101
101
  --success-background-active: var(--color-success-a60);
102
102
  --success-color: #ffffff;
103
+ }
104
+
105
+ // :: dark theme :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
106
+ // Application.setTheme( "dark" ): the class x4theme-dark on <html>, like the theme Dark
107
+ // Modern of VS Code. Its colours are taken in the sources of VS Code:
108
+ // extensions/theme-defaults/themes/dark_modern.json, and for the lists the defaults of
109
+ // src/vs/platform/theme/common/colors/listColors.ts; the values marked "chosen" are not
110
+ // in VS Code. A theme of an application is written the same way, under html.x4theme-<name>.
111
+
112
+ html.x4theme-dark {
113
+ // the scale: a0 the background, a100 the text, the surfaces between them, then the blues
114
+ --color-primary-a0: #1f1f1f; // editor.background
115
+ --color-primary-a5: #252526; // chosen
116
+ --color-primary-a10: #2a2d2e; // list.hoverBackground
117
+ --color-primary-a20: #37373d; // list.inactiveSelectionBackground
118
+ --color-primary-a30: #3c3c3c; // input.border
119
+ --color-primary-a40: #04395e; // list.activeSelectionBackground
120
+ --color-primary-a50: #0078d4; // button.background, focusBorder
121
+ --color-primary-a55: #026ec1; // button.hoverBackground
122
+ --color-primary-a60: #0078d4;
123
+ --color-primary-a70: #026ec1;
124
+ --color-primary-a80: #04395e;
125
+ --color-primary-a90: #cccccc;
126
+ --color-primary-a100: #cccccc; // foreground
127
+
128
+ // what the scale does not give
129
+ --background-ternary: #181818; // sideBar.background
130
+ --background-hover: #2b2b2b; // button.secondaryHoverBackground
131
+ --background-active: #37373d; // list.inactiveSelectionBackground
132
+ --background-input: #313131; // input.background
133
+
134
+ --text-secondary: #9d9d9d; // descriptionForeground
135
+ --text-ternary: #bbbbbb; // chosen: between the description and the text
136
+
137
+ --border: #3c3c3c; // input.border
138
+ --border-light: #2b2b2b; // panel.border
139
+ --border-hover: #616161; // chosen (badge.background)
140
+
141
+ --disabled-background: #313131; // chosen
142
+ --disabled-color: #6e6e6e; // chosen
143
+ --disabled-color-dark: #6e6e6e; // chosen
144
+ --disabled-color-light: #4f4f4f; // chosen
145
+
146
+ --accent-color: #ffffff; // button.foreground: the text on the accent
147
+ --accent-color-hover: #ffffff;
148
+ --accent-color-focus: #ffffff;
149
+ --accent-color-active: #ffffff;
150
+
151
+ // the title bar of a Dialog: the one of the window of VS Code
152
+ --color-dialog-caption: #181818; // titleBar.activeBackground
153
+ --color-dialog-caption-text: #cccccc; // titleBar.activeForeground
154
+ --color-dialog-background: #202020; // editorWidget.background
155
+
156
+ color-scheme: dark; // the native parts: select, scroll bars, number spinners
103
157
  }
@@ -18,9 +18,9 @@
18
18
 
19
19
  :root {
20
20
  --tooltip-caption-background: var( --color-primary-a70 );
21
- --tooltip-caption-color: var( --color-primary-a0 );
21
+ --tooltip-caption-color: var( --accent-color );
22
22
 
23
- --tooltip-icon-background: var( --color-primary-a0 );
23
+ --tooltip-icon-background: var( --accent-color );
24
24
  --tooltip-icon-color: var( --color-primary-a70 );
25
25
 
26
26
  --tooltip-background: var( --color-primary-a10 ); //white;
@@ -42,6 +42,9 @@ export interface ApplicationEvents extends EventMap {
42
42
  // singleton
43
43
  let main_app: Application = null;
44
44
 
45
+ // the class of a theme on <html>: x4theme-<name> (see Application.setTheme)
46
+ const THEME_PREFIX = "x4theme-";
47
+
45
48
  /**
46
49
  * Provides information about the user's device and browser capabilities.
47
50
  */
@@ -201,6 +204,50 @@ export class Application<E extends ApplicationEvents = ApplicationEvents> extend
201
204
  Application.instance().fire( "global", { msg, params } );
202
205
  }
203
206
 
207
+ /**
208
+ * Selects the theme of the page: the class `x4theme-<name>` on `<html>`, in place
209
+ * of the one of the theme before.
210
+ * - `""`: the light theme of x4, the one by default: no class.
211
+ * - `"dark"`: the dark theme of x4, like the theme Dark Modern of VS Code
212
+ * (`html.x4theme-dark` in themes.scss).
213
+ * - any other name: a theme of the application, defined in its own css under
214
+ * `html.x4theme-<name>`, with the variables of themes.scss it changes.
215
+ * @param name - the name of the theme; "" for the light theme.
216
+ * @example
217
+ * Application.setTheme( "dark" ); // the dark theme of x4
218
+ * Application.setTheme( "pink" ); // html.x4theme-pink { --color-primary-a0: ...; } in the css of the application
219
+ * Application.setTheme( "" ); // back to the light theme
220
+ */
221
+
222
+ static setTheme( name: string ) {
223
+ const classes = document.documentElement.classList;
224
+
225
+ const current = Application.getTheme( );
226
+ if( current ) {
227
+ classes.remove( THEME_PREFIX + current );
228
+ }
229
+
230
+ if( name ) {
231
+ classes.add( THEME_PREFIX + name );
232
+ }
233
+ }
234
+
235
+ /**
236
+ * The name of the theme of the page, as setTheme got it: `""` for the light theme
237
+ * of x4, `"dark"` for its dark theme, or the name of a theme of the application.
238
+ */
239
+
240
+ static getTheme( ): string {
241
+ const classes = document.documentElement.classList;
242
+ for( let i = 0; i < classes.length; i++ ) {
243
+ if( classes[i].startsWith( THEME_PREFIX ) ) {
244
+ return classes[i].replace( THEME_PREFIX, "" );
245
+ }
246
+ }
247
+
248
+ return "";
249
+ }
250
+
204
251
  /**
205
252
  *
206
253
  */