open-olive / loop-development-kit Goto Github PK
View Code? Open in Web Editor NEWThe JavaScript/TypeScript Loop Development Kit for Olive Helps.
License: MIT License
The JavaScript/TypeScript Loop Development Kit for Olive Helps.
License: MIT License
const testEncodedString = await network.encode('Hi');
await filesystem.writeFile(
'/Users/michaellatman/Desktop/test.txt',
testEncodedString,
WriteOperation.overwrite,
**????? What can go here?**
);
When I use Microsoft Teams to share a screen in a meeting, it looks like OliveHelps sidebar doesn't work correctly.
Here're the steps to reproduce.
It looks like Google Hangout doesn't have the same problem.
Since the problem won't happen when not using Teams to share screen, you may put this issue at a lower priority.
OliveHelps version: 0.16.2
OS: Windows 10 x64 build 19041.928
Some Loop Authors have requested to allow a Loop to report which operating system it is currently executing under.
Some reasons include:
Currently, the LDK does include filesystem.join(string[]) that "joins an array of path elements into a single path, separating them with an OS specific Separator"
The LDK also supports meta
, metaLeft
, metaRight
keys that will map operating system specific keys (cmd for Mac, windows key for Windows).
We'd like to OCR the entire display such that we can extract patient numbers and other info from applications.
We want to save new files into the user's downloads folder. But, the directory location is operating system dependent, and the loop isn't aware of the running user to locate their user directory.
const third: UpdateWhisper = {
label: 'Third',
components: [
{
type: WhisperComponentType.TextInput,
label: 'Search',
value: '',
onChange: (error, value, whisper) => {
console.log('Never gets called!!!');
},
},
],
};
const second: UpdateWhisper = {
label: 'Second',
components: [
{
type: WhisperComponentType.TextInput,
label: 'awdaw',
value: '',
onChange: (error, value, whisper) => {
console.log('We are here');
whisper.update(third);
},
},
],
};
(async () => {
const first = await whisper.create({
label: 'First',
components: [
{
type: WhisperComponentType.TextInput,
label: 'Search',
value: '',
onChange: (error, value, whisper) => {
whisper.update(second);
},
},
],
onClose: () => {},
});
})();
Expected:
Acutal:
"As a loop author, I would like a way to make http calls from the network aptitude so that I can use local endpoints for development"
The network aptitude currently only allows for https
calls to be made. This does not allow for an easy way to hit localhost
from a loop installed locally for development purposes. It would be nice if some sort of method or override was provided that allowed for http
calls to made in local loops for faster development iterations.
await filesystem.listenFile(
'/Users/michaellatman/Desktop/lol.txt',
event => {
console.info('Received Event', event);
}
);
The loop stops working, and changing the code to remove the filesystem does not fix the issue until you can reinstall the loop from scratch.
{"level":"info","system":"authenticated application","system":"secureloops","loopID":"3c3dae7a-fa35-47ac-96f4-9ebbf1cee8dc","time":"2021-05-06T13:25:29-04:00","message":"loop stopped"}
{"level":"trace","system":"authenticated application","service":"clipboard","method":"listen","session":{"loopId":"3c3dae7a-fa35-47ac-96f4-9ebbf1cee8dc"},"requestID":"83e4f98d","runtime":11419.554645,"time":"2021-05-06T13:25:29-04:00","message":"end message loop => internal"}
{"level":"trace","system":"authenticated application","service":"filesystem","method":"listenFile","session":{"loopId":"3c3dae7a-fa35-47ac-96f4-9ebbf1cee8dc"},"requestID":"36d9ff45","time":"2021-05-06T13:25:29-04:00","message":"start message loop => internal"}
{"level":"trace","system":"analytics","event":{"documentEncoding":"","documentHostname":"","documentLocationUrl":"","documentPath":"","screenColorDepth":"","screenResolution":"","userAgent":"","userLanguage":"","viewportSize":"","action":"UI Event Handlers","category":"Errors","label":"failed to restart secure loop: failed to start loop: failed to create session: token already exists"},"time":"2021-05-06T13:25:41-04:00","message":"Sending event"}
{"level":"trace","system":"analytics","routineGroup":"internal/analytics.manager.Run","routine":"track event.27","status":"starting","time":"2021-05-06T13:25:41-04:00","message":"routine transition"}
{"level":"trace","system":"analytics","routineGroup":"internal/analytics.manager.Run","routine":"track event.27","status":"started","time":"2021-05-06T13:25:41-04:00","message":"routine transition"}
{"level":"trace","system":"analytics","routineGroup":"internal/analytics.manager.Run","routine":"track event.27","status":"stopping","time":"2021-05-06T13:25:42-04:00","message":"routine transition"}
{"level":"trace","system":"analytics","routineGroup":"internal/analytics.manager.Run","routine":"track event.27","status":"stopped","time":"2021-05-06T13:25:42-04:00","message":"routine transition"}
{"level":"debug","system":"authentication","addressSidekickAPI":"https://sidekick.oliveai.com/api","time":"2021-05-06T13:25:54-04:00","message":"Performing user HTTP request"}
{"level":"debug","system":"authentication","StatusCode":200,"time":"2021-05-06T13:25:54-04:00","message":"Auth response"}
{"level":"debug","system":"authenticated application","approved":true,"loop ID":"5fcaa196f7b08f0714b478f6","loop parent status":"approved","loop status":"approved","loop version":"42","user domain":"oliveai.com","time":"2021-05-06T13:25:54-04:00","message":"loop library status"}
{"level":"trace","system":"analytics","event":{"documentEncoding":"","documentHostname":"","documentLocationUrl":"","documentPath":"","screenColorDepth":"","screenResolution":"","userAgent":"","userLanguage":"","viewportSize":"","action":"UI Event Handlers","category":"Errors","label":"failed to restart secure loop: failed to start loop: failed to create session: token already exists"},"time":"2021-05-06T13:25:59-04:00","message":"Sending event"}
It would be helpful to have a place on disk that we can always rely on to store files. Our use-case is indexing files that the current human user has on their desktop. We would want to persist that index somewhere, but in the world of the secure loops, we don't know where to store our files that the user shouldn't need to be aware of.
Whenever we change file system's pathGlobs we have to reinstall the loop for the changes to work. Can we make permissions update on restart?
If I use the following code, the text isn't in bold.
await whisper.create({
label: 'test',
onClose: () => {},
components: [
{
type: whisper.WhisperComponentType.Markdown,
body: '**Bold**',
}
],
});
However, it looks to be working for the body of a Message
.
await whisper.create({
label: 'test',
onClose: () => {},
components: [
{
type: whisper.WhisperComponentType.Message,
body: '**Bold**',
}
],
});
const testEncodedString = await network.encode('Hi');
await filesystem.writeFile(
'/Users/michaellatman/Desktop/test.txt',
testEncodedString,
WriteOperation.overwrite,
54
);
Results in this log:
"level":"info","system":"authenticated application","system":"secureloops","loopID":"8d2350cc-c138-423a-830a-28b7b1924ddc","console":"log","time":"2021-05-06T13:00:00-04:00","message":"TypeError: could not convert function call parameter 72,105 to []uint8"}
We would like to open a file with the default viewing applications for search style loops. For instance, if we search and index an excel file, it would be cool to allow the user to open the source file in Microsoft Excel.
It would be cool to open a file in the application and open a finder/windows explorer/file browser.
system.openFile(path)
system.openFileBrowser(path)
Using email whisper component will make OliveHelps crash.
await whisperService.create({
label: 'Email',
components: [
{
type: whisperService.WhisperComponentType.Markdown,
body: 'This is a "email" component without box. ',
},
{
type: whisperService.WhisperComponentType.Email,
value: '[email protected]',
tooltip: 'a tooltip',
onChange: (newValue) => console.info(`new email: ${newValue}`),
},
],
onClose: () => {},
});
OliveHelps will crash after running the code above in a loop.
LDK version: 3.0.0-beta.1
OliveHelps version: 0.16.2
If href
is provided the link does not click through.
If onClick
is omitted an error is thrown.
Error
{"level":"error","system":"authenticated application","system":"secureloops","loopID":"73893405-b802-4932-958c-7e1cd95faf08","console":"error","time":"2021-05-07T16:33:21-04:00","message":"Unhandled promise rejection GoError: invalid whisper object: error building components: error asserting callback: <nil>"}
Example of code
clipboard.listen(true, (incomingText: string) => {
whisper.create({
label: 'Clipboard Text Whispers',
onClose: () => {
console.log("Closed Clipboard Text Whisper");
},
components: [
{
href: 'https://oliveai.com',
// onClick: () => {
// console.log('Clicked link')
// },
text: 'External link',
style: Urgency.None,
type: whisper.WhisperComponentType.Link,
}
]
});
});
When using the numberInput
whisper component, it looks like the step
and tooltip
don't have effect. E.g.
await whisperService.create({
label: 'Number input',
components: [
{
type: whisperService.WhisperComponentType.Markdown,
body: 'This is a "number input" component without box.',
},
{
type: whisperService.WhisperComponentType.Number,
label: 'range [0, 10], step 2',
value: 0,
min: 0,
max: 10,
step: 2,
tooltip: 'a tooltip',
onChange: (newValue) => console.log(`New number: ${newValue}`),
},
],
onClose: () => {},
});
LDK version: 3.0.0-beta.1
OliveHelps version: 0.16.2
In CommonMark syntax, it is allowed to have HTML elements. And following the discussion here, we can use <div>
to escape the text which we don't want to be rendered as markdown.
However, it looks like the Markdown
component doesn't support HTML elements. If I use the code below,
const {whisper} = require('@oliveai/ldk');
await whisper.create({
label: 'test',
components: [
{
type: whisper.WhisperComponentType.Markdown,
body: `<div>\n- 123<br>\n- 456<br>\n</div>`,
},
],
onClose: () => {},
});
I see
And if I use the same text in https://spec.commonmark.org/dingus/ , I see
Is the Markdown implementation of LDK based on an older version of the spec?
Missing LDK key entirely doesn't throw a helpful error:
TypeError: Cannot convert undefined or null to object
Fix:
#186
I want to access the JWT token from user aptitude.
It looks like the way to grant permission for user aptitude is not yet documented. I tried to use:
{
"ldk": {
"permissions": {
"user": {},
}
}
}
But I see the error when accessing user.jwt()
2021-06-03T09:42:54+08:00 WRN Unable to grant unrequested permission aptitude=user loopDir="C:\\Users\\foobar\\AppData\\Roaming\\Olive Helps\\secureloops\\d37a62d0-248a-48f3-895e-a66fc9dd27c8_49239f3e-955c-4103-a355-fd39c51ef10b_1621307894_local" system=secureloops
2021-06-03T09:42:54+08:00 ERR Received error calling service access to aptitude was not requested console=error system=secureloops
2021-06-03T09:42:54+08:00 ERR error occurred: GoError: access to aptitude was not requested console=error system=secureloops
2021-06-03T09:42:54+08:00 INF loop started loopID=d37a62d0-248a-48f3-895e-a66fc9dd27c8 system=secureloops
With LDK3, I can listen for hotkey with
keyboard.listenHotkey({
key: 'i',
control: true,
}, (pressed) => {});
But how do I unlisten for the hotkey?
For Mac, after a user clicks update to do auto-update for OliveHelps, I can only force terminate OH and manually install the latest version from the website. Here're the steps to reproduce:
On Windows, there's a similar problem.
After the auto-update, I have to kill processes olivehelps.exe
and .olivehelps.exe.old
, and run OH again. Otherwise, OH sidebar won't show up even if I try to launch OH.
OS: Windows 10 x64 build 19041.985
OliveHelps version: 0.16.2 -> 0.17.3
If I decode the JWT token obtained from the User
aptitude, I can get something like:
{
"header": {
"alg": "RS256",
"typ": "JWT"
},
"payload": {
"azp": "aaaaaaaa-cccc-dddd-eeee-ffffffffffffff.loop.oliveai.com",
"exp": 1622714197,
"iss": "https://sidekick.oliveai.com/api/user/createJWT",
"sub": "[email protected]"
}
}
The sub
field is the user's email instead of a unique user ID. We want to obtain a unique user ID that won't change even if the user changes his email.
And the JWT token cannot be validated using the public keys available at https://login.oliveai.com/.well-known/jwks.json
p.s. This is the same issue I reported in Slack. I just create this one to help to track.
At the bottom left corner of a whisper, there's an Olive logo.
Is it possible to change the icon?
Another question is that whether it is possible to change the foreground color of the text? (for components like message and markdown). It looks like the markdown component doesn't allow HTML syntax like
<span style="color:blue">some *blue* text</span>.
For the CollapseBox
whisper component, is it possible to add a callback so that when the box is collapsed or expanded, the callback is invoked, and we get a boolean indicating whether the box becomes opened or not?
If I send a form whisper without inputs
like
this.services_.whisper.formWhisper({
submitButton: 'Login',
label: 'blabla',
markdown: 'blabla',
}, (err, resp) => {
if (err) {
this.logger_.error('faild to listen for form update', 'err', err.stack);
return;
}
});
The loop will abort with log
{"level":"error","system":"authenticated application","system":"sidenote","AppID":"d5a4bb05-e1c5-4540-a469-5326ae834d40","component":"loop","time":"2021-04-13T15:37:58+08:00","message":"[ERROR] (node:20916) UnhandledPromiseRejectionWarning: TypeError: Cannot convert undefined or null to object"}
{"level":"debug","system":"authenticated application","system":"sidenote","AppID":"d5a4bb05-e1c5-4540-a469-5326ae834d40","component":"loop","time":"2021-04-13T15:37:58+08:00","message":" at Function.keys (<anonymous>)"}
{"level":"debug","system":"authenticated application","system":"sidenote","AppID":"d5a4bb05-e1c5-4540-a469-5326ae834d40","component":"loop","time":"2021-04-13T15:37:58+08:00","message":" at Object.generateWhisperForm (C:\\snapshot\\coding-assistant-olive-poc\\node_modules\\@oliveai\\ldk\\dist\\hostClients\\whisperMessageBuilder.js:334:12)"}
{"level":"debug","system":"authenticated application","system":"sidenote","AppID":"d5a4bb05-e1c5-4540-a469-5326ae834d40","component":"loop","time":"2021-04-13T15:37:58+08:00","message":" at WhisperClient.formWhisper (C:\\snapshot\\coding-assistant-olive-poc\\node_modules\\@oliveai\\ldk\\dist\\hostClients\\whisperClient.js:43:49)"}
{"level":"debug","system":"authenticated application","system":"sidenote","AppID":"d5a4bb05-e1c5-4540-a469-5326ae834d40","component":"loop","time":"2021-04-13T15:37:58+08:00","message":" at CodingAssistantLoop.start (C:\\snapshot\\coding-assistant-olive-poc\\src\\coding-assistant-loop.js)"}
{"level":"debug","system":"authenticated application","system":"sidenote","AppID":"d5a4bb05-e1c5-4540-a469-5326ae834d40","component":"loop","time":"2021-04-13T15:37:58+08:00","message":" at LoopServer.<anonymous> (C:\\snapshot\\coding-assistant-olive-poc\\node_modules\\@oliveai\\ldk\\dist\\loopServer.js:55:29)"}
{"level":"debug","system":"authenticated application","system":"sidenote","AppID":"d5a4bb05-e1c5-4540-a469-5326ae834d40","component":"loop","time":"2021-04-13T15:37:58+08:00","message":" at Generator.next (<anonymous>)"}
{"level":"debug","system":"authenticated application","system":"sidenote","AppID":"d5a4bb05-e1c5-4540-a469-5326ae834d40","component":"loop","time":"2021-04-13T15:37:58+08:00","message":" at fulfilled (C:\\snapshot\\coding-assistant-olive-poc\\node_modules\\@oliveai\\ldk\\dist\\loopServer.js:5:58)"}
{"level":"debug","system":"authenticated application","system":"sidenote","AppID":"d5a4bb05-e1c5-4540-a469-5326ae834d40","component":"loop","time":"2021-04-13T15:37:58+08:00","message":" at processTicksAndRejections (internal/process/task_queues.js:97:5)"}
{"level":"debug","system":"authenticated application","system":"sidenote","AppID":"d5a4bb05-e1c5-4540-a469-5326ae834d40","component":"loop","time":"2021-04-13T15:37:58+08:00","message":"(Use `plugin --trace-warnings ...` to show where the warning was created)"}
{"level":"error","system":"authenticated application","system":"sidenote","AppID":"d5a4bb05-e1c5-4540-a469-5326ae834d40","component":"loop","time":"2021-04-13T15:37:58+08:00","message":"[ERROR] (node:20916) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)"}
{"level":"error","system":"authenticated application","system":"sidenote","AppID":"d5a4bb05-e1c5-4540-a469-5326ae834d40","component":"loop","time":"2021-04-13T15:37:58+08:00","message":"[ERROR] (node:20916) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code."}
Maybe it should either allow the inputs
to be absent or call the callback with an error?
Hi, if I upgrade to LDK 3.0.1, it looks like the loop will fail with the error below:
2021-06-22T18:30:57+08:00 TRC Sending event (with base values added) event={"action":"UI Event Handlers","category":"Errors","documentEncoding":"","documentHostname":"","documentLocationUrl":"","documentPath":"","label":"failed to restart secure loop: failed to start loop: failed to run loop: failed to compile loop: SyntaxError: loop: Line 5:2549 Unexpected token { (and 1231 more errors)","screenColorDepth":"","screenResolution":"","userAgent":"","userLanguage":"","viewportSize":""} system=analytics
2021-06-22T18:30:57+08:00 ERR message received with name newError but no handler is registered for that name system=UI
2021-06-22T18:30:57+08:00 ERR map[] system=UI
The error seems to be unrelated to the code of the loop.
If I use the sample code here directly, it works. But if I change this line to "@oliveai/ldk": "^3.0.1"
, it will also have the same problem.
When using Telephone
whisper component, it looks to have some problems. With the code below
await whisper.create({
label: 'Telephone',
components: [
{
type: whisperService.WhisperComponentType.Markdown,
body: 'This is a "telephone" component without box.',
},
{
type: whisperService.WhisperComponentType.Telephone,
label: 'label',
onChange: (value) => console.log(`Telephone is changed: ${value}`),
tooltip: 'tooltip',
value: '09123456789',
},
],
onClose: () => {},
});
, I see
And there's no tooltip.
LDK version: 3.0.0-beta.1
OliveHelps version: 0.16.2
If you have the Publish New Loop tab open and fill everything out, but then click to another tab (e.g. My Authored Loops) and back to the publish tab, all the entered data is erased. That can be really frustrating if you have everything filled out - especially because the long description text editor is a bit clunky (separate issue, but if you paste in your long description a lot of formatting is lost - bullets don't paste over, so you have to redo those - when you add a bullet to a line it only thinks the first word in a line is the bullet and everything else moves down 2 lines....so takes some time to clean up).
It would be nice to keep the form data saved upon entry and only clear it if a user presses clear/cancel, etc.
Builds can take up to more than 7 seconds with the current LDK webpack config.
I edited the config to turn off minimization, enable able caching, excluding node_modules from babel, and like clockwork, my builds were reduced to less than 2.5 seconds with webpack --watch builds happening in under 900 milliseconds.
Here is the current time breakdown with the current config for a cold build:
SMP ⏱
General output time took 7.43 secs
SMP ⏱ Plugins
TerserPlugin took 1.89 secs
BannerPlugin took 0.001 secs
DefinePlugin took 0.001 secs
SMP ⏱ Loaders
babel-loader took 5.11 secs
module count = 249
ts-loader took 2.67 secs
module count = 9
handlebars-loader took 1.33 secs
module count = 2
After edits:
SMP ⏱
General output time took 2.54 secs
SMP ⏱ Plugins
BannerPlugin took 0.002 secs
DefinePlugin took 0 secs
SMP ⏱ Loaders
ts-loader took 1.6 secs
module count = 9
modules with no loaders took 1.057 secs
module count = 231
babel-loader took 0.381 secs
module count = 2
handlebars-loader took 0.251 secs
module count = 2
We are currently creating symbolic links between our webpack build folder and the secure loops directory. It would be awesome if the helps application would auto restart the loop every time webpack --watch
rebuilds the loop.
It looks like when sending an HTTP request, the default timeout is 5 sec.
Is it possible to specify the timeout?
We want to show a list of items in a whisper. To prevent making the whisper too long, we want to show only the first 5 items by default, and the user can click "more" to expand the remaining 5 items.
We tried to implement it by putting the remaining 5 items in a CollapseBox
. And the view looks like:
(Each item is implemented by one CheckBox
, two Message
, and a Divider
.)
The line-height looks too large.
We tried to put the first 5 items in a Box
, and it looks better now.
However, when we expand the remaining 5 items, you can see that the line height for the first 5 items and the remaining 5 are different:
Any suggestion to make it look better?
Using list pair whisper component will make OliveHelps crash.
await whisperService.create({
label: 'List pair',
components: [
{
type: whisperService.WhisperComponentType.Markdown,
body: 'This is a "list pair" component without box. ',
},
{
type: whisperService.WhisperComponentType.ListPair,
lable: 'label',
value: 'style none + copable false',
style: whisperService.Urgency.None,
copyable: false,
},
],
onClose: () => {},
});
OliveHelps will crash after running the code above in a loop.
LDK version: 3.0.0-beta.1
OliveHelps version: 0.16.2
It looks like we aren't able to update the whisper color strip for an existing local loop.
Steps to reproduce:
Update local loop
buttonAfter we create a whisper with whisper.create
, we can obtain a Whisper
object.
From the typescript interface definition, it should have a close
function and a components
property. However, these are all undefined.
const newWhisper = await whisper.create({
label: 'helloworld',
components: [
{
type: whisper.WhisperComponentType.Markdown,
body: 'Welcome!',
},
],
onClose: () => {},
});
console.info(`whisper.components: ${newWhisper.components}`);
console.info(`whisper.close: ${newWhisper.close}`);
If it is the intended behavior, how do we programmatically close a whisper?
Goja
const excelFile = await filesystem.readFile(excelFileLocation);
const book = new Excel.Workbook();
console.log('Load Start!');
const startTime = new Date();
await book.xlsx.load(excelFile);
const endTime = new Date();
console.log('Load End');
let timeDiff = endTime.getTime() - startTime.getTime(); //in ms
// strip the ms
timeDiff /= 1000;
console.log(timeDiff + ' seconds');
Result:
50.564 seconds
Versus (node):
const Excel = require('exceljs');
const fs = require('fs');
const book = new Excel.Workbook();
const fileBytes = fs.readFileSync("/Users/michaellatman/Downloads/Test.xlsx")
console.log('Load Start!');
const startTime = new Date();
await book.xlsx.load(fileBytes);
const endTime = new Date();
console.log('Load End');
var timeDiff = endTime.getTime() - startTime.getTime(); //in ms
// strip the ms
timeDiff /= 1000;
console.log(timeDiff + " seconds");
Result: 0.755 seconds
50 seconds is a huge barrier to us in our development process, not to mention that users will not be able to use excel based whispers for around a minute.
When using the Number
whisper component, it looks like the min
doesn't have an effect. E.g.
await whisperService.create({
label: 'Number input',
components: [
{
type: whisperService.WhisperComponentType.Markdown,
body: 'This is a "number input" component without box.',
},
{
type: whisperService.WhisperComponentType.Number,
label: 'range [0, 10], step 2',
value: 0,
min: 0,
max: 10,
step: 2,
tooltip: 'a tooltip',
onChange: (newValue) => console.log(`New number: ${newValue}`),
},
],
onClose: () => {},
});
The max
has an effect though.
LDK version: 3.0.0-beta.3
OliveHelps version: 0.17.3
Currently the Olive Helps LDK does not provide functionality for configuring a 'Value' attribute, unlike other Form Input types.
["Text"] = new Text { Label = "TextInput", Tooltip = "Text tooltip.", Value ="TEXT VALUE", Order = 1 },
["State"] = new Select
{
Label = "SelectInput",
Tooltip = "Select tooltip.",
Order = 2,
Options = new[] { "Option 1", "Option 2" }
},
In this example, I am able to set the value of a Text Form Input to "TEXT VALUE" upon initializing a WhisperForm(), but unable to set a value attribute of a Select Form Input.
I am new to LDK, i need example codes to start with.
No tooltip appears for the email component upon hover.
{
label: 'Email label',
value: 'Test default value',
tooltip: 'This is a tip',
onChange: (value) => {
console.log('Email value changed', value)
},
type: whisper.WhisperComponentType.Email
}
The new LDK could not find @babel/plugin-transform-runtime
with yarn. Installing it as a dev dependency increases the unminified build size 500kb+. Webpack's build time went from < 1s to > 5s.
webpack 5.37.0 compiled in 5336 ms
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (500 KiB).
This can impact web performance.
Assets:
loop.js (717 KiB)
I ran the code below.
const myWhisper = await whisper.create({
label: 'hello',
components: [
{
type: whisper.WhisperComponentType.Checkbox,
value: false,
label: 'hello',
onChange: () => {},
},
],
onClose: () => {},
});
await delay(5000);
myWhisper.update({
label: 'hello2',
components: [
{
type: whisper.WhisperComponentType.Checkbox,
value: false,
label: 'hello2',
onChange: () => {},
},
],
}, (err) => {
console.error(err);
});
And I check the checkbox immediately.
Wait for 5 seconds. The text is changed from hello
to hello2
, but the new checkbox is also checked.
The expected behavior is that the new checkbox should be unchecked.
The same behavior also happens for the open
status of a CollapseBox
.
Another related use case we have is that we have a whisper with 10 checkboxes. And we put the last 5 checkboxes in a CollapseBox
. When a user checks one checkbox, the whisper is updated to add a Message
. If the user expanded the CollapseBox
before checking the checkbox, we want the CollapseBox
to remain open after the update. The current behavior is: For the 1st update, the open
flag of the CollapseBox
will be respected. However, if the CollapseBox
was open before the update, and we set open
to true for the update, there will be an animation to expand the CollapseBox
. The user experience isn't very good. For the 2nd and later update, the open
flag of the CollapseBox
won't be respected. The CollapseBox
always stays in the same state as before the update.
p.s. This is the same issue I reported in Slack. I just create this one to help to track.
Example Loop path : loop-development-kit/ldk/csharp/OliveHelpsLDK/Example/Program.cs
When attempting to run the csharp Example Loop I got this error when calling the EmitListWhisper() function provided by OliveHelps
System.ArgumentNullException: Value cannot be null. (Parameter 'value')\r\n at Google.Protobuf.ProtoPreconditions.CheckNotNull[T](T value, String name)\r\n at Proto.WhisperListRequest.set_Markdown(String value)\r\n at OliveHelpsLDK.Whispers.WhisperFormBuilder.BuildRequest(WhisperList list, Session session)\r\n at OliveHelpsLDK.Whispers.WhisperClient.ListAsync(WhisperList message, CancellationToken cancellationToken)\r\n at AdvocatiaLoop.Loop.EmitListWhisper() in D:\\Dev\\Saiph\\AdvocatiaLoop\\AdvocatiaLoop\\Program.cs:line 512\r\n at AdvocatiaLoop.Loop.<SearchbarStream>b__8_0() in D:\\Dev\\Saiph\\AdvocatiaLoop\\AdvocatiaLoop\\Program.cs:line 98
This is caused by the WhisperList initialization not containing a "Markdown" attribute:
private void EmitListWhisper()
{
var ccs = new CancellationTokenSource();
ccs.CancelAfter(5000);
var whisperList = new WhisperList
{
Config = new WhisperConfig {
Label = "C# Whisper"
},
Elements = new Dictionary<string, ListBase>
{
["Nickname"] = new ListPair {Label="Nickname", Order=1, Value="Old Greg"},
["Link"] = new ListLink {Align=ListAlign.Center, Href="https://isitchristmas.com/", Order=2, Text="Is it Christmas?" }
},
};
_services.Whisper.ListAsync(whisperList, ccs.Token);
Logger.Info($"Sent List Whisper");
}
It can be fixed by setting the Markdown attribute upon initialization of new WhisperList to a default value.
private void EmitListWhisper()
{
var ccs = new CancellationTokenSource();
ccs.CancelAfter(5000);
var whisperList = new WhisperList
{
Markdown = "DEFAULT VALUE HERE",
Config = new WhisperConfig {
Label = "C# Whisper"
},
Elements = new Dictionary<string, ListBase>
{
["Nickname"] = new ListPair {Label="Nickname", Order=1, Value="Old Greg"},
["Link"] = new ListLink {Align=ListAlign.Center, Href="https://isitchristmas.com/", Order=2, Text="Is it Christmas?" }
},
};
_services.Whisper.ListAsync(whisperList, ccs.Token);
Logger.Info($"Sent List Whisper");
}
Let's say I have several checkboxes in a whisper for different options, and the first checkbox's label is "select all". The behavior I want is when the user checks the first checkbox, all the other checkboxes will become checked.
To achieve this behavior, we need to programmatically update a created whisper.
As discussed here, it is currently not supported.
I hope the support can be added to enhance the user experience.
When trying out the keyboard.listenHotkey()
in the javascript LDK, some behaviors look a little strange. I'm not sure if they are bugs or intended behaviors.
meta
key maps to options
key on the keyboard, and alt
key maps to the cmd
key on the keyboard. Shouldn't they be reversed? I'm not that familiar with Mac. Maybe I'm wrong.pressed
being true
for several times. But it is never called with false
.ldk.filesystem.makeDir(`${__dirname}hello-world`, 54);
Error message
Unhandled promise rejection GoError: mkdir helloworld: read-only file system
listenActiveWindow
should only send a notification when the focused window changes, not when your mouse moves within the active window.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.