mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-16 16:20:04 +00:00
Implement frontend structure
This commit is contained in:
@@ -45,3 +45,4 @@ out/
|
|||||||
/backend/src/main/resources/static/
|
/backend/src/main/resources/static/
|
||||||
/docker/docker-compose.yml
|
/docker/docker-compose.yml
|
||||||
/.gameyfin/
|
/.gameyfin/
|
||||||
|
/frontend/
|
||||||
|
|||||||
+45
-34
@@ -1,63 +1,74 @@
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("org.springframework.boot") version "3.2.2"
|
id("org.springframework.boot") version "3.2.2"
|
||||||
id("io.spring.dependency-management") version "1.1.4"
|
id("io.spring.dependency-management") version "1.1.4"
|
||||||
id("com.vaadin") version "24.3.3"
|
id("com.vaadin") version "24.3.3"
|
||||||
kotlin("jvm") version "1.9.22"
|
kotlin("jvm") version "1.9.22"
|
||||||
kotlin("plugin.spring") version "1.9.22"
|
kotlin("plugin.spring") version "1.9.22"
|
||||||
kotlin("plugin.jpa") version "1.9.22"
|
kotlin("plugin.jpa") version "1.9.22"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "de.grimsi"
|
group = "de.grimsi"
|
||||||
version = "0.0.1-SNAPSHOT"
|
version = "2.0.0-SNAPSHOT"
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = JavaVersion.VERSION_21
|
sourceCompatibility = JavaVersion.VERSION_21
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
compileOnly {
|
compileOnly {
|
||||||
extendsFrom(configurations.annotationProcessor.get())
|
extendsFrom(configurations.annotationProcessor.get())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
name = "Vaadin Addons"
|
||||||
|
url = uri("https://maven.vaadin.com/vaadin-addons")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extra["vaadinVersion"] = "24.3.3"
|
extra["vaadinVersion"] = "24.3.3"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
|
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
// Frontend
|
||||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||||
implementation("com.vaadin:vaadin-spring-boot-starter")
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
implementation("com.vaadin:vaadin-spring-boot-starter")
|
||||||
runtimeOnly("com.h2database:h2")
|
implementation("com.github.mvysny.karibudsl:karibu-dsl-v23:2.1.0")
|
||||||
runtimeOnly("io.micrometer:micrometer-registry-prometheus")
|
implementation("com.github.mvysny.karibu-tools:karibu-tools-23:0.19")
|
||||||
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
implementation("com.flowingcode.addons:font-awesome-iron-iconset:5.2.2")
|
||||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||||
testImplementation("org.springframework.security:spring-security-test")
|
|
||||||
|
// Development
|
||||||
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
|
runtimeOnly("com.h2database:h2")
|
||||||
|
runtimeOnly("io.micrometer:micrometer-registry-prometheus")
|
||||||
|
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||||
|
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||||
|
testImplementation("org.springframework.security:spring-security-test")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyManagement {
|
dependencyManagement {
|
||||||
imports {
|
imports {
|
||||||
mavenBom("com.vaadin:vaadin-bom:${property("vaadinVersion")}")
|
mavenBom("com.vaadin:vaadin-bom:${property("vaadinVersion")}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<KotlinCompile> {
|
tasks.withType<KotlinCompile> {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
freeCompilerArgs += "-Xjsr305=strict"
|
freeCompilerArgs += "-Xjsr305=strict"
|
||||||
jvmTarget = "21"
|
jvmTarget = "21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<Test> {
|
tasks.withType<Test> {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
#-------------------------------------------------------------------------------#
|
||||||
|
# Qodana analysis is configured by qodana.yaml file #
|
||||||
|
# https://www.jetbrains.com/help/qodana/qodana-yaml.html #
|
||||||
|
#-------------------------------------------------------------------------------#
|
||||||
|
version: "1.0"
|
||||||
|
|
||||||
|
#Specify inspection profile for code analysis
|
||||||
|
profile:
|
||||||
|
name: qodana.starter
|
||||||
|
|
||||||
|
#Enable inspections
|
||||||
|
#include:
|
||||||
|
# - name: <SomeEnabledInspectionId>
|
||||||
|
|
||||||
|
#Disable inspections
|
||||||
|
#exclude:
|
||||||
|
# - name: <SomeDisabledInspectionId>
|
||||||
|
# paths:
|
||||||
|
# - <path/where/not/run/inspection>
|
||||||
|
|
||||||
|
projectJDK: "21" #(Applied in CI/CD pipeline)
|
||||||
|
|
||||||
|
#Execute shell command before Qodana execution (Applied in CI/CD pipeline)
|
||||||
|
#bootstrap: sh ./prepare-qodana.sh
|
||||||
|
|
||||||
|
#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline)
|
||||||
|
#plugins:
|
||||||
|
# - id: <plugin.id> #(plugin id can be found at https://plugins.jetbrains.com)
|
||||||
|
|
||||||
|
#Specify Qodana linter for analysis (Applied in CI/CD pipeline)
|
||||||
|
linter: jetbrains/qodana-jvm:latest
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
This directory is automatically generated by Vaadin and contains the pre-compiled
|
||||||
|
frontend files/resources for your project (frontend development bundle).
|
||||||
|
|
||||||
|
It should be added to Version Control System and committed, so that other developers
|
||||||
|
do not have to compile it again.
|
||||||
|
|
||||||
|
Frontend development bundle is automatically updated when needed:
|
||||||
|
- an npm/pnpm package is added with @NpmPackage or directly into package.json
|
||||||
|
- CSS, JavaScript or TypeScript files are added with @CssImport, @JsModule or @JavaScript
|
||||||
|
- Vaadin add-on with front-end customizations is added
|
||||||
|
- Custom theme imports/assets added into 'theme.json' file
|
||||||
|
- Exported web component is added.
|
||||||
|
|
||||||
|
If your project development needs a hot deployment of the frontend changes,
|
||||||
|
you can switch Flow to use Vite development server (default in Vaadin 23.3 and earlier versions):
|
||||||
|
- set `vaadin.frontend.hotdeploy=true` in `application.properties`
|
||||||
|
- configure `vaadin-maven-plugin`:
|
||||||
|
```
|
||||||
|
<configuration>
|
||||||
|
<frontendHotdeploy>true</frontendHotdeploy>
|
||||||
|
</configuration>
|
||||||
|
```
|
||||||
|
- configure `jetty-maven-plugin`:
|
||||||
|
```
|
||||||
|
<configuration>
|
||||||
|
<systemProperties>
|
||||||
|
<vaadin.frontend.hotdeploy>true</vaadin.frontend.hotdeploy>
|
||||||
|
</systemProperties>
|
||||||
|
</configuration>
|
||||||
|
```
|
||||||
|
|
||||||
|
Read more [about Vaadin development mode](https://vaadin.com/docs/next/configuration/development-mode/#pre-compiled-front-end-bundle-for-faster-start-up).
|
||||||
Binary file not shown.
@@ -3,6 +3,7 @@ package de.grimsi.gameyfin
|
|||||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
import org.springframework.boot.runApplication
|
import org.springframework.boot.runApplication
|
||||||
|
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
class GameyfinApplication
|
class GameyfinApplication
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package de.grimsi.gameyfin.security
|
||||||
|
|
||||||
|
import com.vaadin.flow.spring.security.VaadinWebSecurity
|
||||||
|
import de.grimsi.gameyfin.ui.views.LoginView
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer
|
||||||
|
import org.springframework.security.core.userdetails.User
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager
|
||||||
|
import org.springframework.security.provisioning.UserDetailsManager
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
|
||||||
|
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
class SecurityConfiguration : VaadinWebSecurity() {
|
||||||
|
@Throws(Exception::class)
|
||||||
|
override fun configure(http: HttpSecurity) {
|
||||||
|
// Configure your static resources with public access before calling super.configure(HttpSecurity) as it adds final anyRequest matcher
|
||||||
|
http.authorizeHttpRequests { auth: AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry ->
|
||||||
|
auth.requestMatchers(AntPathRequestMatcher("/public/**")).permitAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure your static resources with public access before calling
|
||||||
|
// super.configure(HttpSecurity) as it adds final anyRequest matcher
|
||||||
|
|
||||||
|
super.configure(http)
|
||||||
|
|
||||||
|
// This is important to register your login view to the navigation access control mechanism:
|
||||||
|
setLoginView(http, LoginView::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
public override fun configure(web: WebSecurity) {
|
||||||
|
super.configure(web)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Just for testing
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
fun userDetailsService(): UserDetailsManager {
|
||||||
|
val user: UserDetails =
|
||||||
|
User.withUsername("user")
|
||||||
|
.password("{noop}user")
|
||||||
|
.roles("USER")
|
||||||
|
.build()
|
||||||
|
val admin: UserDetails =
|
||||||
|
User.withUsername("admin")
|
||||||
|
.password("{noop}admin")
|
||||||
|
.roles("ADMIN")
|
||||||
|
.build()
|
||||||
|
return InMemoryUserDetailsManager(user, admin)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package de.grimsi.gameyfin.security
|
||||||
|
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
|
|
||||||
|
fun UserDetails.hasRole(role: String): Boolean {
|
||||||
|
return this.authorities.map { a -> a.authority }.contains("ROLE_".plus(role))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun UserDetails.isAdmin(): Boolean {
|
||||||
|
return hasRole("ADMIN")
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package de.grimsi.gameyfin.ui.layouts
|
||||||
|
|
||||||
|
import com.flowingcode.vaadin.addons.fontawesome.FontAwesome
|
||||||
|
import com.github.mvysny.karibudsl.v10.*
|
||||||
|
import com.github.mvysny.kaributools.tooltip
|
||||||
|
import com.vaadin.flow.component.ClickEvent
|
||||||
|
import com.vaadin.flow.component.applayout.AppLayout
|
||||||
|
import com.vaadin.flow.component.menubar.MenuBarVariant
|
||||||
|
import com.vaadin.flow.component.orderedlayout.FlexComponent
|
||||||
|
import com.vaadin.flow.component.orderedlayout.HorizontalLayout
|
||||||
|
import com.vaadin.flow.router.RouterLayout
|
||||||
|
import com.vaadin.flow.spring.security.AuthenticationContext
|
||||||
|
import de.grimsi.gameyfin.security.isAdmin
|
||||||
|
import de.grimsi.gameyfin.ui.resources.PublicResources
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails
|
||||||
|
|
||||||
|
|
||||||
|
class MainLayout(@field:Transient private val authContext: AuthenticationContext) : KComposite(), RouterLayout {
|
||||||
|
|
||||||
|
|
||||||
|
private val appLayout: AppLayout
|
||||||
|
|
||||||
|
init {
|
||||||
|
val user = authContext.getAuthenticatedUser(UserDetails::class.java).get()
|
||||||
|
|
||||||
|
appLayout = ui {
|
||||||
|
|
||||||
|
appLayout {
|
||||||
|
navbar {
|
||||||
|
flexLayout {
|
||||||
|
setWidthFull()
|
||||||
|
alignItems = FlexComponent.Alignment.CENTER
|
||||||
|
|
||||||
|
image(PublicResources.GAMEYFIN_LOGO_WHITE_BORDER.path) {
|
||||||
|
setWidthFull()
|
||||||
|
height = "40px"
|
||||||
|
className = "header-logo"
|
||||||
|
}
|
||||||
|
|
||||||
|
horizontalLayout {
|
||||||
|
alignItems = FlexComponent.Alignment.CENTER
|
||||||
|
|
||||||
|
val a = avatar(user.username) {
|
||||||
|
tooltip = user.username
|
||||||
|
abbreviation = user.username.take(2).uppercase()
|
||||||
|
colorIndex = user.username[0].code.toByte().mod(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
menuBar {
|
||||||
|
addThemeVariants(MenuBarVariant.LUMO_ICON)
|
||||||
|
item(a) {
|
||||||
|
item(menuItem(FontAwesome.Solid.USER, "Profile"))
|
||||||
|
if (user.isAdmin()) item(menuItem(FontAwesome.Solid.COG, "Administration"))
|
||||||
|
item(menuItem(FontAwesome.Solid.QUESTION_CIRCLE, "Help"))
|
||||||
|
item(menuItem(FontAwesome.Solid.SIGN_OUT, "Sign out") { _ -> authContext.logout() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun menuItem(icon: FontAwesome.Solid, title: String): HorizontalLayout {
|
||||||
|
return HorizontalLayout().apply {
|
||||||
|
alignItems = FlexComponent.Alignment.CENTER
|
||||||
|
justifyContentMode = FlexComponent.JustifyContentMode.START
|
||||||
|
|
||||||
|
val faIcon = icon.create()
|
||||||
|
faIcon.setSize("var(--lumo-icon-size-s)")
|
||||||
|
add(faIcon)
|
||||||
|
|
||||||
|
text(title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun menuItem(
|
||||||
|
icon: FontAwesome.Solid,
|
||||||
|
title: String,
|
||||||
|
action: (ClickEvent<HorizontalLayout>) -> Unit
|
||||||
|
): HorizontalLayout {
|
||||||
|
return menuItem(icon, title).apply {
|
||||||
|
onLeftClick(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package de.grimsi.gameyfin.ui.resources
|
||||||
|
|
||||||
|
enum class PublicResources(val path: String) {
|
||||||
|
GAMEYFIN_LOGO_WHITE("public/images/Gameyfin_Logo_White.svg"),
|
||||||
|
GAMEYFIN_LOGO_WHITE_BORDER("public/images/Gameyfin_Logo_White_BORDER.svg")
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package de.grimsi.gameyfin.ui.views
|
||||||
|
|
||||||
|
import com.github.mvysny.karibudsl.v10.image
|
||||||
|
import com.github.mvysny.karibudsl.v10.loginForm
|
||||||
|
import com.vaadin.flow.component.login.LoginForm
|
||||||
|
import com.vaadin.flow.component.orderedlayout.FlexComponent
|
||||||
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout
|
||||||
|
import com.vaadin.flow.router.BeforeEnterEvent
|
||||||
|
import com.vaadin.flow.router.BeforeEnterObserver
|
||||||
|
import com.vaadin.flow.router.PageTitle
|
||||||
|
import com.vaadin.flow.router.Route
|
||||||
|
import com.vaadin.flow.server.auth.AnonymousAllowed
|
||||||
|
import de.grimsi.gameyfin.ui.resources.PublicResources
|
||||||
|
|
||||||
|
@Route("login")
|
||||||
|
@PageTitle("Login")
|
||||||
|
@AnonymousAllowed
|
||||||
|
class LoginView : VerticalLayout(), BeforeEnterObserver {
|
||||||
|
|
||||||
|
private var login: LoginForm
|
||||||
|
|
||||||
|
init {
|
||||||
|
setSizeFull()
|
||||||
|
justifyContentMode = FlexComponent.JustifyContentMode.CENTER
|
||||||
|
alignItems = FlexComponent.Alignment.CENTER
|
||||||
|
|
||||||
|
image {
|
||||||
|
height = "100px"
|
||||||
|
src = PublicResources.GAMEYFIN_LOGO_WHITE_BORDER.path
|
||||||
|
setAlt("Gameyfin")
|
||||||
|
}
|
||||||
|
|
||||||
|
login = loginForm {
|
||||||
|
addClassName("login-view")
|
||||||
|
action = "login"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun beforeEnter(event: BeforeEnterEvent?) {
|
||||||
|
if (event != null) {
|
||||||
|
if (event.location
|
||||||
|
.queryParameters
|
||||||
|
.parameters
|
||||||
|
.containsKey("error")) {
|
||||||
|
login.isError = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package de.grimsi.gameyfin.ui.views
|
||||||
|
|
||||||
|
import com.github.mvysny.karibudsl.v10.h1
|
||||||
|
import com.github.mvysny.karibudsl.v10.pre
|
||||||
|
import com.github.mvysny.karibudsl.v10.verticalLayout
|
||||||
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout
|
||||||
|
import com.vaadin.flow.router.Route
|
||||||
|
import de.grimsi.gameyfin.ui.layouts.MainLayout
|
||||||
|
import jakarta.annotation.security.PermitAll
|
||||||
|
|
||||||
|
|
||||||
|
@Route("", layout = MainLayout::class)
|
||||||
|
@PermitAll
|
||||||
|
class MainView : VerticalLayout() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
verticalLayout {
|
||||||
|
h1 { text = "Gameyfin main page" }
|
||||||
|
pre { text = "Work in progress" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 450 414">
|
||||||
|
<defs>
|
||||||
|
<style>.cls-1{fill:#fff;}</style>
|
||||||
|
</defs>
|
||||||
|
<title>Element 11</title>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polygon class="cls-1" points="234 60.48 234 85.23 256 54.32 234 60.48"/>
|
||||||
|
<polygon class="cls-1" points="450 0 324 35.28 253.125 118.125 450 63 450 0"/>
|
||||||
|
<polygon class="cls-1" points="234 348.48 306 328.32 306 184.32 450 144 450 90 234 150.48 234 348.48"/>
|
||||||
|
<polygon class="cls-1" points="72 177.84 192 144.24 216 110.52 216 65.52 0 126 0 414 72 312.84 72 177.84"/>
|
||||||
|
<polygon class="cls-1"
|
||||||
|
points="144 245.68 144 301.68 81 319.32 0 414 216 353.52 216 209.52 162 224.64 144 245.68"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 768 B |
@@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 451.94942 416.224">
|
||||||
|
<defs>
|
||||||
|
<style>.cls-1{fill:#fff;stroke:#000;stroke-miterlimit:10;}</style>
|
||||||
|
</defs>
|
||||||
|
<title>Element 10</title>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polygon class="cls-1" points="235.449 61.139 235.449 85.889 257.449 54.979 235.449 61.139"/>
|
||||||
|
<polygon class="cls-1" points="451.449 0.659 325.449 35.939 254.574 118.784 451.449 63.659 451.449 0.659"/>
|
||||||
|
<polygon class="cls-1"
|
||||||
|
points="235.449 349.139 307.449 328.979 307.449 184.979 451.449 144.659 451.449 90.659 235.449 151.139 235.449 349.139"/>
|
||||||
|
<polygon class="cls-1"
|
||||||
|
points="73.449 178.499 193.449 144.899 217.449 111.179 217.449 66.179 1.449 126.659 1.449 414.659 73.449 313.499 73.449 178.499"/>
|
||||||
|
<polygon class="cls-1"
|
||||||
|
points="145.449 246.339 145.449 302.339 82.449 319.979 1.449 414.659 217.449 354.179 217.449 210.179 163.449 225.299 145.449 246.339"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
vaadin.whitelisted-packages:
|
||||||
|
- com.vaadin
|
||||||
|
- org.vaadin
|
||||||
|
- dev.hilla
|
||||||
|
- com.flowingcode
|
||||||
Reference in New Issue
Block a user