🔏 Create Keycloak themes using React

Overview

🔏 Create Keycloak themes using React 🔏

Ultimately this build tool generates a Keycloak theme

NEW in v4

  • Out of the box frontend form validation 🥳
  • Improvements (and breaking changes in import { useKcMessage } from "keycloakify".

Motivations

Keycloak provides theme support for web pages. This allows customizing the look and feel of end-user facing pages so they can be integrated with your applications. It involves, however, a lot of raw JS/CSS/FTL hacking, and bundling the theme is not exactly straightforward.

Beyond that, if you use Keycloak for a specific app you want your login page to be tightly integrated with it. Ideally, you don't want the user to notice when he is being redirected away.

Trying to reproduce the look and feel of a specific app in another stack is not an easy task not to mention the cheer amount of maintenance that it involves.

Without keycloakify, users suffers from a harsh context switch, no fronted form pre-validation

Wouldn't it be great if we could just design the login and register pages as if they were part of our app?
Here is keycloakify for you 🍸

With keycloakify:

TL;DR: Here is a Hello World React project with Keycloakify set up.

If you already have a Keycloak custom theme, it can be easily ported to Keycloakify.


Requirements

Tested with the following Keycloak versions:

This tool will be maintained to stay compatible with Keycloak v11 and up, however, the default pages you will get (before you customize it) will always be the ones of Keycloak v11.

This tool assumes you are bundling your app with Webpack (tested with 4.44.2) . It assumes there is a build/ directory at the root of your react project directory containing a index.html file and a build/static/ directory generated by webpack. For more information see this issue

All this is defaults with create-react-app (tested with 4.0.3)

  • mvn (Maven), rm, mkdir, curl, unzip are assumed to be available.
  • docker must be up and running when running yarn keycloak.

On Windows you'll have to use WSL.

My framework doesn’t seem to be supported, what can I do?

Currently Keycloakify is only compatible with create-react-app apps. It doesn’t mean that you can't use Keycloakify if you are using Next.js, Express or any other framework that involves SSR but your Keycloak theme will need to be a standalone project.
Find specific instructions about how to get started here.

To share your styles between your main app and your login pages you will need to externalize your design system by making it a separate module. Checkout ts_ci, it can help with that.

How to use

Setting up the build tool

yarn add keycloakify @emotion/react tss-react powerhooks

package.json

"scripts": {
    "keycloak": "yarn build && build-keycloak-theme",
}
yarn keycloak # generates keycloak-theme.jar

On the console will be printed all the instructions about how to load the generated theme in Keycloak

Changing just the look of the default Keycloak theme

The first approach is to only customize the style of the default Keycloak login by providing your own class names.

If you have created a new React project specifically to create a Keycloak theme and nothing else then your index should look something like:

src/index.tsx

import { App } from "./<wherever>/App";
import {
  KcApp,
  defaultKcProps,
  getKcContext
} from "keycloakify";
import { css } from "tss-react/@emotion/css";

const { kcContext } = getKcContext();

const myClassName = css({ "color": "red" });

reactDom.render(
        <KcApp
            kcContext={kcContext}
            {...{
                ...defaultKcProps,
                "kcHeaderWrapperClass": myClassName
            }}
        />
    document.getElementById("root")
);

If you share a unique project for your app and the Keycloak theme, your index should look more like this:

src/index.tsx

import { App } from "./<wherever>/App";
import { KcApp, defaultKcProps, getKcContext } from "keycloakify";
import { css } from "tss-react/@emotion/css";

const { kcContext } = getKcContext();

const myClassName = css({ "color": "red" });

reactDom.render(
    // Unless the app is currently being served by Keycloak
    // kcContext is undefined.
    kcContext !== undefined ? (
        <KcApp
            kcContext={kcContext}
            {...{
                ...defaultKcProps,
                "kcHeaderWrapperClass": myClassName,
            }}
        />
    ) : (
        <App />
    ), // Your actual app
    document.getElementById("root"),
);

result:

Example of a customization using only CSS: here (the index.tsx ) and the result you can expect:

Customization using only CSS:

Advanced pages configuration

If you want to go beyond only customizing the CSS you can re-implement some of the pages or even add new ones.

If you want to go this way checkout the demo setup provided here. If you prefer a real life example you can checkout onyxia-web's source. The web app is in production here.

Main takeaways are:

  • You must declare your custom pages in the package.json. example
  • (TS only) You must declare theses page in the type argument of the getter function for the kcContext in order to have the correct typings. example
  • (TS only) If you use Keycloak plugins that defines non standard .ftl values (Like for example this plugin that define authorizedMailDomains in register.ftl) you should declare theses value to get the type. example
  • You should provide sample data for all the non standard value if you want to be able to debug the page outside of keycloak. example

WARNING: If you chose to go this way use:

"dependencies": {
    "keycloakify": "~X.Y.Z"
}

in your package.json instead of ^X.Y.Z. A minor update of Keycloakify might break your app.

Hot reload

Rebuild the theme each time you make a change to see the result is not practical. If you want to test your login screens outside of Keycloak you can mock a given kcContext:

import {
    KcApp,
    defaultKcProps,
    getKcContext
} from "keycloakify";

const { kcContext } = getKcContext({
    "mockPageId": "login.ftl"
});

reactDom.render(
        <KcApp
            kcContext={kcContextMocks.kcLoginContext}
            {...defaultKcProps}
        />
    document.getElementById("root")
);

Then yarn start, you will see your login page.

Checkout this concrete example

Enable loading in a blink of an eye of login pages (--external-assets)

By default the theme generated is standalone. Meaning that when your users reach the login pages all scripts, images and stylesheet are downloaded from the Keycloak server.
If you are specifically building a theme to integrate with an app or a website that allows users to first browse unauthenticated before logging in, you will get a significant performance boost if you jump through those hoops:

  • Provide the url of your app in the homepage field of package.json. ex
  • Build the theme using npx build-keycloak-theme --external-assets ex
  • Enable long-term assets caching on the server hosting your app.
  • Make sure not to build your app and the keycloak theme separately and remember to update the Keycloak theme every time you update your app.
  • Be mindful that if your app is down your login pages are down as well.

Checkout a complete setup here

User profile and frontend form validation

NOTE: In reality the regexp used in this gif doesn't work server side, the regexp pattern should be ^[^@]@gmail\.com$ 😬 .

User Profile is a Keycloak feature that enables to define, from the admin console, what information you want to collect on your users in the register page and to validate inputs on the frontend, in realtime!

NOTE: User profile is only available in Keycloak 15 and it's a beta feature that needs to be enabled when launching keycloak and enabled in the console.

Keycloakify, in register-user-profile.ftl, provides frontend validation out of the box.

For implementing your own register-user-profile.ftl page, you can use import { useFormValidationSlice } from "keycloakify";.
Find usage example here.

As for right now it's not possible to define a pattern for the password from the admin console. You can however pass validators for it to the useFormValidationSlice function.

Support for Terms and conditions

Many organizations have a requirement that when a new user logs in for the first time, they need to agree to the terms and conditions of the website..

First you need to enable the required action on the Keycloak server admin console:
image

Then to load your own therms of services using like this.

Some pages still have the default theme. Why?

This project only support out of the box the most common user facing pages of Keycloak login.
Here is the complete list of pages (you get them after running yarn test) and here are the pages currently implemented by this module.
If you need to customize pages that are not supported yet or if you need to implement some non standard .ftl pages please refer to Advanced pages configuration.

GitHub Actions

image

Here is a demo repo to show how to automate the building and publishing of the theme (the .jar file).

Limitations

process.env.PUBLIC_URL not supported.

You won't be able to import things from your public directory in your JavaScript code. (This isn't recommended anyway).

