janusweb 1.5.31 → 1.5.35

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 (56) hide show
  1. package/CHANGELOG +39 -0
  2. package/media/assets/webui/apps/avatar/avatar.js +2 -1
  3. package/media/assets/webui/apps/buttons/buttons.html +2 -2
  4. package/media/assets/webui/apps/buttons/buttons.js +2 -2
  5. package/media/assets/webui/apps/comms/voip.js +2 -4
  6. package/media/assets/webui/apps/editor/codemirror/addon/hint/javascript-hint.js +162 -0
  7. package/media/assets/webui/apps/editor/codemirror/addon/hint/show-hint.css +36 -0
  8. package/media/assets/webui/apps/editor/codemirror/addon/hint/show-hint.js +529 -0
  9. package/media/assets/webui/apps/editor/codemirror/addon/hint/xml-hint.js +140 -0
  10. package/media/assets/webui/apps/editor/codemirror/keymap/vim.js +5734 -0
  11. package/media/assets/webui/apps/editor/codemirror/lib/codemirror.css +350 -0
  12. package/media/assets/webui/apps/editor/codemirror/lib/codemirror.js +9800 -0
  13. package/media/assets/webui/apps/editor/codemirror/lib/jmldark.css +33 -0
  14. package/media/assets/webui/apps/editor/codemirror/mode/css/css.js +864 -0
  15. package/media/assets/webui/apps/editor/codemirror/mode/htmlmixed/htmlmixed.js +153 -0
  16. package/media/assets/webui/apps/editor/codemirror/mode/javascript/javascript.js +942 -0
  17. package/media/assets/webui/apps/editor/codemirror/mode/xml/xml.js +413 -0
  18. package/media/assets/webui/apps/editor/editor.css +3 -0
  19. package/media/assets/webui/apps/editor/editor.js +407 -9
  20. package/media/assets/webui/apps/editor/editor.json +6 -3
  21. package/media/assets/webui/apps/inventory/inventory.js +2 -2
  22. package/media/assets/webui/apps/locomotion/teleporter.js +8 -7
  23. package/media/assets/webui/apps/navigation/navigation.js +12 -12
  24. package/media/assets/webui/apps/vfs/browserfs/browserfs.js +8 -0
  25. package/media/assets/webui/apps/vfs/vfs.css +52 -0
  26. package/media/assets/webui/apps/vfs/vfs.js +452 -0
  27. package/media/assets/webui/apps/vfs/vfs.json +10 -0
  28. package/media/assets/webui/default.json +2 -1
  29. package/media/assets/webui/themes/default.css +29 -2
  30. package/media/lib/physx/physx.release.js +21 -0
  31. package/media/lib/physx/physx.release.wasm +0 -0
  32. package/media/manifest.json +5 -5
  33. package/media/service-worker.js +7 -0
  34. package/package.json +2 -9
  35. package/scripts/client.js +65 -45
  36. package/scripts/config.js +2 -0
  37. package/scripts/elements/linesegments.js +5 -2
  38. package/scripts/image.js +7 -1
  39. package/scripts/janusbase.js +28 -11
  40. package/scripts/janusghost.js +4 -3
  41. package/scripts/janusparagraph.js +5 -3
  42. package/scripts/janusparticle.js +25 -14
  43. package/scripts/janusplayer.js +25 -10
  44. package/scripts/janusweb.js +22 -3
  45. package/scripts/janusxrplayer.js +4 -1
  46. package/scripts/multiplayermanager.js +3 -3
  47. package/scripts/object.js +172 -82
  48. package/scripts/portal.js +12 -10
  49. package/scripts/room.js +113 -54
  50. package/scripts/sound.js +0 -1
  51. package/scripts/text.js +10 -2
  52. package/scripts/translators/janusvfs.js +20 -0
  53. package/scripts/video.js +1 -1
  54. package/scripts/websurface.js +11 -2
  55. package/utils/build.sh +3 -0
  56. package/utils/init.sh +1 -1
