diff --git a/applications/debug/unit_tests/resources/unit_tests/js/basic.js b/applications/debug/unit_tests/resources/unit_tests/js/basic.js index 0ef904ecb..887b47fb3 100644 --- a/applications/debug/unit_tests/resources/unit_tests/js/basic.js +++ b/applications/debug/unit_tests/resources/unit_tests/js/basic.js @@ -4,12 +4,12 @@ let flipper = require("flipper"); tests.assert_eq(1337, 1337); tests.assert_eq("hello", "hello"); -tests.assert_eq("compatible", sdkCompatibilityStatus(0, 1)); +tests.assert_eq("compatible", sdkCompatibilityStatus(1, 0)); tests.assert_eq("firmwareTooOld", sdkCompatibilityStatus(100500, 0)); tests.assert_eq("firmwareTooNew", sdkCompatibilityStatus(-100500, 0)); tests.assert_eq(true, doesSdkSupport(["baseline"])); tests.assert_eq(false, doesSdkSupport(["abobus", "other-nonexistent-feature"])); tests.assert_eq("flipperdevices", flipper.firmwareVendor); -tests.assert_eq(0, flipper.jsSdkVersion[0]); -tests.assert_eq(3, flipper.jsSdkVersion[1]); +tests.assert_eq(1, flipper.jsSdkVersion[0]); +tests.assert_eq(0, flipper.jsSdkVersion[1]); diff --git a/applications/system/js_app/examples/apps/Scripts/gui.js b/applications/system/js_app/examples/apps/Scripts/gui.js index bc63a7ef6..bc569cee0 100644 --- a/applications/system/js_app/examples/apps/Scripts/gui.js +++ b/applications/system/js_app/examples/apps/Scripts/gui.js @@ -12,6 +12,7 @@ let filePicker = require("gui/file_picker"); let widget = require("gui/widget"); let icon = require("gui/icon"); let flipper = require("flipper"); +let math = require("math"); // declare clock widget children let cuteDolphinWithWatch = icon.getBuiltin("DolphinWait_59x54"); @@ -131,14 +132,14 @@ eventLoop.subscribe(gui.viewDispatcher.navigation, function (_sub, _, gui, views }, gui, views, eventLoop); // go to the demo chooser screen when the right key is pressed on the widget screen -eventLoop.subscribe(views.stopwatchWidget.button, function (_sub, buttonId, gui, views) { - if (buttonId === "right") +eventLoop.subscribe(views.stopwatchWidget.button, function (_sub, buttonEvent, gui, views) { + if (buttonEvent.key === "right" && buttonEvent.type === "short") gui.viewDispatcher.switchTo(views.demos); }, gui, views); // count time eventLoop.subscribe(eventLoop.timer("periodic", 500), function (_sub, _item, views, stopwatchWidgetElements, halfSeconds) { - let text = (halfSeconds / 2 / 60).toString(); + let text = math.floor(halfSeconds / 2 / 60).toString(); if (halfSeconds < 10 * 60 * 2) text = "0" + text; @@ -146,7 +147,7 @@ eventLoop.subscribe(eventLoop.timer("periodic", 500), function (_sub, _item, vie if (((halfSeconds / 2) % 60) < 10) text += "0"; - text += ((halfSeconds / 2) % 60).toString(); + text += (math.floor(halfSeconds / 2) % 60).toString(); stopwatchWidgetElements[0].text = text; views.stopwatchWidget.setChildren(stopwatchWidgetElements); diff --git a/applications/system/js_app/js_modules.c b/applications/system/js_app/js_modules.c index f9c08058f..095880f17 100644 --- a/applications/system/js_app/js_modules.c +++ b/applications/system/js_app/js_modules.c @@ -272,10 +272,6 @@ void js_check_sdk_compatibility(struct mjs* mjs) { static const char* extra_features[] = { "baseline", // dummy "feature" - "gpio-pwm", - "gui-widget", - "serial-framing", - "gui-widget-extras", }; /** diff --git a/applications/system/js_app/js_modules.h b/applications/system/js_app/js_modules.h index c6f72bbe2..a1e6d209e 100644 --- a/applications/system/js_app/js_modules.h +++ b/applications/system/js_app/js_modules.h @@ -15,8 +15,8 @@ extern "C" { #define PLUGIN_API_VERSION 1 #define JS_SDK_VENDOR "flipperdevices" -#define JS_SDK_MAJOR 0 -#define JS_SDK_MINOR 3 +#define JS_SDK_MAJOR 1 +#define JS_SDK_MINOR 0 /** * @brief Returns the foreign pointer in `obj["_"]` diff --git a/applications/system/js_app/modules/js_gui/widget.c b/applications/system/js_app/modules/js_gui/widget.c index bb2898030..47149b4ac 100644 --- a/applications/system/js_app/modules/js_gui/widget.c +++ b/applications/system/js_app/modules/js_gui/widget.c @@ -10,6 +10,11 @@ typedef struct { #define QUEUE_LEN 2 +typedef struct { + GuiButtonType key; + InputType type; +} JsWidgetButtonEvent; + /** * @brief Parses position (X and Y) from an element declaration object */ @@ -101,8 +106,11 @@ static bool element_get_text(struct mjs* mjs, mjs_val_t element, mjs_val_t* text * @brief Widget button element callback */ static void js_widget_button_callback(GuiButtonType result, InputType type, JsWidgetCtx* context) { - UNUSED(type); - furi_check(furi_message_queue_put(context->queue, &result, 0) == FuriStatusOk); + JsWidgetButtonEvent event = { + .key = result, + .type = type, + }; + furi_check(furi_message_queue_put(context->queue, &event, 0) == FuriStatusOk); } #define DESTRUCTURE_OR_RETURN(mjs, child_obj, part, ...) \ @@ -263,25 +271,44 @@ static mjs_val_t js_widget_button_event_transformer( FuriMessageQueue* queue, JsWidgetCtx* context) { UNUSED(context); - GuiButtonType btn_type; - furi_check(furi_message_queue_get(queue, &btn_type, 0) == FuriStatusOk); - const char* btn_name; - if(btn_type == GuiButtonTypeLeft) { - btn_name = "left"; - } else if(btn_type == GuiButtonTypeCenter) { - btn_name = "center"; - } else if(btn_type == GuiButtonTypeRight) { - btn_name = "right"; + JsWidgetButtonEvent event; + furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk); + const char* event_key; + if(event.key == GuiButtonTypeLeft) { + event_key = "left"; + } else if(event.key == GuiButtonTypeCenter) { + event_key = "center"; + } else if(event.key == GuiButtonTypeRight) { + event_key = "right"; } else { furi_crash(); } - return mjs_mk_string(mjs, btn_name, ~0, false); + const char* event_type; + if(event.type == InputTypePress) { + event_type = "press"; + } else if(event.type == InputTypeRelease) { + event_type = "release"; + } else if(event.type == InputTypeShort) { + event_type = "short"; + } else if(event.type == InputTypeLong) { + event_type = "long"; + } else if(event.type == InputTypeRepeat) { + event_type = "repeat"; + } else { + furi_crash(); + } + mjs_val_t obj = mjs_mk_object(mjs); + JS_ASSIGN_MULTI(mjs, obj) { + JS_FIELD("key", mjs_mk_string(mjs, event_key, ~0, true)); + JS_FIELD("type", mjs_mk_string(mjs, event_type, ~0, true)); + } + return obj; } static void* js_widget_custom_make(struct mjs* mjs, Widget* widget, mjs_val_t view_obj) { UNUSED(widget); JsWidgetCtx* context = malloc(sizeof(JsWidgetCtx)); - context->queue = furi_message_queue_alloc(QUEUE_LEN, sizeof(GuiButtonType)); + context->queue = furi_message_queue_alloc(QUEUE_LEN, sizeof(JsWidgetButtonEvent)); context->contract = (JsEventLoopContract){ .magic = JsForeignMagic_JsEventLoopContract, .object_type = JsEventLoopObjectTypeQueue, diff --git a/applications/system/js_app/packages/create-fz-app/package.json b/applications/system/js_app/packages/create-fz-app/package.json index 7778104e2..b2a8f157a 100644 --- a/applications/system/js_app/packages/create-fz-app/package.json +++ b/applications/system/js_app/packages/create-fz-app/package.json @@ -1,6 +1,6 @@ { "name": "@flipperdevices/create-fz-app", - "version": "0.1.1", + "version": "0.1.2", "description": "Template package for JS apps Flipper Zero", "bin": "index.js", "type": "module", diff --git a/applications/system/js_app/packages/create-fz-app/template/package.json b/applications/system/js_app/packages/create-fz-app/template/package.json index 322a8b58b..a67dc71c3 100644 --- a/applications/system/js_app/packages/create-fz-app/template/package.json +++ b/applications/system/js_app/packages/create-fz-app/template/package.json @@ -6,7 +6,7 @@ "start": "npm run build && node node_modules/@flipperdevices/fz-sdk/sdk.js upload" }, "devDependencies": { - "@flipperdevices/fz-sdk": "^0.3", + "@flipperdevices/fz-sdk": "^1.0", "typescript": "^5.6.3" } } \ No newline at end of file diff --git a/applications/system/js_app/packages/fz-sdk/global.d.ts b/applications/system/js_app/packages/fz-sdk/global.d.ts index 4c7f217d0..71e177823 100644 --- a/applications/system/js_app/packages/fz-sdk/global.d.ts +++ b/applications/system/js_app/packages/fz-sdk/global.d.ts @@ -72,7 +72,7 @@ * @brief Checks compatibility between the script and the JS SDK that the * firmware provides * - * @note You're looking at JS SDK v0.3 + * @note You're looking at JS SDK v1.0 * * @param expectedMajor JS SDK major version expected by the script * @param expectedMinor JS SDK minor version expected by the script @@ -92,7 +92,7 @@ declare function sdkCompatibilityStatus(expectedMajor: number, expectedMinor: nu * @brief Checks compatibility between the script and the JS SDK that the * firmware provides in a boolean fashion * - * @note You're looking at JS SDK v0.3 + * @note You're looking at JS SDK v1.0 * * @param expectedMajor JS SDK major version expected by the script * @param expectedMinor JS SDK minor version expected by the script @@ -105,7 +105,7 @@ declare function isSdkCompatible(expectedMajor: number, expectedMinor: number): * @brief Asks the user whether to continue executing the script if the versions * are not compatible. Does nothing if they are. * - * @note You're looking at JS SDK v0.3 + * @note You're looking at JS SDK v1.0 * * @param expectedMajor JS SDK major version expected by the script * @param expectedMinor JS SDK minor version expected by the script diff --git a/applications/system/js_app/packages/fz-sdk/gpio/index.d.ts b/applications/system/js_app/packages/fz-sdk/gpio/index.d.ts index cd5ce2b60..3a3af12f7 100644 --- a/applications/system/js_app/packages/fz-sdk/gpio/index.d.ts +++ b/applications/system/js_app/packages/fz-sdk/gpio/index.d.ts @@ -80,6 +80,7 @@ export interface Pin { * PWM-related methods on this pin will throw an error when called. * @note On Flipper Zero only pins PA4 and PA7 support PWM * @version Added in JS SDK 0.2, extra feature `"gpio-pwm"` + * @version Baseline since JS SDK 1.0 */ isPwmSupported(): boolean; /** @@ -89,18 +90,21 @@ export interface Pin { * @param freq Frequency in Hz * @param duty Duty cycle in % * @version Added in JS SDK 0.2, extra feature `"gpio-pwm"` + * @version Baseline since JS SDK 1.0 */ pwmWrite(freq: number, duty: number): void; /** * Determines whether PWM is running. Throws an error if PWM is not * supported on this pin. * @version Added in JS SDK 0.2, extra feature `"gpio-pwm"` + * @version Baseline since JS SDK 1.0 */ isPwmRunning(): boolean; /** * Stops PWM. Does not restore previous pin configuration. Throws an error * if PWM is not supported on this pin. * @version Added in JS SDK 0.2, extra feature `"gpio-pwm"` + * @version Baseline since JS SDK 1.0 */ pwmStop(): void; } diff --git a/applications/system/js_app/packages/fz-sdk/gui/icon.d.ts b/applications/system/js_app/packages/fz-sdk/gui/icon.d.ts index 8d2b68bce..03fc8c53b 100644 --- a/applications/system/js_app/packages/fz-sdk/gui/icon.d.ts +++ b/applications/system/js_app/packages/fz-sdk/gui/icon.d.ts @@ -7,6 +7,7 @@ export type IconData = symbol & { "__tag__": "icon" }; * Gets a built-in firmware icon for use in GUI * @param icon Name of the icon * @version Added in JS SDK 0.2, extra feature `"gui-widget"` + * @version Baseline since JS SDK 1.0 */ export declare function getBuiltin(icon: BuiltinIcon): IconData; @@ -14,5 +15,6 @@ export declare function getBuiltin(icon: BuiltinIcon): IconData; * Loads a .fxbm icon (XBM Flipper sprite, from flipperzero-game-engine) for use in GUI * @param path Path to the .fxbm file * @version Added in JS SDK 0.3, extra feature `"gui-widget-extras"` + * @version Baseline since JS SDK 1.0 */ export declare function loadFxbm(path: string): IconData; diff --git a/applications/system/js_app/packages/fz-sdk/gui/index.d.ts b/applications/system/js_app/packages/fz-sdk/gui/index.d.ts index 969b6934e..77e5891cd 100644 --- a/applications/system/js_app/packages/fz-sdk/gui/index.d.ts +++ b/applications/system/js_app/packages/fz-sdk/gui/index.d.ts @@ -133,17 +133,20 @@ export declare class View { * Adds a child to the View * @param child Child to add * @version Added in JS SDK 0.2, extra feature `"gui-widget"` + * @version Baseline since JS SDK 1.0 */ addChild(child: C): void; /** * Removes all children from the View * @version Added in JS SDK 0.2, extra feature `"gui-widget"` + * @version Baseline since JS SDK 1.0 */ resetChildren(): void; /** * Removes all previous children from the View and assigns new children * @param children The list of children to assign * @version Added in JS SDK 0.2, extra feature `"gui-widget"` + * @version Baseline since JS SDK 1.0 */ setChildren(children: Child[]): void; } @@ -158,7 +161,9 @@ export declare class ViewFactory, children?: Child[]): V; } diff --git a/applications/system/js_app/packages/fz-sdk/gui/widget.d.ts b/applications/system/js_app/packages/fz-sdk/gui/widget.d.ts index bf4aab22b..3b0bea006 100644 --- a/applications/system/js_app/packages/fz-sdk/gui/widget.d.ts +++ b/applications/system/js_app/packages/fz-sdk/gui/widget.d.ts @@ -23,6 +23,7 @@ * This view has the elements as its children. * * @version Added in JS SDK 0.2, extra feature `"gui-widget"` + * @version Baseline since JS SDK 1.0 * @module */ @@ -42,9 +43,21 @@ type TextBoxElement = { element: "text_box", stripToDots: boolean } & Position & type TextScrollElement = { element: "text_scroll" } & Position & Size & Text; type ButtonElement = { element: "button", button: "left" | "center" | "right" } & Text; type IconElement = { element: "icon", iconData: IconData } & Position; -type RectElement = { element: "rect", radius: number, fill: boolean } & Position & Size; /** @version Amended in JS SDK 0.3, extra feature `"gui-widget-extras"` */ -type CircleElement = { element: "circle", radius: number, fill: boolean } & Position; /** @version Added in JS SDK 0.3, extra feature `"gui-widget-extras"` */ -type LineElement = { element: "line", x1: number, y1: number, x2: number, y2: number }; /** @version Added in JS SDK 0.3, extra feature `"gui-widget-extras"` */ +/** + * @version Amended in JS SDK 0.3, extra feature `"gui-widget-extras"` + * @version Baseline since JS SDK 1.0 + * */ +type RectElement = { element: "rect", radius: number, fill: boolean } & Position & Size; +/** + * @version Added in JS SDK 0.3, extra feature `"gui-widget-extras"` + * @version Baseline since JS SDK 1.0 + * */ +type CircleElement = { element: "circle", radius: number, fill: boolean } & Position; +/** + * @version Added in JS SDK 0.3, extra feature `"gui-widget-extras"` + * @version Baseline since JS SDK 1.0 + * */ +type LineElement = { element: "line", x1: number, y1: number, x2: number, y2: number }; type Element = StringMultilineElement | StringElement @@ -58,12 +71,16 @@ type Element = StringMultilineElement type Props = {}; type Child = Element; +declare class ButtonEvent { + key: "left" | "center" | "right"; + type: "press" | "release" | "short" | "long" | "repeat"; +} declare class Widget extends View { /** * Event source for buttons. Only gets fired if there's a corresponding * button element. */ - button: Contract<"left" | "center" | "right">; + button: Contract; } declare class WidgetFactory extends ViewFactory { } declare const factory: WidgetFactory; diff --git a/applications/system/js_app/packages/fz-sdk/package.json b/applications/system/js_app/packages/fz-sdk/package.json index 3ab108e48..020f3d45c 100644 --- a/applications/system/js_app/packages/fz-sdk/package.json +++ b/applications/system/js_app/packages/fz-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@flipperdevices/fz-sdk", - "version": "0.3.0", + "version": "1.0.0", "description": "Type declarations and documentation for native JS modules available on Flipper Zero", "keywords": [ "flipper", diff --git a/applications/system/js_app/packages/fz-sdk/serial/index.d.ts b/applications/system/js_app/packages/fz-sdk/serial/index.d.ts index 5064c4213..14247f509 100644 --- a/applications/system/js_app/packages/fz-sdk/serial/index.d.ts +++ b/applications/system/js_app/packages/fz-sdk/serial/index.d.ts @@ -29,6 +29,7 @@ export interface Framing { * @param framing See `Framing` type * @version Added in JS SDK 0.1 * @version Added `framing` parameter in JS SDK 0.3, extra feature `"serial-framing"` + * @version Baseline since JS SDK 1.0 */ export declare function setup(port: "lpuart" | "usart", baudRate: number, framing?: Framing): void; diff --git a/documentation/js/js_gui__widget.md b/documentation/js/js_gui__widget.md index 9ea3e4dfa..4c922a67c 100644 --- a/documentation/js/js_gui__widget.md +++ b/documentation/js/js_gui__widget.md @@ -35,3 +35,20 @@ Elements are objects with properties to define them, in the form `{ element: "ty | `rect` | `x` (number), `y` (number)
`w` (number), `h` (number)
`radius` (number), `fill` (boolean) | Draw a rectangle, optionally rounded and filled. | | `circle` | `x` (number), `y` (number)
`radius` (number), `fill` (boolean) | Draw a circle, optionally filled. | | `line` | `x1` (number), `y1` (number)
`x2` (number), `y2` (number) | Draw a line between 2 points. | + +## Structures + +### ButtonEvent + +Button event information structure. + +**Fields** + +- key: The key that was pressed (`"left" | "center" | "right"`) +- type: The type of the event (`"press" | "release" | "short" | "long" | "repeat"`) + +## View events + +| Item | Type | Description | +|----------|--------|-----------------------------------------------------------------------------| +| `button` | `ButtonEvent`| Fires when the user presses on one of the three possible buttons if there's a corresponding button element. Refer to the `ButtonEvent` structure above for possible values. |