Git Product home page Git Product logo

loop-development-kit's Issues

OliveHelps sidebar doesn't show up when sharing screen with Teams

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.

  1. Use Microsoft teams to create a meeting
  2. Share the whole screen of the main screen.
  3. Move the mouse to the right side of the screen, and the OliveHelps sidebar appears.
  4. Close the sidebar, and move the mouse to the right side again. It no longer appears.

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

Allow Loop to report Olive Helps operating system

Some Loop Authors have requested to allow a Loop to report which operating system it is currently executing under.

Some reasons include:

  1. To provide operating system specific filesystem operations
  2. To provide operating system specific keyboard listening logic

Notes

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).

Full screen OCR Support

We'd like to OCR the entire display such that we can extract patient numbers and other info from applications.

Updated whispers with TextInputs stop sending callbacks

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:

  • Edit text field 1
  • See text field 2
  • Edit text field 2
  • See text field 3

Acutal:

  • Edit text field 1
  • See text field 2
  • Edit text field 2
  • See text field 2

Allow network aptitude to make http calls for local devleopment

"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.

`Filesystem.listenFile `causes loop to crash and be causes loop to be unable to be uninstalled

    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"}

Provide a storage folder for loops

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.

Bold text style isn't supported in Markdown whisper component

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**',
        }
      ],
    });

Writing to file fails due to encoding

 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"}

Filesystem or system Aptitude: Open file

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

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

Link component `href` and `onClick` issues

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,
                    }
                ]
            });
        });

"step" and "tooltip" have no effect for "numberInput" whisper component

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

Markdown doesn't support HTML elements

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
image
And if I use the same text in https://spec.commonmark.org/dingus/ , I see
image
Is the Markdown implementation of LDK based on an older version of the spec?

Unable to grant permission for user aptitude

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

Stop listening for hotkey

With LDK3, I can listen for hotkey with

keyboard.listenHotkey({
  key: 'i',
  control: true,
}, (pressed) => {});

But how do I unlisten for the hotkey?

OliveHelps auto-update doesn't restart normally

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:

  1. installed older version
    MD5: 67deaef2009ea3226522a4419b999107
    SHA: e2e14dec6313a420583270989bad4582429816eb
  2. launch OH and sign in, OH ask me to update
  3. click on "update", the OH UI disappear
  4. only OH process and icon on dock were visible, but no any UI even I click on the icon
  5. after about 15 min, the situation remained
  6. manually download OH from web
    MD5: 35f1e5576b2a567b4ebc2856a601b396
    SHA: 4250f81ef658de18b1062f7973d678fd3fb0b15a
  7. manually install OH, and force terminate previous OH process
  8. restart OH, it was back to normal.

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

JWT token of User aptitude doesn't encode a unique user ID

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.

Text styling and logo image

At the bottom left corner of a whisper, there's an Olive logo.
image
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>.

[suggestion] Add callback for CollapseBox

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?

Sending form whisper without 'input' will fail

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?

LDK 3.0.1 failed with SyntaxError: loop: Line 5:2549 Unexpected token

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.

"telephone" whisper component behave incorrect for tooltip and value

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
Untitled
And there's no tooltip.

LDK version: 3.0.0-beta.1
OliveHelps version: 0.16.2

Publish Loop: Data Erased

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.

Add development mode config

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

Different line-height for Box and CollapseBox

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:
image
(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.
image
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:
image
Any suggestion to make it look better?

Using list pair whisper component will make OliveHelps crash

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

Cannot update whisper color strip for local loop

It looks like we aren't able to update the whisper color strip for an existing local loop.
Steps to reproduce:

  • Install a local loop
  • Update the loop in the Loop Library to change the color of the whisper strip.
  • Click Update local loop button
  • Restart the loop
  • The color of the whisper strip is not changed.

`Whisper` object from `whisper.create` doesn't have `components` and `close`

After 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 loads excel files (using exceljs) 6633.33% slower than node.

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.

Lower bound of `Number` whisper component has no effect

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

Form Input Select does not contain 'Value' configurable attribute.

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.

Email component tooltip not working

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
}

`@babel/plugin-transform-runtime` drastically increases the build size

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)

Updated whisper doesn't use the settings of new components

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.

Bug found in EmitListWhisper()

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");
}

Add support to update an existing 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.

Behavior of keyboard hotkey

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.

  • On Mac, it looks like the 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.
  • When I press Ctrl + I, all the listeners for Ctrl + I, Ctrl + shift + I, Ctrl + meta + I, and Ctrl + Alt + I will be called. Is that the intended behavior? If so, how do I distinguish the user is pressing Ctrl + I instead of Ctrl + Alt + I?
  • If I press Ctrl + I for a few seconds and release, the listener will be called with argument pressed being true for several times. But it is never called with false.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.