@@ -0,0 +1,52 @@
1
+ janus-ui-vfs-browser ui-flexpanel>ui-treeview {
2
+ flex: 0;
3
+ border-right: 1px solid #333;
4
+ margin-right: 1em;
5
+ padding-right: 1em;
6
+ }
7
+ janus-ui-vfs-browser-directorylist {
8
+ display: grid;
9
+ grid-template-columns: repeat(auto-fit, 90px);
10
+ width: 40em;
11
+ height: 15em;
12
+ overflow: auto;
13
+ outline: 0;
14
+ }
15
+ janus-ui-vfs-browser-directorylist.dragactive {
16
+ border: 2px solid lime;
17
+ border-radius: 10px;
18
+ background: rgba(0,255,0,.2);
19
+ margin: -2px;
20
+ }
21
+ janus-ui-vfs-browser-file.dragactive {
22
+ border: 2px solid lime;
23
+ background: rgba(0,255,0,.2);
24
+ }
25
+ janus-ui-vfs-browser-file {
26
+ width: 80px;
27
+ height: 80px;
28
+ border: 1px solid black;
29
+ border-radius: 10px;
30
+ margin: 5px;
31
+ display: flex;
32
+ text-align: center;
33
+ align-items: flex-end;
34
+ justify-content: center;
35
+ cursor: pointer;
36
+ background: rgba(0,0,0,.2);
37
+ }
38
+ janus-ui-vfs-browser-file[selected] {
39
+ background: rgba(255,255,255,.2);
40
+ }
41
+ janus-ui-vfs-browser-file:hover {
42
+ background: rgba(0,255,0,.2);
43
+ }
44
+ janus-ui-vfs-browser-file[selected]:hover {
45
+ background: rgba(255,255,255,.4);
46
+ }
47
+ janus-ui-vfs-browser-file ui-label {
48
+ width: 100%;
49
+ overflow: hidden;
50
+ text-overflow: ellipsis;
51
+ pointer-events: none;
52
+ }
@@ -0,0 +1,452 @@
1
+ elation.extend('engine.assetloader.agent.janus-vfs', new function() {
2
+ this.initBrowserFS = function() {
3
+ return new Promise((accept, reject) => {
4
+ BrowserFS.configure({
5
+ fs: "MountableFileSystem",
6
+ options: {
7
+ //'/': { fs: "LocalStorage" },
8
+ //'/blurp': { fs: "IndexedDB", options: { storeName: 'janusVFS' } },
9
+ '/': { fs: "IndexedDB", options: { storeName: 'janusVFS' } },
10
+ //'/mnt/usb0': { fs: "LocalStorage" }
11
+ }
12
+ }, (e) => {
13
+ if (e) {
14
+ } else {
15
+ var fs = BrowserFS.BFSRequire('fs');
16
+ this.fs = fs;
17
+ accept(this.fs);
18
+ }
19
+ });
20
+ });
21
+ }
22
+
23
+ this.getFS = async function() {
24
+ if (!this.fs) {
25
+ await this.initBrowserFS();
26
+ }
27
+ return this.fs;
28
+ }
29
+
30
+ this.fetch = async function(url) {
31
+ let fs = await this.getFS();
32
+
33
+ let pathparts = url.split('://');
34
+ return new Promise((resolve, reject) => {
35
+ let filepath = pathparts[1];
36
+ fs.readFile(filepath, ( e, file) => {
37
+ if (file) {
38
+ resolve({
39
+ target: {
40
+ responseURL: url,
41
+ data: file.buffer,
42
+ response: file.buffer,
43
+ getResponseHeader(header) {
44
+ if (header.toLowerCase() == 'content-type') {
45
+ // FIXME - We should implement mime type detection at a lower level, and avoid the need to access the XHR object in calling code
46
+ return 'application/octet-stream';
47
+ }
48
+ }
49
+ },
50
+ });
51
+ } else {
52
+ reject();
53
+ }
54
+ });
55
+ });
56
+ }
57
+ });
58
+
59
+ elation.elements.define('janus.ui.vfs.browser', class extends elation.elements.base {
60
+ init() {
61
+ super.init();
62
+ this.defineAttributes({
63
+ cwd: { type: 'string', default: '/' },
64
+ chroot: { type: 'string', default: '' },
65
+ });
66
+ }
67
+ create() {
68
+ super.create();
69
+
70
+ /*
71
+ this.win = elation.elements.create('ui-window', {
72
+ append: document.body,
73
+ center: true,
74
+ windowtitle: '/',
75
+ });
76
+ */
77
+ this.content = elation.elements.create('ui-rowlayout', {
78
+ append: this,
79
+ });
80
+ this.controls = elation.elements.create('ui-content', {
81
+ append: this.content,
82
+ });
83
+ this.updirbutton = elation.elements.create('ui-button', {
84
+ append: this.controls,
85
+ label: '⬅',
86
+ onclick: ev => this.handleUpDirectoryClick(ev),
87
+ });
88
+ this.deletebutton = elation.elements.create('ui-button', {
89
+ append: this.controls,
90
+ label: '🗑️',
91
+ onclick: ev => this.handleDeleteClick(ev),
92
+ });
93
+ this.addbuttonlist = elation.elements.create('ui-list', {
94
+ selectable: true,
95
+ items: ['Upload', 'Directory', 'HTML', 'JS'],
96
+ onselectionchange: ev => this.handleAddButtonSelect(ev),
97
+ });
98
+ this.addbutton = elation.elements.create('ui-popupbutton', {
99
+ append: this.controls,
100
+ label: '➕',
101
+ popupcontent: this.addbuttonlist,
102
+ });
103
+ this.mainpanel = elation.elements.create('ui-flexpanel', {
104
+ append: this.content,
105
+ });
106
+
107
+ this.dirtree = elation.elements.create('ui-treeview', {
108
+ append: this.mainpanel,
109
+ attrs: {
110
+ name: 'path',
111
+ label: 'name',
112
+ },
113
+ });
114
+ elation.events.add(this.dirtree, 'ui_treeview_select', ev => this.handleDirectoryTreeSelect(ev));
115
+
116
+ this.dirlist = elation.elements.create('janus-ui-vfs-browser-directorylist', {
117
+ append: this.mainpanel,
118
+ itemcomponent: 'janus-ui-vfs-browser-file',
119
+ selectable: true,
120
+ multiselect: true,
121
+ draggable: true,
122
+ });
123
+ elation.events.add(this.dirlist, 'directorychange', ev => this.handleDirectoryChange(ev));
124
+ elation.events.add(this.dirlist, 'selectionchange', ev => this.handleFilesSelectionChange(ev));
125
+
126
+ elation.events.add(this.dirlist, 'dragstart', ev => this.handleDragStart(ev));
127
+ elation.events.add(this.dirlist, 'dragover', ev => this.handleDragOver(ev));
128
+ elation.events.add(this.dirlist, 'dragenter', ev => this.handleDragEnter(ev));
129
+ elation.events.add(this.dirlist, 'dragleave', ev => this.handleDragLeave(ev));
130
+ elation.events.add(this.dirlist, 'drop', ev => this.handleDrop(ev));
131
+
132
+ elation.events.add(this.dirlist, 'keypress', ev => this.handleFilesKeypress(ev));
133
+
134
+ //this.win.setcontent(this);
135
+ this.updateDirectories();
136
+ this.updateFiles();
137
+ }
138
+ async updateFiles() {
139
+ let fs = await elation.engine.assetloader.agent['janus-vfs'].getFS();
140
+
141
+ fs.readdir(this.chroot + this.cwd, (e, contents) => {
142
+ let entries = {};
143
+ let promises = [];
144
+ for (let i = 0; i < contents.length; i++) {
145
+ promises.push(new Promise(accept => {
146
+ fs.stat(this.chroot + this.cwd + contents[i], (e, stats) => {
147
+ entries[contents[i]] = {
148
+ name: contents[i],
149
+ path: this.cwd,
150
+ stats: stats,
151
+ };
152
+ accept(stats);
153
+ });
154
+ }));
155
+ }
156
+ Promise.all(promises).then(() => {
157
+ this.dirlist.clear();
158
+ this.dirlist.setItems(entries);
159
+ });
160
+ });
161
+ this.updirbutton.disabled = (this.cwd == '/');
162
+ this.deletebutton.disabled = (this.dirlist.selection.length == 0);
163
+ //this.win.settitle(this.cwd);
164
+ }
165
+ async updateDirectories() {
166
+ let dirtree = {
167
+ '/': {
168
+ name: '/',
169
+ path: '/',
170
+ items: await this.getDirectoryTree(),
171
+ }
172
+ };
173
+ this.dirtree.setItems(dirtree);
174
+ let selected = this.dirtree.find(this.cwd);
175
+ if (selected) {
176
+ selected.select();
177
+ }
178
+
179
+ console.log('my full dirtree', dirtree, this.dirtree, selected);
180
+ }
181
+ async getDirectoryTree(dir='/', entries) {
182
+ if (!entries) entries = {};
183
+ let fs = await elation.engine.assetloader.agent['janus-vfs'].getFS();
184
+ return new Promise(async (resolve, reject) => {
185
+ fs.readdir(this.chroot + dir, async (e, contents) => {
186
+ let promises = [];
187
+ for (let i = 0; i < contents.length; i++) {
188
+ promises.push(new Promise(accept => {
189
+ let stats = fs.stat(this.chroot + dir + contents[i], async (e, stats) => {
190
+ if (stats.isDirectory()) {
191
+ entries[contents[i]] = {
192
+ name: contents[i],
193
+ path: dir + contents[i] + '/',
194
+ stats: stats,
195
+ };
196
+ let children = await this.getDirectoryTree(dir + contents[i] + '/');
197
+ entries[contents[i]].items = (Object.keys(children).length > 0 ? children : false);
198
+ }
199
+ accept();
200
+ });
201
+ }));
202
+ }
203
+ await Promise.all(promises);
204
+ resolve(entries);
205
+ });
206
+ });
207
+ }
208
+ chdir(dir) {
209
+ this.cwd = dir + (dir.lastIndexOf('/') != dir.length - 1 ? '/' : '');
210
+ this.dirlist.selectall(false);
211
+
212
+ let selected = this.dirtree.find(this.cwd);
213
+ if (selected && selected !== this.dirtree.selected) {
214
+ selected.select();
215
+ } else {
216
+ this.updateFiles();
217
+ }
218
+ }
219
+ updir() {
220
+ let pathparts = this.cwd.split('/');
221
+ pathparts.pop();
222
+ pathparts.pop();
223
+ let newpath = (pathparts.length > 0 ? pathparts.join('/') + '/' : '/');
224
+ //newpath = newpath.replaceAll('//', '/');
225
+ this.chdir(newpath);
226
+ }
227
+ async mkdir(dir) {
228
+ let fs = await elation.engine.assetloader.agent['janus-vfs'].getFS();
229
+ let fulldir = this.chroot + (dir[0] == '/' ? dir : this.cwd + dir);
230
+ await fs.mkdir(fulldir);
231
+ this.updateDirectories();
232
+ this.updateFiles();
233
+ }
234
+ async rm(files) {
235
+ let fs = await elation.engine.assetloader.agent['janus-vfs'].getFS();
236
+ if (!elation.utils.isArray(files)) files = [files];
237
+ console.log('delete files', files);
238
+ let promises = [];
239
+ files.forEach(file => {
240
+ promises.push(new Promise((resolve, reject) => {
241
+ fs.unlink(this.chroot + this.cwd + file, e => {
242
+ if (!e) {
243
+ resolve();
244
+ } else {
245
+ reject();
246
+ }
247
+ });
248
+ }));
249
+ });
250
+ await Promise.all(promises);
251
+ this.updateFiles();
252
+ }
253
+ async rmdir(dirs) {
254
+ let fs = await elation.engine.assetloader.agent['janus-vfs'].getFS();
255
+ if (!elation.utils.isArray(dirs)) dirs = [dirs];
256
+ console.log('delete dirs', dirs);
257
+ let promises = [];
258
+ dirs.forEach(dir => {
259
+ promises.push(new Promise((resolve, reject) => {
260
+ fs.rmdir(this.chroot + this.cwd + dir, e => {
261
+ if (!e) {
262
+ resolve();
263
+ } else {
264
+ reject();
265
+ }
266
+ });
267
+ }));
268
+ });
269
+ await Promise.all(promises);
270
+ this.updateDirectories();
271
+ this.updateFiles();
272
+ }
273
+ showUploadPrompt() {
274
+ let input = document.createElement('input');
275
+ input.type = 'file';
276
+ input.multiple = true;
277
+ input.style.width = 0;
278
+ input.style.height = 0;
279
+ input.style.overflow = 'hidden';
280
+ input.addEventListener('change', ev => this.handleFileUpload(ev));
281
+ this.appendChild(input);
282
+ input.click();
283
+ }
284
+ handleDirectoryTreeSelect(ev) {
285
+ let newpath = ev.data.item.path;
286
+ this.chdir(newpath);
287
+ }
288
+ handleDirectoryChange(ev) {
289
+ let newdir = ev.target;
290
+ let newpath = newdir.path + newdir.name;
291
+ this.chdir(newpath);
292
+ }
293
+ handleUpDirectoryClick(ev) {
294
+ if (!ev.target.disabled) {
295
+ this.updir();
296
+ }
297
+ }
298
+ handleDeleteClick(ev) {
299
+ if (this.dirlist.selection.length > 0) {
300
+ console.log('delete', this.dirlist.selection);
301
+ let files = this.dirlist.selection.filter(n => !n.stats.isDirectory()).map(n => n.name),
302
+ dirs = this.dirlist.selection.filter(n => n.stats.isDirectory()).map(n => n.name);
303
+ if (files.length > 0) this.rm(files);
304
+ if (dirs.length > 0) this.rmdir(dirs);
305
+ this.dirlist.selectall(false);
306
+ }
307
+ }
308
+ handleAddButtonSelect(ev) {
309
+ let selected = this.addbuttonlist.selection[0];
310
+ if (selected) {
311
+ this.addbuttonlist.selectall(false);
312
+ this.addbutton.hidePopup();
313
+
314
+ switch(selected.value) {
315
+ case 'Upload':
316
+ this.showUploadPrompt();
317
+ break;
318
+ case 'Directory':
319
+ let dirname = prompt('Directory name?'); // FIXME - make this an HTML pop-up prompt
320
+ if (dirname !== null) {
321
+ this.mkdir(dirname);
322
+ }
323
+ }
324
+ }
325
+ }
326
+ handleFilesSelectionChange(ev) {
327
+ this.deletebutton.disabled = (this.dirlist.selection.length == 0);
328
+ }
329
+ handleFilesKeypress(ev) {
330
+ if (ev.keyCode == 127) {
331
+ this.handleDeleteClick(ev);
332
+ }
333
+ }
334
+ async handleFileUpload(ev) {
335
+ let files = ev.target.files;
336
+ console.log('upload some files', ev, files);
337
+ if (files && files.length > 0) {
338
+ let filenames = await this.saveFiles(files);
339
+ console.log('finished writing files', filenames);
340
+ }
341
+ }
342
+ handleDragStart(ev) {
343
+ let urilist = '';
344
+ this.dirlist.selection.forEach(f => {
345
+ urilist += 'janus-vfs://' + this.chroot + this.cwd + f.name + '\r\n';
346
+ });
347
+ ev.dataTransfer.setData('text/uri-list', urilist);
348
+ console.log('drag it!', urilist, ev, this.dirlist.selection);
349
+ }
350
+ handleDragOver(ev) {
351
+ ev.dataTransfer.dropEffect = "copy"
352
+ ev.preventDefault();
353
+ }
354
+ handleDragEnter(ev) {
355
+ let dragtarget = this.getDragTarget(ev);
356
+ if (dragtarget && !dragtarget.hasclass('dragactive')) {
357
+ dragtarget.addclass('dragactive');
358
+ }
359
+ }
360
+ handleDragLeave(ev) {
361
+ //let dragtarget = this.getDragTarget(ev);
362
+ let dragtarget = ev.target;
363
+ if (dragtarget && elation.html.hasclass(dragtarget, 'dragactive')) elation.html.removeclass(dragtarget, 'dragactive');
364
+ if (this.dirlist.hasclass('dragactive') && !this.dirlist.contains(dragtarget)) this.dirlist.removeclass('dragactive');
365
+ }
366
+ async handleDrop(ev) {
367
+ ev.preventDefault();
368
+ let dragtarget = this.getDragTarget(ev);
369
+ if (dragtarget) dragtarget.removeclass('dragactive');
370
+ let files = ev.dataTransfer.files;
371
+ let subdir = (dragtarget instanceof elation.elements.janus.ui.vfs.browser.file && dragtarget.stats.isDirectory() ? dragtarget.name : '');
372
+ console.log('upload some files', ev, files);
373
+ if (files && files.length > 0) {
374
+ let filenames = await this.saveFiles(files, subdir);
375
+ console.log('finished writing files', filenames);
376
+ }
377
+ }
378
+ getDragTarget(ev) {
379
+ let dragtarget = ev.target;
380
+
381
+ while (dragtarget !== this.dirlist && !(dragtarget instanceof elation.elements.janus.ui.vfs.browser.file && dragtarget.stats.isDirectory())) {
382
+ dragtarget = dragtarget.parentNode;
383
+ }
384
+ console.log('my dragtarget!', dragtarget);
385
+ return dragtarget;
386
+ }
387
+ async saveFiles(files, subdir) {
388
+ let fs = await elation.engine.assetloader.agent['janus-vfs'].getFS();
389
+
390
+ let Buffer = BrowserFS.BFSRequire('buffer').Buffer;
391
+ let promises = [];
392
+ for (let i = 0; i < files.length; i++) {
393
+ let file = files[i];
394
+ let fname = this.chroot + this.cwd + (subdir ? subdir + '/' : '') + file.name;
395
+ console.log('Got file!', file.name, file.size, fname);
396
+ promises.push(new Promise((accept, reject) => {
397
+ let reader = new FileReader();
398
+ reader.onload = function() {
399
+ let buffer = Buffer.from(this.result);
400
+ fs.writeFile(fname, buffer, (e, f) => {
401
+ accept(fname);
402
+ });
403
+ }
404
+ reader.readAsArrayBuffer(file);
405
+ }));
406
+ }
407
+ Promise.all(promises).then((filenames) => {
408
+ this.updateFiles();
409
+ return filenames;
410
+ });
411
+ }
412
+ });
413
+ elation.elements.define('janus.ui.vfs.browser.directorytree', class extends elation.elements.ui.treeview {
414
+ create() {
415
+ super.create();
416
+ }
417
+ });
418
+ elation.elements.define('janus.ui.vfs.browser.directorylist', class extends elation.elements.ui.list {
419
+ create() {
420
+ super.create();
421
+ }
422
+ });
423
+ elation.elements.define('janus.ui.vfs.browser.directory', class extends elation.elements.ui.item {
424
+ create() {
425
+ }
426
+ });
427
+ elation.elements.define('janus.ui.vfs.browser.file', class extends elation.elements.ui.item {
428
+ create() {
429
+ super.create();
430
+
431
+ this.name = this.value.name;
432
+ this.path = this.value.path;
433
+ this.stats = this.value.stats;
434
+
435
+ this.title = this.name;
436
+
437
+ elation.elements.create('ui-label', {
438
+ append: this,
439
+ label: this.name + (this.stats.isDirectory() ? '/' : ''),
440
+ });
441
+
442
+ this.addEventListener('dblclick', ev => this.handleDblClick(ev));
443
+ }
444
+ handleDblClick(ev) {
445
+ if (this.stats.isDirectory()) {
446
+ this.dispatchEvent({type: 'directorychange', bubbles: true});
447
+ } else {
448
+ console.log('do something with this thing!', this);
449
+ }
450
+ ev.preventDefault();
451
+ }
452
+ });
@@ -0,0 +1,10 @@
1
+ {
2
+ "templates": {
3
+ },
4
+ "scripts": ["./vfs.js", "./browserfs/browserfs.js"],
5
+ "css": ["./vfs.css"],
6
+ "components": [
7
+ "janus-ui-vfs-browser"
8
+ ]
9
+ }
10
+
@@ -10,7 +10,8 @@
10
10
  "locomotion": "./apps/locomotion/locomotion.json",