@font-face importing fonts from the src/ dir

If you are building the theme with --external-assets this limitation doesn't apply, you can import fonts however you see fit.

Example of setup that won't work

Possible workarounds

  • Use --external-assets.
  • If it is possible, use Google Fonts or any other font provider.
  • If you want to host your font recommended approach is to move your fonts into the public directory and to place your @font-face statements in the public/index.html.
    Example here.
  • You can also use non relative url but don't forget Access-Control-Allow-Origin.

Implement context persistence (optional)

If, before logging in, a user has selected a specific language you don't want it to be reset to default when the user gets redirected to the login or register pages.

Same goes for the dark mode, you don't want, if the user had it enabled to show the login page with light themes.

The problem is that you are probably using localStorage to persist theses values across reload but, as the Keycloak pages are not served on the same domain that the rest of your app you won't be able to carry over states using localStorage.

The only reliable solution is to inject parameters into the URL before redirecting to Keycloak. We integrate with keycloak-js, by providing you a way to tell keycloak-js that you would like to inject some search parameters before redirecting.

The method also works with @react-keycloak/web (use the initOptions).

You can implement your own mechanism to pass the states in the URL and restore it on the other side but we recommend using powerhooks/useGlobalState from the library powerhooks that provide an elegant way to handle states such as isDarkModeEnabled or selectedLanguage.

Let's modify the example from the official keycloak-js documentation to enables the states of useGlobalStates to be injected in the URL before redirecting.
Note that the states are automatically restored on the other side by powerhooks

import keycloak_js from "keycloak-js";
import { injectGlobalStatesInSearchParams } from "powerhooks/useGlobalState";
import { createKeycloakAdapter } from "keycloakify";

//...

const keycloakInstance = keycloak_js({
    "url": "http://keycloak-server/auth",
    "realm": "myrealm",
    "clientId": "myapp",
});

keycloakInstance.init({
    "onLoad": "check-sso",
    "silentCheckSsoRedirectUri": window.location.origin + "/silent-check-sso.html",
    "adapter": createKeycloakAdapter({
        "transformUrlBeforeRedirect": injectGlobalStatesInSearchParams,
        keycloakInstance,
    }),
});

//...

If you really want to go the extra miles and avoid having the white flash of the blank html before the js bundle have been evaluated here is a snippet that you can place in your public/index.html if you are using powerhooks/useGlobalState.

Kickstart video

NOTE: keycloak-react-theming was renamed keycloakify since this video was recorded kickstart_video

About the errors related to objectToJson in Keycloak logs.

The logs of your keycloak server will always show this kind of errors every time a client request a page:

