diff --git a/3parties/xterm-addon-fit.js b/3parties/xterm-addon-fit.js new file mode 100644 index 0000000..709e60a --- /dev/null +++ b/3parties/xterm-addon-fit.js @@ -0,0 +1,2 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(window,function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(){}return e.prototype.activate=function(e){this._terminal=e},e.prototype.dispose=function(){},e.prototype.fit=function(){var e=this.proposeDimensions();if(e&&this._terminal){var t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}},e.prototype.proposeDimensions=function(){if(this._terminal&&this._terminal.element&&this._terminal.element.parentElement){var e=this._terminal._core,t=window.getComputedStyle(this._terminal.element.parentElement),r=parseInt(t.getPropertyValue("height")),n=Math.max(0,parseInt(t.getPropertyValue("width"))),o=window.getComputedStyle(this._terminal.element),i=r-(parseInt(o.getPropertyValue("padding-top"))+parseInt(o.getPropertyValue("padding-bottom"))),a=n-(parseInt(o.getPropertyValue("padding-right"))+parseInt(o.getPropertyValue("padding-left")))-e.viewport.scrollBarWidth;return{cols:Math.max(2,Math.floor(a/e._renderService.dimensions.actualCellWidth)),rows:Math.max(1,Math.floor(i/e._renderService.dimensions.actualCellHeight))}}},e}();t.FitAddon=n}])}); +//# sourceMappingURL=xterm-addon-fit.js.map \ No newline at end of file diff --git a/bricks/asr.js b/bricks/asr.js new file mode 100644 index 0000000..5055818 --- /dev/null +++ b/bricks/asr.js @@ -0,0 +1,75 @@ +var bricks = window.bricks || {}; +bricks.ASRClient = class extends bricks.VBox { + /* + options: + { + start_icon:record.png, + stop_icon:stop.png + ws_url: + icon_options + ws_params: + } + event: + start: start recording, no params + stop: stop recording, no params + transtext: recognised text, params={ + "content": + "speaker": + "start": + "end": + } + */ + constructor(opts){ + super(opts); + var icon_options = this.icon_options || {}; + icon_options.url = this.start_icon || bricks_resource('imgs/start_recording.png'); + this.icon = new bricks.Icon(icon_options); + this.status = 'stop'; + this.icon.bind('click', this.toggle_button.bind(this)); + this.add_widget(this.icon); + this.socket = new WebSocket(this.ws_url); + this.socket.onmessage = this.response_data.bind(this); + this.bind('transtext', this.response_log.bind(this)); + } + response_log(event){ + console.log('response data=', event.params); + } + toggle_button(){ + if (this.status == 'stop'){ + this.icon.set_url(this.start_icon||bricks_resource('imgs/stop_recording.png')); + this.status = 'start'; + this.start_recording(); + } else { + this.icon.set_url(this.stop_icon||bricks_resource('imgs/start_recording.png')); + this.status = 'stop'; + this.stop_recording(); + } + } + async start_recording() { + this.stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + this.mediaRecorder = new MediaRecorder(this.stream); + this.mediaRecorder.ondataavailable = (event) => { + if (event.data.size > 0) { + // 将音频数据通过 WebSocket 发送到服务器 + blobToBase64(event.data).then((b64str) => { + var d = objcopy(this.ws_params); + d.type = 'audiobuffer'; + d.data = b64str; + this.socket.send(JSON.stringify(d)); + }).catch((error) => { + console.log('Error', error); + }); + } + } + this.mediaRecorder.start(1000); // 每 1 秒发送一次数据 + } + stop_recording(){ + this.mediaRecorder.stop(); + } + response_data(event){ + var d = JSON.parse(event.data); + this.dispatch('transtext', d); + } +} + +bricks.Factory.register('ASRClient', bricks.ASRClient); diff --git a/bricks/audio.js b/bricks/audio.js old mode 100755 new mode 100644 index d8b687c..38fb3ef --- a/bricks/audio.js +++ b/bricks/audio.js @@ -26,7 +26,7 @@ bricks.AudioPlayer = class extends bricks.JsWidget { this.audio = this._create('audio'); this.audio.controls = true; if (this.opts.autoplay){ - this.audio.addEventListener('canplay', this.play_audio.bind(this)); + this.audio.addEventListener('canplay', this.play.bind(this)); } this.audio.style.width = "100%" this.dom_element.appendChild(this.audio); @@ -34,6 +34,58 @@ bricks.AudioPlayer = class extends bricks.JsWidget { this.set_source(this.url); } } + + set_stream_urls(response){ + async function* dyn_urls(response) { + const reader = response.body.getReader(); + var value; + var done; + while (true){ + done, value = await reader.read(); + if (value.done){ + console.log('done=', done, 'value=', value); + break; + } + let result = ''; + for (let i = 0; i < value.value.length; i++) { + result += String.fromCharCode(value.value[i]); + } + console.log('audio set url=', result); + yield result; + } + } + this.url_generator = dyn_urls(response); + this.srcList = []; + schedule_once(this.load_queue_url.bind(this), 0.1); + } + async load_queue_url(){ + while (true){ + var d = await this.url_generator.next(); + if (d.done){ + return; + } + this.srcList.push({played:false, url:d.value}); + if (this.srcList.length < 2 ){ + await this.play_srclist(); + this.audio.addEventListener('ended', + this.play_srclist.bind(this)); + } + } + } + async play_srclist(evnet){ + if (event && ! this.audio.ended){ + return; + } + for (var i=0;i { + this.screen_orient = window.screen.orientation.type; + this.bind('orient_changed', this.screen_orient); + }); } + async getCameras() { + try { + const devices = await navigator.mediaDevices.enumerateDevices(); + this.video_devices = devices.filter(device => device.kind === 'videoinput'); + } catch (error) { + console.error('获取摄像头数量出错:', error); + } + } + async start_camera(vpos) { + if (typeof(vpos) == 'undefined') vpos = 0; + if (this.video_devices === null){ + await this.getCameras(); + } + if (vpos == this.vpos) return; + this.vpos = vpos; + if (this.video_stream){ + this.video_stream.getTracks().forEach(track => { + track.stop(); + }); + } + if (navigator.mediaDevices.getUserMedia) { + var x = { deviceId: this.video_devices[vpos].deviceId }; + this.video_stream = await navigator.mediaDevices.getUserMedia({ video: x }); + } else { + console.log("Webcam access is not supported in this browser."); + } + } + async start_mic() { + if (this.audio_stream) return; + if (navigator.mediaDevices.getUserMedia) { + this.audio_stream = navigator.mediaDevices.getUserMedia({ audio: true }); + } else { + console.log("mic access is not supported in this browser."); + this.stream = null; + } + } new_zindex(){ const v = this.zindex; this.zindex = v + 1; diff --git a/bricks/build.sh b/bricks/build.sh index 8a1a0f7..0590691 100755 --- a/bricks/build.sh +++ b/bricks/build.sh @@ -1,14 +1,15 @@ SOURCES=" page_data_loader.js factory.js uitypesdef.js utils.js uitype.js \ - i18n.js widget.js layout.js bricks.js image.js html.js \ - jsoncall.js myoperator.js scroll.js menu.js popup.js modal.js running.js \ + i18n.js widget.js layout.js bricks.js image.js html.js splitter.js \ + jsoncall.js myoperator.js scroll.js menu.js popup.js camera.js modal.js running.js \ markdown_viewer.js video.js audio.js toolbar.js tab.js \ input.js registerfunction.js button.js accordion.js dataviewer.js \ tree.js multiple_state_image.js dynamiccolumn.js form.js message.js conform.js \ paging.js datagrid.js iframe.js cols.js echartsext.js \ floaticonbar.js miniform.js wterm.js dynamicaccordion.js \ - binstreaming.js streaming_audio.js vadtext.js rtc.js \ + binstreaming.js streaming_audio.js vadtext.js rtc.js docxviewer.js \ llm_dialog.js llm.js websocket.js datarow.js tabular.js \ - line.js pie.js bar.js gobang.js " + line.js pie.js bar.js gobang.js period.js iconbarpage.js \ + keypress.js asr.js webspeech.js countdown.js progressbar.js " echo ${SOURCES} cat ${SOURCES} > ../dist/bricks.js # uglifyjs --compress --mangle -- ../dist/bricks.js > ../dist/bricks.min.js @@ -38,4 +39,5 @@ cp -a ../examples/* ../dist/examples cp -a ../3parties/* ../dist/3parties cp -a ../docs/* ../dist/docs cp *.tmpl ../dist +cp -a ../dist /tmp echo "Finished ..." diff --git a/bricks/camera.js b/bricks/camera.js new file mode 100644 index 0000000..397c331 --- /dev/null +++ b/bricks/camera.js @@ -0,0 +1,99 @@ +var bricks = window.bricks || {} +bricks.Camera = class extends bricks.Popup { + /* + { + fps:60 + } + */ + constructor(opts){ + opts.fps = opts.fps || 60; + opts.auto_dismiss = false; + super(opts); + this.stream = null; + this.video = document.createElement('video'); + var filler = new bricks.Filler({}); + var hbox = new bricks.HBox({ + cheight:3 + }); + this.cur_camera_id = 0; + this.add_widget(filler); + this.add_widget(hbox); + var shot_btn = new bricks.Icon({ + url:bricks_resource('imgs/camera.png'), + margin: '10px', + tip:'Take a picture', + rate:2.5 + }); + var switch_btn = new bricks.Icon({ + url:bricks_resource('imgs/switch-camera.png'), + tip:'switch camera', + margin: '10px', + rate:1.5 + }); + var del_btn = new bricks.Icon({ + url:bricks_resource('imgs/delete.png'), + tip:'canel it', + margin: '10px', + rate:1.5 + }) + del_btn.bind('click', this.dismiss.bind(this)); + shot_btn.bind('click', this.take_picture.bind(this)); + switch_btn.bind('click', this.switch_camera.bind(this, switch_btn)); + this.imgw = new bricks.Image({ + width:'100%' + }); + hbox.add_widget(switch_btn); + hbox.add_widget(shot_btn); + hbox.add_widget(new bricks.Filler({})); + hbox.add_widget(del_btn); + filler.add_widget(this.imgw); + this.task_period = 1 / this.fps; + schedule_once(this.startCamera.bind(this), 0.1); + } + async switch_camera(btn, event){ + if (bricks.app.video_devices.length < 2){ + btn.disabled(true); + return; + } + var vpos = bricks.app.vpos; + vpos += 1; + if (vpos >= bricks.app.video_devices.length){ + vpos = 0; + } + this.startCamera(vpos); + } + async startCamera(vpos) { + await bricks.app.start_camera(vpos); + this.stream = bricks.app.video_stream; + this.video.srcObject = this.stream; + this.video.play(); + this.show_cnt = 1; + this.task = schedule_once(this.show_picture.bind(this), this.task_period); + } + show_picture(){ + if (this.task_period == 0){ + return; + } + var canvas = document.createElement('canvas'); + canvas.height = this.video.videoHeight; + canvas.width = this.video.videoWidth; + const context = canvas.getContext('2d'); + context.drawImage(this.video, 0, 0); + this.imgw.set_url(canvas.toDataURL('image/jpeg')); + this.task = schedule_once(this.show_picture.bind(this), this.task_period); + this.show_cnt += 1; + } + take_picture(event){ + event.stopPropagation(); + if (this.task){ + task.cancel(); + this.task = null; + } + this.task_period = 0; + this.task = null; + var d = this.imgw.base64(); + this.dispatch('shot', d); + } +} + +bricks.Factory.register('Camera', bricks.Camera); diff --git a/bricks/cols.js b/bricks/cols.js index 843db02..5574c69 100644 --- a/bricks/cols.js +++ b/bricks/cols.js @@ -55,6 +55,7 @@ bricks.Cols = class extends bricks.VBox { } async handle_click(rw, event){ + event.stopPropagation(); var orev = null; if (this.select_record){ orev = this.select_record; @@ -64,6 +65,8 @@ bricks.Cols = class extends bricks.VBox { } this.select_record = rw; this.select_record.set_css('selected_record'); + console.log('record data=', rw.user_data); + this.dispatch('record_click', rw.user_data); } async dataHandle(d){ var data = d.rows; @@ -71,18 +74,20 @@ bricks.Cols = class extends bricks.VBox { if (!data){ return; } - if (! this.loader.is_max_page(page)){ + var rev = ! this.loader.is_max_page(page); + if (rev){ data.reverse(); } for (var i=0;i { - if (! edit_names.incloudes(t.name)){ + if (! edit_names.includes(t.name)){ tbdesc.tools.push(t); } }); @@ -168,12 +168,13 @@ bricks.DataViewer = class extends bricks.VBox { this.delete_record(this.select_row); return; } - var d = { - tabular:this, - row:this.select_row, - data:row.user_data + var data = null; + if (this.select_row){ + var r = this.select_row; + var data = r.user_data; } - this.dispathc(t.name. d); + console.log(tdesc.name, 'clicked ==================', tdesc.name, data) + this.dispatch(tdesc.name, data); } get_edit_fields(){ var fs = this.row_options.fields; @@ -193,7 +194,8 @@ bricks.DataViewer = class extends bricks.VBox { this.dispatch('row_check_changed', event.params.user_data); } async renew_record_view(form, row){ - var d = form.getValue(); + var d = form._getValue(); + d = form._getValue(); var record = bricks.extend(row.user_data, d); row.renew(record); } @@ -215,6 +217,11 @@ bricks.DataViewer = class extends bricks.VBox { fs.push(this.fields[i]); } var f = new bricks.ModalForm({ + "widget":this, + "archor":"cc", + "movable":true, + "resizable":true, + "archor":"cc", "width":"90%", "height":"70%", "submit_url":this.editable.new_data_url, @@ -225,10 +232,10 @@ bricks.DataViewer = class extends bricks.VBox { } async add_record_finish(f, event){ f.dismiss(); + this.render(); var resp = event.params; var desc = await resp.json(); var w = await bricks.widgetBuild(desc); - this.render(); } update_record(){ var record = this.select_row.user_data; @@ -239,6 +246,10 @@ bricks.DataViewer = class extends bricks.VBox { fields.push(f); } var f = new bricks.ModalForm({ + "widget":this, + "movable":true, + "resizable":true, + "archor":"cc", "width":"90%", "height":"70%", "submit_url":this.editable.update_data_url+'?id=' + record.id, @@ -251,15 +262,15 @@ bricks.DataViewer = class extends bricks.VBox { f.dismiss(); } async update_record_finish(form, event){ + await this.renew_record_view(form, this.select_row); var resp = event.params; var desc = await resp.json(); var w = await bricks.widgetBuild(desc); - if (desc.widgettype == 'Message'){ - await this.renew_record_view(form, this.select_row); - } } delete_record(row, record){ var conform_w = new bricks.Conform({ + cwidth:16, + cheight:9, target:this, title:'Delete conform', message:'Are you sure to delete is record?' @@ -315,8 +326,8 @@ bricks.DataViewer = class extends bricks.VBox { var d = await this.loader.loadNextPage(); if (d){ this.dataHandle(d); - var total = this.scrollpanel.dom_element.scrollHeight - this.scrollpanel.dom_element.clientHeight; - this.scrollpanel.dom_element.scrollTop = d.pos_rate * total; + // var total = this.scrollpanel.dom_element.scrollHeight - this.scrollpanel.dom_element.clientHeight; + // this.scrollpanel.dom_element.scrollTop = d.pos_rate * total; } else { bricks.debug(this.loader, 'load next page error'); } diff --git a/bricks/docxviewer.js b/bricks/docxviewer.js new file mode 100644 index 0000000..d990187 --- /dev/null +++ b/bricks/docxviewer.js @@ -0,0 +1,119 @@ +/* need mammoth module + +*/ +var bricks = window.bricks || {}; + +bricks.DOCXviewer = class extends bricks.VBox { + /* + url: + */ + constructor(opts){ + super(opts); + this.bind('on_parent', this.set_url.bind(this)); + // schedule_once(this.set_url.bind(this, this.url), 0.2); + } + async set_url(url){ + var container = this.dom_element + var hab = new bricks.HttpArrayBuffer(); + var ab = await hab.get(this.url); + var result = await mammoth.convertToHtml({ arrayBuffer: ab }); + container.innerHTML = result.value; + } +} + +function extractBodyContent(htmlString) { + // 正则表达式匹配和之间的内容 + const regex = /]*>([\s\S]*?)<\/body>/i; + const matches = htmlString.match(regex); + return matches ? matches[1] : null; // 如果匹配到,返回匹配的内容,否则返回null +} + +bricks.EXCELviewer = class extends bricks.VBox { + constructor(opts){ + opts.height = "100%", + super(opts); + this.sheets_w = new bricks.HBox({cheight:3, width:'100%'}); + this.sheets_w.set_css('scroll'); + this.cur_sheetname = null; + this.container = new bricks.Filler({}); + this.add_widget(this.container); + this.add_widget(this.sheets_w); + this.bind('on_parent', this.set_url.bind(this)); + } + async set_url(url){ + this.sheets_w.clear_widgets(); + var hab = new bricks.HttpArrayBuffer(); + var ab = await hab.get(this.url); + const data = new Uint8Array(ab); + this.workbook = XLSX.read(data, {type: 'array'}); + this.workbook.SheetNames.forEach((sheetname, index) => { + var w = new bricks.Text({text:sheetname, wrap:false}); + w.set_css('clickable'); + w.set_style('padding', '10px'); + w.bind('click', this.show_sheet_by_name.bind(this, sheetname, w)); + this.sheets_w.add_widget(w); + if (index==0){ + this.show_sheet_by_name(this.workbook.SheetNames[0], w); + } + }); + } + show_sheet_by_name(sheetname, tw){ + if (this.cur_sheetname == sheetname) return; + this.sheets_w.children.forEach(c => c.set_css('selected', true)); + tw.set_css('selected'); + const x = new bricks.VScrollPanel({width: '100%', height: '100%'}); + const sheet = this.workbook.Sheets[sheetname]; + // const html = extractBodyContent(XLSX.utils.sheet_to_html(sheet)); + const html = XLSX.utils.sheet_to_html(sheet); + x.dom_element.innerHTML = html; + this.container.clear_widgets(); + this.container.add_widget(x); + this.cur_sheetname = sheetname; + } +} + +bricks.PDFviewer = class extends bricks.VBox { + /* + url: + */ + constructor(opts){ + opts.width = '100%'; + super(opts); + this.bind('on_parent', this.set_url.bind(this)); + } + async set_url(url){ + var container = this.dom_element + var hab = new bricks.HttpArrayBuffer(); + var ab = await hab.get(this.url); + const task = pdfjsLib.getDocument({ data: ab }); + task.promise.then((pdf) => { + this.pdf = pdf; + for (let i = 1; i <= this.pdf.numPages; i++) { + this.pdf.getPage(i).then((page) => { + this.add_page_content(page); + }); + } + }).catch((err) => { + console.log('error'); + }) + } + add_page_content(page){ + const scale = 1.5; + const viewport = page.getViewport({ scale }); + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + canvas.height = viewport.height; + canvas.width = viewport.width; + page.render({ canvasContext: context, viewport }); + var w = new bricks.JsWidget(); + w.dom_element.appendChild(canvas); + this.add_widget(w); + if (i < this.pdf.numPages){ + w = new bricks.Splitter(); + this.add_widget(w) + } + } +} +bricks.Factory.register('DOCXviewer', bricks.DOCXviewer); +bricks.Factory.register('EXCELviewer', bricks.EXCELviewer); +bricks.Factory.register('PDFviewer', bricks.PDFviewer); diff --git a/bricks/dynamiccolumn.js b/bricks/dynamiccolumn.js index 70c497e..569ecfa 100644 --- a/bricks/dynamiccolumn.js +++ b/bricks/dynamiccolumn.js @@ -30,21 +30,19 @@ bricks.DynamicColumn = class extends bricks.Layout { var cw; var cols; var gap; - var width = this.get_width(); - if (this.col_cwidth){ - cw = bricks.app.charsize * this.col_cwidth; - } else { - cw = this.col_width; - } gap = bricks.app.charsize * (this.col_cgap || 0.1); - if (width > 0){ - if (bricks.is_mobile() && (width < this.get_height())){ - cols = this.mobile_cols || 1; - } else { - cols = (width + gap) / (cw + gap) - } - cols = Math.floor(width/cw); + var width = bricks.app.screenWidth(); + var height = bricks.app.screenHeight(); + if (bricks.is_mobile() && width < height){ + cols = this.mobile_cols; cw = (width - (cols - 1) * gap) / cols; + console.log('====mobile==cols=', cols, '===='); + } else { + if (this.col_cwidth){ + cw = bricks.app.charsize * this.col_cwidth; + } else { + cw = this.col_width; + } } this.dom_element.style.gridTemplateColumns = "repeat(auto-fill, minmax(" + cw + "px, 1fr))"; this.set_style('gap', gap + 'px'); diff --git a/bricks/echartsext.js b/bricks/echartsext.js index f9d0881..65f896b 100644 --- a/bricks/echartsext.js +++ b/bricks/echartsext.js @@ -83,6 +83,7 @@ bricks.EchartsExt = class extends bricks.VBox { this.valueFields = this.get_series(data); data = this.pivotify(data); } + this.chart = echarts.init(this.holder.dom_element); var opts = this.setup_options(data); opts.grid = { left: '3%', diff --git a/bricks/factory.js b/bricks/factory.js old mode 100755 new mode 100644 diff --git a/bricks/floaticonbar.js b/bricks/floaticonbar.js index a65670b..eeb0a02 100644 --- a/bricks/floaticonbar.js +++ b/bricks/floaticonbar.js @@ -20,10 +20,22 @@ bricks.IconBar = class extends bricks.HBox { if (! opts.cheight){ opts.cheight = 2; } + if (! opts.rate){ + opts.rate = 1; + } super(opts); + this.set_css('childrensize'); + this.height_int = 0; var tools = this.opts.tools; for (var i=0;i - - - + + + + - - - + + + + - - + + +