11
11
  "virtualgamepad": "./apps/virtualgamepad/virtualgamepad.json",
12
12
  "buttons": "./apps/buttons/buttons.json",
13
- "xrmenu": "./apps/xrmenu/xrmenu.json"
13
+ "xrmenu": "./apps/xrmenu/xrmenu.json",
14
+ "vfs": "./apps/vfs/vfs.json"
14
15
  },
15
16
  "includes": [
16
17
  ],
@@ -71,6 +71,10 @@ ui-list>ui-item[hover],
71
71
  ui-checklist>ui-checklistitem[hover] {
72
72
  background-color: rgba(128,128,255,.05);
73
73
  }
74
+ ui-list[selectable]>ui-item[hover] {
75
+ background-color: rgba(128,128,255,.2);
76
+ cursor: pointer;
77
+ }
74
78
  ui-list>ui-item.state_selected,
75
79
  ui-checklist>ui-checklistitem.state_selected {
76
80
  background: rgba(128,128,255,.5);
@@ -184,6 +188,10 @@ ui-tabs>ui-tabbar>ui-tabcountbutton {
184
188
  margin-bottom: 0;
185
189
  white-space: nowrap;
186
190
  display: inline-block;
191
+ max-width:10em;
192
+ text-overflow: ellipsis;
193
+ direction: rtl;
194
+ overflow: hidden;
187
195
  }
188
196
  ui-tabs>ui-tabbar>ui-button[selected],
189
197
  ui-tabs>ui-tabbar>ui-button[selected][hover],
@@ -272,6 +280,8 @@ ui-window {
272
280
  border: 1px solid #666;
273
281
  box-shadow: 1px 1px 10px rgba(0,0,0,1);
274
282
  color: white;
283
+ display: flex;
284
+ flex-direction: column;
275
285
  }
276
286
  ui-window>ui-window-titlebar {
277
287
  display: flex;
@@ -286,6 +296,7 @@ ui-window>ui-window-titlebar {
286
296
  white-space: nowrap;
287
297
  vertical-align: middle;
288
298
  border-bottom: 1px solid #666;
299
+ order: 0;
289
300
  }
290
301
  ui-window.state_movable>ui-window-titlebar:hover {
291
302
  cursor: -webkit-grab;
@@ -331,6 +342,9 @@ ui-window>.ui_window_content {
331
342
  -moz-box-sizing: border-box;
332
343
  position: relative;
333
344
  }
345
+ ui-window>ui-window-content {
346
+ order: 1;
347
+ }
334
348
  ui-window[scrollable]>ui-window-content {
335
349
  overflow: auto;
336
350
  display: block;
@@ -673,6 +687,7 @@ ui-button,
673
687
  ui-togglebutton,
674
688
  ui-dropdownbutton,
675
689
  ui-tabcountbutton,
690
+ ui-popupbutton,
676
691
  ui-notificationbutton {
677
692
  display: inline-block;
678
693
  border: none;
@@ -687,11 +702,13 @@ ui-notificationbutton {
687
702
 
688
703
  background-image: linear-gradient(#484e55, #3a3f44 60%, #313539);
689
704
  background-color: transparent;
705
+ position: relative;
690
706
  }
691
707
  ui-button[hover],
692
708
  ui-togglebutton[hover],
693
709
  ui-dropdownbutton[hover],
694
710
  ui-tabcountbutton[hover],
711
+ ui-popupbutton[hover],
695
712
  ui-notificationbutton[hover] {
696
713
  background-image: linear-gradient(#323232, #404142 40%, #393b3d);
697
714
  }
@@ -699,6 +716,7 @@ ui-button:active,
699
716
  ui-togglebutton:active,
700
717
  ui-dropdownbutton:active,
701
718
  ui-tabcountbutton:active,
719
+ ui-popupbutton:active,
702
720
  ui-notificationbutton:active {
703
721
  background-image: linear-gradient(#020202, #101112 40%, #191b1d);
704
722
  }
@@ -706,9 +724,10 @@ ui-button[disabled],
706
724
  ui-togglebutton:disabled,
707
725
  ui-dropdownbutton:disabled,
708
726
  ui-tabcountbutton:disabled,
727
+ ui-popupbutton:disabled,
709
728
  ui-notificationbutton:disabled {
710
729
  color: #999;
711
- background-image: linear-gradient(#484e55, #3a3f44 60%, #313539);
730
+ background-image: linear-gradient(#383e45, #2a2f34 60%, #212549);
712
731
  cursor: not-allowed;
713
732
  }
714
733
 
@@ -1034,6 +1053,7 @@ ui-treeview {
1034
1053
  background: black;
1035
1054
  color: white;
1036
1055
  */
1056
+ display: block;
1037
1057
  }
1038
1058
  ui-treeview ui-list {
1039
1059
  /*background: rgba(0,0,0,.8);*/
@@ -1060,8 +1080,12 @@ ui-treeview ui-treeviewitem {
1060
1080
  order: 2;
1061
1081
  padding-left: 1em;
1062
1082
  }
1083
+ ui-treeview ui-treeviewitem[collapsed]>ui-treeviewitem {
1084
+ display: none;
1085
+ }
1063
1086
  ui-treeview ui-treeviewitem>ui-text {
1064
1087
  order: 1;
1088
+ user-select: none;
1065
1089
  }
1066
1090
  ui-treeview ui-treeviewitem:hover>ui-text {
1067
1091
  color: #9c9;
@@ -1083,11 +1107,14 @@ ui-treeview ui-treeviewitem ui-text:before {
1083
1107
  float: left;
1084
1108
  clear: left;
1085
1109
  width: .8em;
1086
- margin-left: -1.2em;
1110
+ padding-right: .5em;
1087
1111
  }
1088
1112
  ui-treeview ui-treeviewitem.haschildren>ui-text:before {
1089
1113
  content: '▼';
1090
1114
  }
1115
+ ui-treeview ui-treeviewitem.haschildren[collapsed]>ui-text:before {
1116
+ content: '▶';
1117
+ }
1091
1118
  ui-treeview ui-treeviewitem:last-child>ul {
1092
1119
  border-left: 0;
1093
1120
  }