FTL stack trace ("~" means nesting-related):
        - Failed at: #local value = object[key]  [in template "login.ftl" in macro "objectToJson_please_ignore_errors" at line 70, column 21]
        - Reached through: @compress  [in template "login.ftl" in macro "objectToJson_please_ignore_errors" at line 36, column 5]
        - Reached through: @objectToJson_please_ignore_errors object=value depth=(dep...  [in template "login.ftl" in macro "objectToJson_please_ignore_errors" at line 81, column 27]
        - Reached through: @compress  [in template "login.ftl" in macro "objectToJson_please_ignore_errors" at line 36, column 5]
        - Reached through: @objectToJson_please_ignore_errors object=(.data_model) de...  [in template "login.ftl" at line 163, column 43]

Theses are expected to show up in the log. Unfortunately, there is nothing I know of that can be done to avoid them or even mute them. They can be, however, safely ignored.

To converts the .ftl values into a JavaScript object without making assumptions on the .data_model we have to do things that throws.
It's all-right because every statement that can fail is inside an <#attempt><#recorver> block but it results in errors being printed to the logs.

Adding custom message (to i18n/useKcMessage.tsx)

You can reproduce this approach ( don't forget to evaluate the code ).
This approach is a bit hacky as it doesn't provide type safety but it works.

Email domain whitelist

NOTE: This have been kind of deprecated by user attribute you could use a pattern like this one to whitelist email domains.

If you want to restrict the emails domain that can register, you can use this plugin and kcRegisterContext["authorizedMailDomains"] to validate on.

Changelog highlights

v4

  • Out of the box frontend form validation 🥳
  • Improvements (and breaking changes in import { useKcMessage } from "keycloakify".

v3

No breaking changes except that @emotion/react, tss-react and powerhooks are now peerDependencies instead of being just dependencies.
It's important to avoid problem when using keycloakify alongside mui and when passing params from the app to the login page.

v2.5

v2

  • It's now possible to implement custom .ftl pages.
  • Support for Keycloak plugins that introduce non standard ftl values. (Like for example this plugin that define authorizedMailDomains in register.ftl).
Comments
  • Unable to use messagesPerField existsError and get

    Unable to use messagesPerField existsError and get

    Hello,

    I'm glad that you introduced messagesPerField.exists and messagesPerField.get in the new keycloakify v2.4.0 ! Though, I'm unable to use it. Looking at the code, it seems that if I have no profile.attributes in my configuration. I don't know if that's necessary as I would like to use it on classic fields such as userLabel, username, email, firstName, lastName, password and password-confirm.

    I'm not sure if this is a bug or whether it is intended. I would be happy if you could update the lib for it to iterate on those values at least.

    Cheers!

    opened by Romcol 18
  • White blank page after password update action page

    White blank page after password update action page

    Hi there !

    I am having strange white page instead of page with text and button to proceed to my website page.

    image

    Here is the code for page before white blank page - the page with passsword setting : https://gist.github.com/hookenful/a454ce48d1b22f95cfbe73a58b01e91a

    And it is the code of blank page: https://gist.github.com/hookenful/172626bc633f01cf14dd4c8b18d1a543

    What can be wrong?

    Thanks in advance for any help :)

    opened by hookenful 15
  • Using keycloak version 18 yields TemplateModelException

    Using keycloak version 18 yields TemplateModelException

    FYI: While evaluating with keycloak version 18 we see the following error when trying to load the login page. With 17.0.1 this seems to work fine.

    2022-04-22 12:39:11,303 ERROR [freemarker.runtime] (executor-thread-2) Error executing FreeMarker template part in the #attempt block: freemarker.core._TemplateModelException: An error has occurred when reading existing sub-variable "loginAction"; see cause exception! The type of the containing value was: extended_hash+string (org.keycloak.forms.login.freemarker.model.UrlBean wrapped into f.e.b.StringModel)
    
    ----
    FTL stack trace ("~" means nesting-related):
    	- Failed at: #if !object[key]??  [in template "error.ftl" in function "ftl_object_to_js_code_declaring_an_object" at line 157, column 21]
    	- Reached through: ${ftl_object_to_js_code_declaring_an_...  [in template "error.ftl" at line 7, column 1]
    ----
    	at freemarker.ext.beans.BeanModel.get(BeanModel.java:185)
    	at freemarker.core.DynamicKeyName.dealWithStringKey(DynamicKeyName.java:164)
    	at freemarker.core.DynamicKeyName._eval(DynamicKeyName.java:84)
    	at freemarker.core.Expression.eval(Expression.java:101)
    	at freemarker.core.ExistsExpression._eval(ExistsExpression.java:49)
    	at freemarker.core.Expression.eval(Expression.java:101)
    	at freemarker.core.Expression.evalToBoolean(Expression.java:177)
    	at freemarker.core.Expression.evalToBoolean(Expression.java:163)
    	at freemarker.core.NotExpression.evalToBoolean(NotExpression.java:34)
    	at freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48)
    	at freemarker.core.Environment.visit(Environment.java:347)
    	at freemarker.core.Environment.visit(Environment.java:353)
    	at freemarker.core.Environment.visitAttemptRecover(Environment.java:564)
    	at freemarker.core.AttemptBlock.accept(AttemptBlock.java:45)
    	at freemarker.core.Environment.visit(Environment.java:383)
    	at freemarker.core.IteratorBlock$IterationContext.executedNestedContentForCollOrSeqListing(IteratorBlock.java:291)
    	at freemarker.core.IteratorBlock$IterationContext.executeNestedContent(IteratorBlock.java:271)
    	at freemarker.core.IteratorBlock$IterationContext.accept(IteratorBlock.java:244)
    	at freemarker.core.Environment.visitIteratorBlock(Environment.java:657)
    	at freemarker.core.IteratorBlock.acceptWithResult(IteratorBlock.java:108)
    	at freemarker.core.IteratorBlock.accept(IteratorBlock.java:94)
    	at freemarker.core.Environment.visit(Environment.java:347)
    	at freemarker.core.Environment.visit(Environment.java:389)
    	at freemarker.core.Environment.invokeMacroOrFunctionCommonPart(Environment.java:889)
    	at freemarker.core.Environment.invokeMacro(Environment.java:825)
    	at freemarker.core.Environment.invokeFunction(Environment.java:841)
    	at freemarker.core.MethodCall._eval(MethodCall.java:65)
    	at freemarker.core.Expression.eval(Expression.java:101)
    	at freemarker.core.Assignment.accept(Assignment.java:134)
    	at freemarker.core.Environment.visit(Environment.java:383)
    	at freemarker.core.IteratorBlock$IterationContext.executedNestedContentForCollOrSeqListing(IteratorBlock.java:291)
    	at freemarker.core.IteratorBlock$IterationContext.executeNestedContent(IteratorBlock.java:271)
    	at freemarker.core.IteratorBlock$IterationContext.accept(IteratorBlock.java:244)
    	at freemarker.core.Environment.visitIteratorBlock(Environment.java:657)
    	at freemarker.core.IteratorBlock.acceptWithResult(IteratorBlock.java:108)
    	at freemarker.core.IteratorBlock.accept(IteratorBlock.java:94)
    	at freemarker.core.Environment.visit(Environment.java:347)
    	at freemarker.core.Environment.visit(Environment.java:389)
    	at freemarker.core.Environment.invokeMacroOrFunctionCommonPart(Environment.java:889)
    	at freemarker.core.Environment.invokeMacro(Environment.java:825)
    	at freemarker.core.Environment.invokeFunction(Environment.java:841)
    	at freemarker.core.MethodCall._eval(MethodCall.java:65)
    	at freemarker.core.Expression.eval(Expression.java:101)
    	at freemarker.core.BuiltInsForOutputFormatRelated$AbstractConverterBI.calculateResult(BuiltInsForOutputFormatRelated.java:50)
    	at freemarker.core.MarkupOutputFormatBoundBuiltIn._eval(MarkupOutputFormatBoundBuiltIn.java:40)
    	at freemarker.core.Expression.eval(Expression.java:101)
    	at freemarker.core.DollarVariable.calculateInterpolatedStringOrMarkup(DollarVariable.java:100)
    	at freemarker.core.DollarVariable.accept(DollarVariable.java:63)
    	at freemarker.core.Environment.visit(Environment.java:347)
    	at freemarker.core.Environment.visit(Environment.java:353)
    	at freemarker.core.Environment.process(Environment.java:326)
    	at freemarker.template.Template.process(Template.java:383)
    	at org.keycloak.theme.FreeMarkerUtil.processTemplate(FreeMarkerUtil.java:68)
    	at org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider.processTemplate(FreeMarkerLoginFormsProvider.java:530)
    	at org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider.createResponse(FreeMarkerLoginFormsProvider.java:295)
    	at org.keycloak.forms.login.freemarker.FreeMarkerLoginFormsProvider.createErrorPage(FreeMarkerLoginFormsProvider.java:646)
    	at org.keycloak.services.ErrorPage.error(ErrorPage.java:31)
    	at org.keycloak.services.resources.SessionCodeChecks.restartAuthenticationSessionFromCookie(SessionCodeChecks.java:412)
    	at org.keycloak.services.resources.SessionCodeChecks.initialVerifyAuthSession(SessionCodeChecks.java:193)
    	at org.keycloak.services.resources.SessionCodeChecks.initialVerify(SessionCodeChecks.java:200)
    	at org.keycloak.services.resources.LoginActionsService.checksForCode(LoginActionsService.java:201)
    	at org.keycloak.services.resources.LoginActionsService.authenticate(LoginActionsService.java:266)
    	at org.keycloak.services.resources.LoginActionsService.authenticateForm(LoginActionsService.java:349)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    	at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)
    	at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
    	at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660)
    	at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524)
    	at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474)
    	at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
    	at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476)
    	at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434)
    	at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:192)
    	at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:141)
    	at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:32)
    	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)
    	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
    	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
    	at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
    	at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
    	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
    	at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
    	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
    	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:82)
    	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:42)
    	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
    	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
    	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
    	at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:67)
    	at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:55)
    	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
    	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
    	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
    	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:380)
    	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:358)
    	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
    	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
    	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
    	at org.keycloak.quarkus.runtime.integration.web.QuarkusRequestFilter.lambda$createBlockingHandler$1(QuarkusRequestFilter.java:71)
    	at io.vertx.core.impl.ContextImpl.lambda$null$0(ContextImpl.java:159)
    	at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:100)
    	at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:157)
    	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:543)
    	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
    	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
    	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
    	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
    	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    	at java.base/java.lang.Thread.run(Thread.java:829)
    Caused by: java.lang.reflect.InvocationTargetException
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    	at freemarker.ext.beans.BeansWrapper.invokeMethod(BeansWrapper.java:1552)
    	at freemarker.ext.beans.BeanModel.invokeThroughDescriptor(BeanModel.java:233)
    	at freemarker.ext.beans.BeanModel.get(BeanModel.java:152)
    	... 111 more
    Caused by: java.lang.RuntimeException: action URI not set
    	at org.keycloak.forms.login.freemarker.model.UrlBean.getLoginAction(UrlBean.java:48)
    	... 118 more
    

    Are you able to reproduce that with the demo app and keycloak version 18 as well?

    opened by valkum 15
  • What changes should be made to make it work with custom webpack config.

    What changes should be made to make it work with custom webpack config.

    Is there any specific guide to make it work with custom webpack config.

    When I tried it is just tries to find the bundle.js on the keycloak domain and gives 404 error.

    Any suggestion, how can I fix it or using create-react-app is absolutely essential ?

    opened by shubhamdeodia 15
  • Unable to launch on Apple M1

    Unable to launch on Apple M1

    Hello,

    I have build a Keycloak theme but when I try to launch the docker container I am facing the following issues (see below). Do you know how could I solve this problem? Thank you

    09:31:08,790 ERROR [org.jboss.modcluster] (ServerService Thread Pool -- 60) MODCLUSTER000034: Failed to start advertise listener: java.net.SocketException: Protocol not available (Error setting socket option) at java.base/java.net.PlainDatagramSocketImpl.socketSetOption0(Native Method) at java.base/java.net.PlainDatagramSocketImpl.socketSetOption(PlainDatagramSocketImpl.java:91) at java.base/java.net.AbstractPlainDatagramSocketImpl.setOption(AbstractPlainDatagramSocketImpl.java:352) at java.base/java.net.MulticastSocket.setInterface(MulticastSocket.java:477) at [email protected]//org.jboss.modcluster.advertise.impl.AdvertiseListenerImpl.init(AdvertiseListenerImpl.java:151) at [email protected]//org.jboss.modcluster.advertise.impl.AdvertiseListenerImpl.start(AdvertiseListenerImpl.java:161) at [email protected]//org.jboss.modcluster.ModClusterService.init(ModClusterService.java:165) at [email protected]//org.wildfly.mod_cluster.undertow.UndertowEventHandlerAdapterService.start(UndertowEventHandlerAdapterService.java:83) at [email protected]//org.wildfly.clustering.service.AsyncServiceConfigurator$AsyncService.lambda$start$0(AsyncServiceConfigurator.java:117) at [email protected]//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35) at [email protected]//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982) at [email protected]//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486) at [email protected]//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377) at java.base/java.lang.Thread.run(Thread.java:834) at [email protected]//org.jboss.threads.JBossThread.run(JBossThread.java:485)

    help wanted 
    opened by Alejandro-J-Caputto 15
  • [FIX] Add attempt and recover to prevent ftl from crashing

    [FIX] Add attempt and recover to prevent ftl from crashing

    Hi,

    I have notice 500 errors on some of my customs .ftl in some scenarios (update-user-profile.ftl , idp-review-user-profile.ftl) probably due to the data_model not being converted in a successfully way. Adding <#attempt> and <#recover> inside the whole function prevent at least the page from crashing and going 500.

    Thanks for merging!

    opened by Romcol 14
  • no such file or directory, scandir 'E:\keycloak_custom\my-app\build_keycloak\src\main\resources\theme\my-app\tmp_xxKdLpdIdLd\keycloak\login\resources'

    no such file or directory, scandir 'E:\keycloak_custom\my-app\build_keycloak\src\main\resources\theme\my-app\tmp_xxKdLpdIdLd\keycloak\login\resources'

    Error: ENOENT: no such file or directory, scandir 'E:\keycloak_custom\my-app\build_keycloak\src\main\resources\theme\my-app\tmp_xxKdLpdIdLd\keycloak\login\resources'
        at Object.readdirSync (node:fs:1390:3)
        at crawlRec (E:\keycloak_custom\my-app\node_modules\keycloakify\bin\tools\crawl.js:41:39)
        at crawl (E:\keycloak_custom\my-app\node_modules\keycloakify\bin\tools\crawl.js:61:9)
        at transformCodebase (E:\keycloak_custom\my-app\node_modules\keycloakify\bin\tools\transformCodebase.js:48:50)
        at generateKeycloakThemeResources (E:\keycloak_custom\my-app\node_modules\keycloakify\bin\build-keycloak-theme\generateKeycloakThemeResources.js:124:51)
        at main (E:\keycloak_custom\my-app\node_modules\keycloakify\bin\build-keycloak-theme\build-keycloak-theme.js:57:73)
        at Object.<anonymous> (E:\keycloak_custom\my-app\node_modules\keycloakify\bin\build-keycloak-theme\index.js:17:37)
        at Module._compile (node:internal/modules/cjs/loader:1101:14)
        at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
        at Module.load (node:internal/modules/cjs/loader:981:32) {
      errno: -4058,
      syscall: 'scandir',
      code: 'ENOENT',
      path: 'E:\\keycloak_custom\\my-app\\build_keycloak\\src\\main\\resources\\theme\\my-app\\tmp_xxKdLpdIdLd\\keycloak\\login\\resources'
    }
    
    opened by kousherAlam 14
  • Ability to add custom .ftl pages

    Ability to add custom .ftl pages

    Hello!

    We have a highly customized Keycloak setup and have added a couple of additional '.ftl' pages to our login & signup flows.

    As far as I know, the current version does not support such a thing as building arbitrary Freemarker pages. The only way I see to achieve this is to fork the library and add them manually to the build script.

    Will it be possible to incorporate such functionality into the library? I could work on the PR if it's feasible (any advice is more than welcome ofc).

    P.S. Thanks for the lib, it's a great piece of work, saved so much time for us 🙌

    opened by asashay 14
  • Again phantom error after update to v5.3.2

    Again phantom error after update to v5.3.2

    Hi there!

    Getting again that strange problem while activating update-password link from email: image

    Source code of error page: https://pastebin.com/TxQPpR4x

    Thank for any help!

    opened by hookenful 13
  • Support Keycloak 14 new validation attribute feature :rocket:

    Support Keycloak 14 new validation attribute feature :rocket:

    Links

    https://www.keycloak.org/docs/latest/server_admin/#user-profile https://github.com/keycloak/keycloak/blob/14.0.0/themes/src/main/resources/theme/base/login/register.ftl https://hub.docker.com/r/jboss/keycloak/tags?page=1&ordering=last_updated

    opened by garronej 13
  • feat: add logout-confirm

    feat: add logout-confirm

    Bonjour, pour Keycloak 18, je souhaiterai ajouter la possibilité de thémer logout-confirm.tfl

    La traduction française des nouveaux termes n'est pas encore dispo, y-a-t-il moyen de fournir une trad par défaut en FR pour les nouveaux termes (ex: logoutConfirmTitle ou logoutConfirmHeader), ou faut-il générer les fichiers pour la 18.0.0 ?

    opened by revolunet 12
  • Failure on WebauthnAuthenticate (race condition?)

    Failure on WebauthnAuthenticate (race condition?)

    I'm running into a very consistently reproduced WebAuthn bug when using a keycloakify 6.8.5 based theme on Keycloak 19.0.3. When authenticating via a security key (tried various kinds) to the account console, I get the following:

    image

    Investigating the failure further, I noticed that the POST payload being sent for authentication (with the required values like clientDataJSON, authenticatorData, etc.) had all of the values un-set.

    After some experimentation, I realized that simply delaying the form submission worked around the problem. Specifically, I delayed the form submission like so:

    setTimeout(() => {
        submitForm();
    }, 1000);
    

    I'm investigating the root cause, but I figured I'd open the issue in case any React experts share a hunch.

    opened by upfluentpatrickpilch 1
  • Update garronej_modules_update (landingpage)

    Update garronej_modules_update (landingpage)

    Mend Renovate

    This PR contains the following updates:

    | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | evt (source) | 2.4.5 -> 2.4.13 | age | adoption | passing | confidence | | powerhooks | ^0.20.22 -> ^0.20.32 | age | adoption | passing | confidence | | tsafe | ^1.1.3 -> ^1.4.1 | age | adoption | passing | confidence |


    Release Notes

    garronej/evt

    v2.4.13

    Compare Source

    v2.4.11

    Compare Source

    v2.4.10

    Compare Source

    v2.4.8

    Compare Source

    v2.4.7

    Compare Source

    v2.4.6

    Compare Source

    garronej/powerhooks

    v0.20.32

    Compare Source

    What's Changed
    Other Changes

    Full Changelog: https://github.com/garronej/powerhooks/compare/v0.20.31...v0.20.32

    v0.20.31

    Compare Source

    What's Changed

    Other Changes

    Full Changelog: https://github.com/garronej/powerhooks/compare/v0.20.30...v0.20.31

    v0.20.30

    Compare Source

    What's Changed
    Other Changes

    Full Changelog: https://github.com/garronej/powerhooks/compare/v0.20.29...v0.20.30

    v0.20.29

    Compare Source

    What's Changed

    Other Changes

    Full Changelog: https://github.com/garronej/powerhooks/compare/v0.20.28...v0.20.29

    v0.20.28

    Compare Source

    What's Changed
    Other Changes

    Full Changelog: https://github.com/garronej/powerhooks/compare/v0.20.27...v0.20.28

    v0.20.27

    Compare Source

    Full Changelog: https://github.com/garronej/powerhooks/compare/v0.20.26...v0.20.27

    v0.20.26

    Compare Source

    Full Changelog: https://github.com/garronej/powerhooks/compare/v0.20.25...v0.20.26

    v0.20.25

    Compare Source

    Full Changelog: https://github.com/garronej/powerhooks/compare/v0.20.24...v0.20.25

    v0.20.24

    Compare Source

    What's Changed

    Other Changes

    Full Changelog: https://github.com/garronej/powerhooks/compare/v0.20.23...v0.20.24

    v0.20.23

    Compare Source

    What's Changed

    Other Changes

    Full Changelog: https://github.com/garronej/powerhooks/compare/v0.20.22...v0.20.23

    garronej/tsafe

    v1.4.1

    Compare Source

    v1.4.0

    Compare Source

    v1.3.4

    Compare Source

    v1.3.3

    Compare Source

    v1.3.2

    Compare Source

    v1.3.0

    Compare Source

    Release as CJS and ESM

    v1.2.1

    Compare Source

    v1.2.0

    Compare Source


    Configuration

    📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

    🚦 Automerge: Enabled.

    Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

    👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.


    • [ ] If you want to rebase/retry this PR, check this box

    This PR has been generated by Mend Renovate. View repository job log here.

    opened by renovate[bot] 0
  • Publish a tutorial on Youtube

    Publish a tutorial on Youtube

    If someone would like to shoot a Keycloakify video tutorial, it would greatly help beginners create their first Keycloak theme.

    I think such a video would get a some views.

    Of course you can publish it from your Youtube channel.

    good first issue hacktoberfest-accepted 
    opened by garronej 2
  • Feature request: Increase testibility for i18n

    Feature request: Increase testibility for i18n

    You did a great work with useI18n() hook and that made development much smoother. But now I am facing difficulties with migration from v5. My unit tests now missing i18n object which is passed as a prop now. It would be really handy if you exported this function from keycloakify/lib/i18n

    Then it would be possible to write unit tests like this

    import { screen, render } from '@testing-library/react'
    import { getI18n } from 'keycloakify/lib/i18n'
    import { kcContext } from './mocks'
    
    
    test('should render a title', () => {
     const i18n = await getI18n({ kcContext, extraMessages: {en: {myTitle: "Test title"}} })
     render(<MyComponent i18n={i18n} titleKey="myTitle" />)
     expect(screen.getByText('Test title')).toBeInTheDocument()
    })
    
    

    or like this

    import { screen, render } from '@testing-library/react'
    import { getI18nSync } from 'keycloakify/lib/i18n'
    import { kcContext } from './mocks'
    
    const i18n = getI18nSync({ kcContext, extraMessages: {en: {myTitle: "Test title"}} })
    
    test('should render a title', () => {
     render(<MyComponent i18n={i18n} titleKey="myTitle" />)
     expect(screen.getByText('Test title')).toBeInTheDocument()
    })
    
    
    opened by oleh-hutsalo-ks 1
  • Cannot load css module files

    Cannot load css module files

    I imported a css module file like so:

    import styles from "./style.module.css";
    

    this is how I use the imported styles in case it is relevant but I doubt it is:

        <Component
          className={cx({ // cx is an alias for the "classnames" package
            [styles["email-input-success"]]: valid,
          })}
          // Other props
        >
          {/* Children */}
        </Component>
    

    and I got these errors: image

    this is the content of the css module file:

    .email-input-success input {
      @apply !border-green-500 !shadow-none; /* I am using tailwindcss */
    }
    

    I do not know anything else, I provided all the information I considered relevant. Feel free to ask for more information if you have a clue on why this is happening!

    opened by ferretwithaberet 1
Releases(v6.8.9)
  • v6.8.9(Nov 29, 2022)

    What's Changed

    Other Changes

    • Update garronej_modules_update (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/210

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.8.8...v6.8.9

    Source code(tar.gz)
    Source code(zip)
  • v6.8.8(Nov 16, 2022)

    What's Changed

    Other Changes

    • Update dependency powerhooks to ^0.20.27 (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/207

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.8.7...v6.8.8

    Source code(tar.gz)
    Source code(zip)
  • v6.8.7(Nov 13, 2022)

    What's Changed

    Other Changes

    • Update dependency powerhooks to ^0.20.26 (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/205

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.8.6...v6.8.7

    Source code(tar.gz)
    Source code(zip)
  • v6.8.6(Nov 11, 2022)

  • v6.8.5(Oct 25, 2022)

    What's Changed

    Other Changes

    • Update garronej_modules_update (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/202

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.8.4...v6.8.5

    Source code(tar.gz)
    Source code(zip)
  • v6.8.4(Oct 15, 2022)

  • v6.8.3(Oct 15, 2022)

  • v6.8.2(Oct 15, 2022)

  • v6.8.1(Oct 14, 2022)

    What's Changed

    Other Changes

    • Update garronej_modules_update (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/196

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.8.0...v6.8.1

    Source code(tar.gz)
    Source code(zip)
  • v6.8.0(Oct 13, 2022)

  • v6.7.2(Oct 13, 2022)

    What's Changed

    Other Changes

    • Update garronej_modules_update (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/194

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.7.1...v6.7.2

    Source code(tar.gz)
    Source code(zip)
  • v6.7.1(Oct 11, 2022)

    What's Changed

    Other Changes

    • Add support for webauthn-authenticate.ftl by @Mstrodl in https://github.com/InseeFrLab/keycloakify/pull/185
    • Update garronej_modules_update (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/189

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.6.3...v6.7.1

    Source code(tar.gz)
    Source code(zip)
  • v6.6.3(Oct 5, 2022)

  • v6.6.2(Oct 5, 2022)

  • v6.6.1(Oct 5, 2022)

    What's Changed

    Other Changes

    • Add support for login-password.ftl by @Mstrodl in https://github.com/InseeFrLab/keycloakify/pull/184
    • Update dependency tss-react to ^4.3.4 (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/187

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.5.0...v6.6.1

    Source code(tar.gz)
    Source code(zip)
  • v6.5.0(Oct 4, 2022)

    What's Changed

    Other Changes

    • Add login-username support by @Mstrodl in https://github.com/InseeFrLab/keycloakify/pull/183

    New Contributors

    • @Mstrodl made their first contribution in https://github.com/InseeFrLab/keycloakify/pull/183

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.4.3...v6.5.0

    Source code(tar.gz)
    Source code(zip)
  • v6.4.3(Oct 4, 2022)

    What's Changed

    Other Changes

    • Update dependency tss-react to ^4.3.3 (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/182

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.4.2...v6.4.3

    Source code(tar.gz)
    Source code(zip)
  • v6.4.2(Oct 1, 2022)

    What's Changed

    Other Changes

    • Update garronej_modules_update (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/181

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.4.1...v6.4.2

    Source code(tar.gz)
    Source code(zip)
  • v6.4.1(Sep 29, 2022)

    What's Changed

    Other Changes

    • Update garronej_modules_update (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/179

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.4.0...v6.4.1

    Source code(tar.gz)
    Source code(zip)
  • v6.4.0(Sep 27, 2022)

    What's Changed

    Other Changes

    • Update garronej_modules_update (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/174

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.3.5...v6.4.0

    Source code(tar.gz)
    Source code(zip)
  • v6.3.6(Sep 14, 2022)

    What's Changed

    Other Changes

    • Update garronej_modules_update (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/174

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.3.5...v6.3.6

    Source code(tar.gz)
    Source code(zip)
  • v6.3.5(Sep 13, 2022)

  • v6.3.4(Sep 12, 2022)

  • v6.3.3(Sep 10, 2022)

    What's Changed

    Other Changes

    • Update garronej_modules_update (main) by @renovate in https://github.com/InseeFrLab/keycloakify/pull/172

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.3.2...v6.3.3

    Source code(tar.gz)
    Source code(zip)
  • v5.9.3(Sep 11, 2022)

  • v6.3.2(Sep 9, 2022)

  • v6.3.0(Sep 9, 2022)

  • v6.2.0(Sep 9, 2022)

  • v6.1.0(Sep 8, 2022)

    What's Changed

    Other Changes

    • feat: add silent flag by @Tasyp in https://github.com/InseeFrLab/keycloakify/pull/170

    Full Changelog: https://github.com/InseeFrLab/keycloakify/compare/v6.0.3...v6.1.0

    Source code(tar.gz)
    Source code(zip)
  • v6.0.3(Sep 7, 2022)

Owner
Lab de @InseeFr
null
Create, isolate and test modular UI components in React.

ui-harness Isolate, test and document modular UI with React using familiar describe/it testing semantics. http://uiharness.com Quick Start (1-minute)

Phil Cockfield 269 Sep 27, 2022
An extremely fast create-react-app replacement.

Create Esbuild App / esbuild-scripts ?? An extremely fast create-react-app replacement. What is it? create-esbuild-app is a wrapper around create-reac

Luke Sheard 85 Nov 27, 2022
Getting Started with Create React App

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: ya

Douglas Morais 21 Oct 18, 2022
A next-generation tool to create blazing-fast documentation sites.

Table of contents About Showcase sites 1. documentation 2. grommet-controls 3. theme-ui design system 4. starter projects Motivation Inspiration Roadm

null 72 Oct 24, 2022
Create Redux State At Run Time

use-redux-states Create redux state at runtime. Overview use-redux-states allows

Michael Ishola 2 Feb 22, 2022
Commonninja-react - A small library for using Common Ninja's plugins in React projects

Common Ninja for React A small library for using Common Ninja's plugins in React

Common Ninja 1 Jan 3, 2022
A demo of LaunchDarkly, React, and Vite, using the Pokémon API!

Pokémon Feature Flags demo Here's a demo for integrating feature flags into a React project! Built with React, Vite, the PokeAPI, and LaunchDarkly! Wh

Cassidy Williams 15 Jan 5, 2022
Engineering at EcoVadis 16 Aug 22, 2022
Random quote machine built using React v17 for the freeCodeCamp project.

Random Quote Machine This project is a random quote machine I built using React for FreeCodeCamp's Front End Development Libraries Project. This proje

Wendy Surya Wijaya 6 Nov 10, 2022
An implementation of Minesweeper game using Javascript and React.js

Minesweeper About An implementation of a Minesweeper game. Built with Typescript and React. Getting started git clone https://github.com/FakeMetalFan/

Maksym Boiko 1 Jan 10, 2022
Expo plugin for adding @intercom/react-native-intercom using expo config plugins

Expo Config Plugin react-native-intercom An Expo config plugin for easily setting up React Native Intercom Installation Prerequisites App project usin

Chadwick Maycumber 56 Dec 6, 2022
Experiment in creating a custom react renderer using an offscreen webgl canvas on top of Skia CanvasKit

React-CanvasKit Experimental implementation of Skia CanvasKit using ReactJS. This implementation allows you to use all familiar React concepts like ho

uDev.be 80 Nov 22, 2022
A Hangman game made using react.js

Hangman The project is made to understand REACT concepts like : how to handle ev

Dhruv Prajapati 1 Dec 17, 2021
Lights Out Game Built Using React

Lights Out Game This project is made for better understanding of states and prop

Dhruv Prajapati 2 Feb 10, 2022
This website is made using React.js and Bootstrap5, where anyone can take service from team. it's a fully responsive.

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 1 Jan 19, 2022
VisionCamera Frame Processor Plugin to label images using MLKit Vision

vision-camera-image-labeler A VisionCamera Frame Processor Plugin to label images using MLKit Vision Image Labeling. Installation npm install vision-c

Marc Rousavy 71 Dec 5, 2022
This helper plugin draws kanban boards using the outliner approach

Overview As the name suggests, this helper plugin draws kanban boards using the outliner approach. This plugin offers 2 options: Please note that you

null 66 Nov 26, 2022
A next.js website that fetches random user data using axios

next.js random user using axios.get() example typescript by: UnusualAbsurd Live

UnusualAbsurd 1 Dec 17, 2021
A browser extension for using kbar in HackMD

hackbar A browser extension for using kbar in HackMD Report Bug · Request Featur

TzuWei 20 Oct 10, 2022