Compare commits
87 Commits
symfony_up
...
v2026.2.3-
| Author | SHA1 | Date | |
|---|---|---|---|
| dc9c5f6545 | |||
| 25f2aaab8c | |||
| 0cc9cdaf07 | |||
| 247f437445 | |||
| 0e94367223 | |||
| a9ee28b395 | |||
| bd074c5c9d | |||
| 42c552c528 | |||
| 3b376e5386 | |||
| 45a8e6b4a1 | |||
| 1f8e9c3c56 | |||
| a511b86db8 | |||
| 1c0ad054bb | |||
| 5a8799bb7f | |||
| 6c443d8e86 | |||
| 8795fedda9 | |||
| 588fb57299 | |||
| eb345e17ca | |||
| c2693c4648 | |||
| 43efc16562 | |||
| 80d6440ece | |||
| 5ee972f003 | |||
| 6f3edb41ea | |||
| c52939a7a3 | |||
| 573d409606 | |||
| 9a58bc9a5e | |||
| 8780800dff | |||
| f442942faf | |||
| a61d881a4e | |||
| 926b614136 | |||
| c0c84f4651 | |||
| 176e255037 | |||
| b134358e9e | |||
| 3525aaeeb7 | |||
| af67ec3931 | |||
| d515f42cfd | |||
| 5d6aff8d90 | |||
| 15ba26ccf2 | |||
| d3fa0cbbf9 | |||
| ed776ca75b | |||
| 67133a4efa | |||
| 6044ee5c50 | |||
| 184925ab13 | |||
| 5a5467fda9 | |||
| f135746826 | |||
| 62915fe5e4 | |||
| 68a25aafa4 | |||
| 9fff5bd5d1 | |||
| 304663adb7 | |||
| cd593e99fc | |||
| 9d51654aec | |||
| 82465322f2 | |||
| 055e59d896 | |||
| 3db8a30115 | |||
| 2e8d878337 | |||
| 28221e092a | |||
| 0c0b8ae920 | |||
| 98f6e8cb6e | |||
| 4239177563 | |||
| e9c6795eb7 | |||
| fb8a54f687 | |||
| 0144a3953c | |||
| acbe9c7f63 | |||
| e2b227ed7a | |||
| c0dcc2896a | |||
| 92bfa5b301 | |||
| 826690769f | |||
| 6b3e19b063 | |||
| eff849549b | |||
| 63a0f442ed | |||
| 1cf21622b8 | |||
| 85a440017c | |||
| d388e25192 | |||
| 5b55a6ce73 | |||
| c660c13ea2 | |||
| d186a96f0d | |||
| b57442bec1 | |||
| 086d6c601e | |||
| 15806a6e04 | |||
| fe2de91e91 | |||
| 76143fca3e | |||
| 7219471a86 | |||
| b55c223d8a | |||
| 23547f4237 | |||
| 4cdca43ecc | |||
| fa0fc0743d | |||
| dd4b410624 |
13
.dockerignore
Normal file
13
.dockerignore
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
node_modules
|
||||||
|
var/cache
|
||||||
|
var/log
|
||||||
|
var/sessions
|
||||||
|
public/build
|
||||||
|
*.md
|
||||||
|
docker-compose*.yml
|
||||||
|
compose.override.yaml
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
884
.editorconfig
Normal file
884
.editorconfig
Normal file
@@ -0,0 +1,884 @@
|
|||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
max_line_length = 120
|
||||||
|
tab_width = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
ij_continuation_indent_size = 8
|
||||||
|
ij_formatter_off_tag = @formatter:off
|
||||||
|
ij_formatter_on_tag = @formatter:on
|
||||||
|
ij_formatter_tags_enabled = true
|
||||||
|
ij_smart_tabs = false
|
||||||
|
ij_visual_guides = 80,120
|
||||||
|
ij_wrap_on_typing = false
|
||||||
|
|
||||||
|
[*.blade.php]
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_blade_keep_indents_on_empty_lines = false
|
||||||
|
|
||||||
|
[*.css]
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_continuation_indent_size = 4
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_css_align_closing_brace_with_properties = false
|
||||||
|
ij_css_blank_lines_around_nested_selector = 1
|
||||||
|
ij_css_blank_lines_between_blocks = 1
|
||||||
|
ij_css_block_comment_add_space = false
|
||||||
|
ij_css_brace_placement = end_of_line
|
||||||
|
ij_css_enforce_quotes_on_format = false
|
||||||
|
ij_css_hex_color_long_format = false
|
||||||
|
ij_css_hex_color_lower_case = false
|
||||||
|
ij_css_hex_color_short_format = false
|
||||||
|
ij_css_hex_color_upper_case = false
|
||||||
|
ij_css_keep_blank_lines_in_code = 2
|
||||||
|
ij_css_keep_indents_on_empty_lines = false
|
||||||
|
ij_css_keep_single_line_blocks = false
|
||||||
|
ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
|
||||||
|
ij_css_space_after_colon = true
|
||||||
|
ij_css_space_before_opening_brace = true
|
||||||
|
ij_css_use_double_quotes = true
|
||||||
|
ij_css_value_alignment = do_not_align
|
||||||
|
|
||||||
|
[*.feature]
|
||||||
|
indent_size = 2
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_gherkin_keep_indents_on_empty_lines = false
|
||||||
|
|
||||||
|
[*.less]
|
||||||
|
indent_size = 2
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_less_align_closing_brace_with_properties = false
|
||||||
|
ij_less_blank_lines_around_nested_selector = 1
|
||||||
|
ij_less_blank_lines_between_blocks = 1
|
||||||
|
ij_less_block_comment_add_space = false
|
||||||
|
ij_less_brace_placement = 0
|
||||||
|
ij_less_enforce_quotes_on_format = false
|
||||||
|
ij_less_hex_color_long_format = false
|
||||||
|
ij_less_hex_color_lower_case = false
|
||||||
|
ij_less_hex_color_short_format = false
|
||||||
|
ij_less_hex_color_upper_case = false
|
||||||
|
ij_less_keep_blank_lines_in_code = 2
|
||||||
|
ij_less_keep_indents_on_empty_lines = false
|
||||||
|
ij_less_keep_single_line_blocks = false
|
||||||
|
ij_less_line_comment_add_space = false
|
||||||
|
ij_less_line_comment_at_first_column = false
|
||||||
|
ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
|
||||||
|
ij_less_space_after_colon = true
|
||||||
|
ij_less_space_before_opening_brace = true
|
||||||
|
ij_less_use_double_quotes = true
|
||||||
|
ij_less_value_alignment = 0
|
||||||
|
|
||||||
|
[*.neon]
|
||||||
|
ij_visual_guides =
|
||||||
|
|
||||||
|
[*.sass]
|
||||||
|
indent_size = 2
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_sass_align_closing_brace_with_properties = false
|
||||||
|
ij_sass_blank_lines_around_nested_selector = 1
|
||||||
|
ij_sass_blank_lines_between_blocks = 1
|
||||||
|
ij_sass_brace_placement = 0
|
||||||
|
ij_sass_enforce_quotes_on_format = false
|
||||||
|
ij_sass_hex_color_long_format = false
|
||||||
|
ij_sass_hex_color_lower_case = false
|
||||||
|
ij_sass_hex_color_short_format = false
|
||||||
|
ij_sass_hex_color_upper_case = false
|
||||||
|
ij_sass_keep_blank_lines_in_code = 2
|
||||||
|
ij_sass_keep_indents_on_empty_lines = false
|
||||||
|
ij_sass_keep_single_line_blocks = false
|
||||||
|
ij_sass_line_comment_add_space = false
|
||||||
|
ij_sass_line_comment_at_first_column = false
|
||||||
|
ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
|
||||||
|
ij_sass_space_after_colon = true
|
||||||
|
ij_sass_space_before_opening_brace = true
|
||||||
|
ij_sass_use_double_quotes = true
|
||||||
|
ij_sass_value_alignment = 0
|
||||||
|
|
||||||
|
[*.scss]
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_continuation_indent_size = 2
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_scss_align_closing_brace_with_properties = false
|
||||||
|
ij_scss_blank_lines_around_nested_selector = 1
|
||||||
|
ij_scss_blank_lines_between_blocks = 1
|
||||||
|
ij_scss_block_comment_add_space = false
|
||||||
|
ij_scss_brace_placement = 0
|
||||||
|
ij_scss_enforce_quotes_on_format = false
|
||||||
|
ij_scss_hex_color_long_format = false
|
||||||
|
ij_scss_hex_color_lower_case = false
|
||||||
|
ij_scss_hex_color_short_format = false
|
||||||
|
ij_scss_hex_color_upper_case = false
|
||||||
|
ij_scss_keep_blank_lines_in_code = 2
|
||||||
|
ij_scss_keep_indents_on_empty_lines = false
|
||||||
|
ij_scss_keep_single_line_blocks = false
|
||||||
|
ij_scss_line_comment_add_space = false
|
||||||
|
ij_scss_line_comment_at_first_column = false
|
||||||
|
ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
|
||||||
|
ij_scss_space_after_colon = true
|
||||||
|
ij_scss_space_before_opening_brace = true
|
||||||
|
ij_scss_use_double_quotes = true
|
||||||
|
ij_scss_value_alignment = 0
|
||||||
|
|
||||||
|
[*.twig]
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_continuation_indent_size = 2
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_twig_keep_indents_on_empty_lines = false
|
||||||
|
ij_twig_spaces_inside_comments_delimiters = true
|
||||||
|
ij_twig_spaces_inside_delimiters = true
|
||||||
|
ij_twig_spaces_inside_variable_delimiters = true
|
||||||
|
|
||||||
|
[*.vue]
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_continuation_indent_size = 4
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_vue_indent_children_of_top_level = template
|
||||||
|
ij_vue_interpolation_new_line_after_start_delimiter = true
|
||||||
|
ij_vue_interpolation_new_line_before_end_delimiter = true
|
||||||
|
ij_vue_interpolation_wrap = off
|
||||||
|
ij_vue_keep_indents_on_empty_lines = false
|
||||||
|
ij_vue_spaces_within_interpolation_expressions = true
|
||||||
|
|
||||||
|
[.editorconfig]
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_editorconfig_align_group_field_declarations = false
|
||||||
|
ij_editorconfig_space_after_colon = true
|
||||||
|
ij_editorconfig_space_after_comma = true
|
||||||
|
ij_editorconfig_space_before_colon = true
|
||||||
|
ij_editorconfig_space_before_comma = false
|
||||||
|
ij_editorconfig_spaces_around_assignment_operators = true
|
||||||
|
|
||||||
|
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xlf,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}]
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_xml_align_attributes = true
|
||||||
|
ij_xml_align_text = false
|
||||||
|
ij_xml_attribute_wrap = normal
|
||||||
|
ij_xml_block_comment_add_space = false
|
||||||
|
ij_xml_block_comment_at_first_column = true
|
||||||
|
ij_xml_keep_blank_lines = 2
|
||||||
|
ij_xml_keep_indents_on_empty_lines = false
|
||||||
|
ij_xml_keep_line_breaks = true
|
||||||
|
ij_xml_keep_line_breaks_in_text = true
|
||||||
|
ij_xml_keep_whitespaces = false
|
||||||
|
ij_xml_keep_whitespaces_around_cdata = preserve
|
||||||
|
ij_xml_keep_whitespaces_inside_cdata = false
|
||||||
|
ij_xml_line_comment_at_first_column = true
|
||||||
|
ij_xml_space_after_tag_name = false
|
||||||
|
ij_xml_space_around_equals_in_attribute = false
|
||||||
|
ij_xml_space_inside_empty_tag = true
|
||||||
|
ij_xml_text_wrap = normal
|
||||||
|
|
||||||
|
[{*.ats,*.cts,*.mts,*.ts,*.tsx}]
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_continuation_indent_size = 2
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_typescript_align_imports = false
|
||||||
|
ij_typescript_align_multiline_array_initializer_expression = false
|
||||||
|
ij_typescript_align_multiline_binary_operation = false
|
||||||
|
ij_typescript_align_multiline_chained_methods = false
|
||||||
|
ij_typescript_align_multiline_extends_list = false
|
||||||
|
ij_typescript_align_multiline_for = true
|
||||||
|
ij_typescript_align_multiline_parameters = true
|
||||||
|
ij_typescript_align_multiline_parameters_in_calls = false
|
||||||
|
ij_typescript_align_multiline_ternary_operation = false
|
||||||
|
ij_typescript_align_object_properties = 0
|
||||||
|
ij_typescript_align_union_types = false
|
||||||
|
ij_typescript_align_var_statements = 0
|
||||||
|
ij_typescript_array_initializer_new_line_after_left_brace = false
|
||||||
|
ij_typescript_array_initializer_right_brace_on_new_line = false
|
||||||
|
ij_typescript_array_initializer_wrap = off
|
||||||
|
ij_typescript_assignment_wrap = off
|
||||||
|
ij_typescript_binary_operation_sign_on_next_line = false
|
||||||
|
ij_typescript_binary_operation_wrap = off
|
||||||
|
ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
|
||||||
|
ij_typescript_blank_lines_after_imports = 1
|
||||||
|
ij_typescript_blank_lines_around_class = 1
|
||||||
|
ij_typescript_blank_lines_around_field = 0
|
||||||
|
ij_typescript_blank_lines_around_field_in_interface = 0
|
||||||
|
ij_typescript_blank_lines_around_function = 1
|
||||||
|
ij_typescript_blank_lines_around_method = 1
|
||||||
|
ij_typescript_blank_lines_around_method_in_interface = 1
|
||||||
|
ij_typescript_block_brace_style = end_of_line
|
||||||
|
ij_typescript_block_comment_add_space = false
|
||||||
|
ij_typescript_block_comment_at_first_column = true
|
||||||
|
ij_typescript_call_parameters_new_line_after_left_paren = false
|
||||||
|
ij_typescript_call_parameters_right_paren_on_new_line = false
|
||||||
|
ij_typescript_call_parameters_wrap = off
|
||||||
|
ij_typescript_catch_on_new_line = false
|
||||||
|
ij_typescript_chained_call_dot_on_new_line = true
|
||||||
|
ij_typescript_class_brace_style = end_of_line
|
||||||
|
ij_typescript_class_decorator_wrap = split_into_lines
|
||||||
|
ij_typescript_class_field_decorator_wrap = off
|
||||||
|
ij_typescript_class_method_decorator_wrap = off
|
||||||
|
ij_typescript_comma_on_new_line = false
|
||||||
|
ij_typescript_do_while_brace_force = never
|
||||||
|
ij_typescript_else_on_new_line = false
|
||||||
|
ij_typescript_enforce_trailing_comma = whenmultiline
|
||||||
|
ij_typescript_enum_constants_wrap = on_every_item
|
||||||
|
ij_typescript_extends_keyword_wrap = off
|
||||||
|
ij_typescript_extends_list_wrap = off
|
||||||
|
ij_typescript_field_prefix = _
|
||||||
|
ij_typescript_file_name_style = relaxed
|
||||||
|
ij_typescript_finally_on_new_line = false
|
||||||
|
ij_typescript_for_brace_force = never
|
||||||
|
ij_typescript_for_statement_new_line_after_left_paren = false
|
||||||
|
ij_typescript_for_statement_right_paren_on_new_line = false
|
||||||
|
ij_typescript_for_statement_wrap = off
|
||||||
|
ij_typescript_force_quote_style = true
|
||||||
|
ij_typescript_force_semicolon_style = true
|
||||||
|
ij_typescript_function_expression_brace_style = end_of_line
|
||||||
|
ij_typescript_function_parameter_decorator_wrap = off
|
||||||
|
ij_typescript_if_brace_force = never
|
||||||
|
ij_typescript_import_merge_members = global
|
||||||
|
ij_typescript_import_prefer_absolute_path = global
|
||||||
|
ij_typescript_import_sort_members = true
|
||||||
|
ij_typescript_import_sort_module_name = false
|
||||||
|
ij_typescript_import_use_node_resolution = true
|
||||||
|
ij_typescript_imports_wrap = on_every_item
|
||||||
|
ij_typescript_indent_case_from_switch = true
|
||||||
|
ij_typescript_indent_chained_calls = true
|
||||||
|
ij_typescript_indent_package_children = 0
|
||||||
|
ij_typescript_jsdoc_include_types = false
|
||||||
|
ij_typescript_jsx_attribute_value = braces
|
||||||
|
ij_typescript_keep_blank_lines_in_code = 2
|
||||||
|
ij_typescript_keep_first_column_comment = true
|
||||||
|
ij_typescript_keep_indents_on_empty_lines = false
|
||||||
|
ij_typescript_keep_line_breaks = true
|
||||||
|
ij_typescript_keep_simple_blocks_in_one_line = false
|
||||||
|
ij_typescript_keep_simple_methods_in_one_line = false
|
||||||
|
ij_typescript_jsx_self_closing_tag = true
|
||||||
|
ij_typescript_line_comment_add_space = true
|
||||||
|
ij_typescript_line_comment_at_first_column = false
|
||||||
|
ij_typescript_method_brace_style = end_of_line
|
||||||
|
ij_typescript_method_call_chain_wrap = off
|
||||||
|
ij_typescript_method_parameters_new_line_after_left_paren = false
|
||||||
|
ij_typescript_method_parameters_right_paren_on_new_line = false
|
||||||
|
ij_typescript_method_parameters_wrap = off
|
||||||
|
ij_typescript_object_literal_wrap = on_every_item
|
||||||
|
ij_typescript_object_types_wrap = on_every_item
|
||||||
|
ij_typescript_parentheses_expression_new_line_after_left_paren = false
|
||||||
|
ij_typescript_parentheses_expression_right_paren_on_new_line = false
|
||||||
|
ij_typescript_place_assignment_sign_on_next_line = false
|
||||||
|
ij_typescript_prefer_as_type_cast = false
|
||||||
|
ij_typescript_prefer_explicit_types_function_expression_returns = false
|
||||||
|
ij_typescript_prefer_explicit_types_function_returns = false
|
||||||
|
ij_typescript_prefer_explicit_types_vars_fields = false
|
||||||
|
ij_typescript_prefer_parameters_wrap = false
|
||||||
|
ij_typescript_property_prefix =
|
||||||
|
ij_typescript_reformat_c_style_comments = false
|
||||||
|
ij_typescript_space_after_colon = true
|
||||||
|
ij_typescript_space_after_comma = true
|
||||||
|
ij_typescript_space_after_dots_in_rest_parameter = false
|
||||||
|
ij_typescript_space_after_generator_mult = true
|
||||||
|
ij_typescript_space_after_property_colon = true
|
||||||
|
ij_typescript_space_after_quest = true
|
||||||
|
ij_typescript_space_after_type_colon = true
|
||||||
|
ij_typescript_space_after_unary_not = false
|
||||||
|
ij_typescript_space_before_async_arrow_lparen = true
|
||||||
|
ij_typescript_space_before_catch_keyword = true
|
||||||
|
ij_typescript_space_before_catch_left_brace = true
|
||||||
|
ij_typescript_space_before_catch_parentheses = true
|
||||||
|
ij_typescript_space_before_class_lbrace = true
|
||||||
|
ij_typescript_space_before_class_left_brace = true
|
||||||
|
ij_typescript_space_before_colon = true
|
||||||
|
ij_typescript_space_before_comma = false
|
||||||
|
ij_typescript_space_before_do_left_brace = true
|
||||||
|
ij_typescript_space_before_else_keyword = true
|
||||||
|
ij_typescript_space_before_else_left_brace = true
|
||||||
|
ij_typescript_space_before_finally_keyword = true
|
||||||
|
ij_typescript_space_before_finally_left_brace = true
|
||||||
|
ij_typescript_space_before_for_left_brace = true
|
||||||
|
ij_typescript_space_before_for_parentheses = true
|
||||||
|
ij_typescript_space_before_for_semicolon = false
|
||||||
|
ij_typescript_space_before_function_left_parenth = false
|
||||||
|
ij_typescript_space_before_generator_mult = false
|
||||||
|
ij_typescript_space_before_if_left_brace = true
|
||||||
|
ij_typescript_space_before_if_parentheses = true
|
||||||
|
ij_typescript_space_before_method_call_parentheses = false
|
||||||
|
ij_typescript_space_before_method_left_brace = true
|
||||||
|
ij_typescript_space_before_method_parentheses = false
|
||||||
|
ij_typescript_space_before_property_colon = false
|
||||||
|
ij_typescript_space_before_quest = true
|
||||||
|
ij_typescript_space_before_switch_left_brace = true
|
||||||
|
ij_typescript_space_before_switch_parentheses = true
|
||||||
|
ij_typescript_space_before_try_left_brace = true
|
||||||
|
ij_typescript_space_before_type_colon = false
|
||||||
|
ij_typescript_space_before_unary_not = false
|
||||||
|
ij_typescript_space_before_while_keyword = true
|
||||||
|
ij_typescript_space_before_while_left_brace = true
|
||||||
|
ij_typescript_space_before_while_parentheses = true
|
||||||
|
ij_typescript_spaces_around_additive_operators = true
|
||||||
|
ij_typescript_spaces_around_arrow_function_operator = true
|
||||||
|
ij_typescript_spaces_around_assignment_operators = true
|
||||||
|
ij_typescript_spaces_around_bitwise_operators = true
|
||||||
|
ij_typescript_spaces_around_equality_operators = true
|
||||||
|
ij_typescript_spaces_around_logical_operators = true
|
||||||
|
ij_typescript_spaces_around_multiplicative_operators = true
|
||||||
|
ij_typescript_spaces_around_relational_operators = true
|
||||||
|
ij_typescript_spaces_around_shift_operators = true
|
||||||
|
ij_typescript_spaces_around_unary_operator = false
|
||||||
|
ij_typescript_spaces_within_array_initializer_brackets = true
|
||||||
|
ij_typescript_spaces_within_brackets = false
|
||||||
|
ij_typescript_spaces_within_catch_parentheses = false
|
||||||
|
ij_typescript_spaces_within_for_parentheses = false
|
||||||
|
ij_typescript_spaces_within_if_parentheses = false
|
||||||
|
ij_typescript_spaces_within_imports = true
|
||||||
|
ij_typescript_spaces_within_interpolation_expressions = false
|
||||||
|
ij_typescript_spaces_within_method_call_parentheses = false
|
||||||
|
ij_typescript_spaces_within_method_parentheses = false
|
||||||
|
ij_typescript_spaces_within_object_literal_braces = true
|
||||||
|
ij_typescript_spaces_within_object_type_braces = true
|
||||||
|
ij_typescript_spaces_within_parentheses = false
|
||||||
|
ij_typescript_spaces_within_switch_parentheses = false
|
||||||
|
ij_typescript_spaces_within_type_assertion = false
|
||||||
|
ij_typescript_spaces_within_union_types = true
|
||||||
|
ij_typescript_spaces_within_while_parentheses = false
|
||||||
|
ij_typescript_special_else_if_treatment = true
|
||||||
|
ij_typescript_ternary_operation_signs_on_next_line = false
|
||||||
|
ij_typescript_ternary_operation_wrap = off
|
||||||
|
ij_typescript_union_types_wrap = on_every_item
|
||||||
|
ij_typescript_use_chained_calls_group_indents = false
|
||||||
|
ij_typescript_use_double_quotes = false
|
||||||
|
ij_typescript_use_explicit_js_extension = never
|
||||||
|
ij_typescript_use_import_type = auto
|
||||||
|
ij_typescript_use_path_mapping = always
|
||||||
|
ij_typescript_use_public_modifier = false
|
||||||
|
ij_typescript_use_semicolon_after_statement = true
|
||||||
|
ij_typescript_var_declaration_wrap = normal
|
||||||
|
ij_typescript_while_brace_force = never
|
||||||
|
ij_typescript_while_on_new_line = false
|
||||||
|
ij_typescript_wrap_comments = false
|
||||||
|
|
||||||
|
[{*.bash,*.sh,*.zsh}]
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_shell_binary_ops_start_line = false
|
||||||
|
ij_shell_keep_column_alignment_padding = false
|
||||||
|
ij_shell_minify_program = false
|
||||||
|
ij_shell_redirect_followed_by_space = false
|
||||||
|
ij_shell_switch_cases_indented = false
|
||||||
|
ij_shell_use_unix_line_separator = true
|
||||||
|
|
||||||
|
[{*.cjs,*.es6,*.js,*.mjs,*.jsx}]
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_continuation_indent_size = 2
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_javascript_align_imports = false
|
||||||
|
ij_javascript_align_multiline_array_initializer_expression = false
|
||||||
|
ij_javascript_align_multiline_binary_operation = false
|
||||||
|
ij_javascript_align_multiline_chained_methods = false
|
||||||
|
ij_javascript_align_multiline_extends_list = false
|
||||||
|
ij_javascript_align_multiline_for = true
|
||||||
|
ij_javascript_align_multiline_parameters = true
|
||||||
|
ij_javascript_align_multiline_parameters_in_calls = false
|
||||||
|
ij_javascript_align_multiline_ternary_operation = false
|
||||||
|
ij_javascript_align_object_properties = 0
|
||||||
|
ij_javascript_align_union_types = false
|
||||||
|
ij_javascript_align_var_statements = 0
|
||||||
|
ij_javascript_array_initializer_new_line_after_left_brace = false
|
||||||
|
ij_javascript_array_initializer_right_brace_on_new_line = false
|
||||||
|
ij_javascript_array_initializer_wrap = off
|
||||||
|
ij_javascript_assignment_wrap = off
|
||||||
|
ij_javascript_binary_operation_sign_on_next_line = false
|
||||||
|
ij_javascript_binary_operation_wrap = off
|
||||||
|
ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
|
||||||
|
ij_javascript_blank_lines_after_imports = 1
|
||||||
|
ij_javascript_blank_lines_around_class = 1
|
||||||
|
ij_javascript_blank_lines_around_field = 0
|
||||||
|
ij_javascript_blank_lines_around_function = 1
|
||||||
|
ij_javascript_blank_lines_around_method = 1
|
||||||
|
ij_javascript_block_brace_style = end_of_line
|
||||||
|
ij_javascript_block_comment_add_space = false
|
||||||
|
ij_javascript_block_comment_at_first_column = true
|
||||||
|
ij_javascript_call_parameters_new_line_after_left_paren = false
|
||||||
|
ij_javascript_call_parameters_right_paren_on_new_line = false
|
||||||
|
ij_javascript_call_parameters_wrap = off
|
||||||
|
ij_javascript_catch_on_new_line = false
|
||||||
|
ij_javascript_chained_call_dot_on_new_line = true
|
||||||
|
ij_javascript_class_brace_style = end_of_line
|
||||||
|
ij_javascript_class_decorator_wrap = split_into_lines
|
||||||
|
ij_javascript_class_field_decorator_wrap = off
|
||||||
|
ij_javascript_class_method_decorator_wrap = off
|
||||||
|
ij_javascript_comma_on_new_line = false
|
||||||
|
ij_javascript_do_while_brace_force = never
|
||||||
|
ij_javascript_else_on_new_line = false
|
||||||
|
ij_javascript_enforce_trailing_comma = whenmultiline
|
||||||
|
ij_javascript_extends_keyword_wrap = off
|
||||||
|
ij_javascript_extends_list_wrap = off
|
||||||
|
ij_javascript_field_prefix = _
|
||||||
|
ij_javascript_file_name_style = relaxed
|
||||||
|
ij_javascript_finally_on_new_line = false
|
||||||
|
ij_javascript_for_brace_force = never
|
||||||
|
ij_javascript_for_statement_new_line_after_left_paren = false
|
||||||
|
ij_javascript_for_statement_right_paren_on_new_line = false
|
||||||
|
ij_javascript_for_statement_wrap = off
|
||||||
|
ij_javascript_force_quote_style = true
|
||||||
|
ij_javascript_force_semicolon_style = true
|
||||||
|
ij_javascript_function_expression_brace_style = end_of_line
|
||||||
|
ij_javascript_function_parameter_decorator_wrap = off
|
||||||
|
ij_javascript_if_brace_force = never
|
||||||
|
ij_javascript_import_merge_members = global
|
||||||
|
ij_javascript_import_prefer_absolute_path = global
|
||||||
|
ij_javascript_import_sort_members = true
|
||||||
|
ij_javascript_import_sort_module_name = false
|
||||||
|
ij_javascript_import_use_node_resolution = true
|
||||||
|
ij_javascript_imports_wrap = on_every_item
|
||||||
|
ij_javascript_indent_case_from_switch = true
|
||||||
|
ij_javascript_indent_chained_calls = true
|
||||||
|
ij_javascript_indent_package_children = 0
|
||||||
|
ij_javascript_jsx_attribute_value = braces
|
||||||
|
ij_javascript_keep_blank_lines_in_code = 2
|
||||||
|
ij_javascript_keep_first_column_comment = true
|
||||||
|
ij_javascript_keep_indents_on_empty_lines = false
|
||||||
|
ij_javascript_keep_line_breaks = true
|
||||||
|
ij_javascript_keep_simple_blocks_in_one_line = false
|
||||||
|
ij_javascript_keep_simple_methods_in_one_line = false
|
||||||
|
ij_javascript_jsx_self_closing_tag = true
|
||||||
|
ij_javascript_line_comment_add_space = true
|
||||||
|
ij_javascript_line_comment_at_first_column = false
|
||||||
|
ij_javascript_method_brace_style = end_of_line
|
||||||
|
ij_javascript_method_call_chain_wrap = off
|
||||||
|
ij_javascript_method_parameters_new_line_after_left_paren = false
|
||||||
|
ij_javascript_method_parameters_right_paren_on_new_line = false
|
||||||
|
ij_javascript_method_parameters_wrap = off
|
||||||
|
ij_javascript_object_literal_wrap = on_every_item
|
||||||
|
ij_javascript_object_types_wrap = on_every_item
|
||||||
|
ij_javascript_parentheses_expression_new_line_after_left_paren = false
|
||||||
|
ij_javascript_parentheses_expression_right_paren_on_new_line = false
|
||||||
|
ij_javascript_place_assignment_sign_on_next_line = false
|
||||||
|
ij_javascript_prefer_as_type_cast = false
|
||||||
|
ij_javascript_prefer_explicit_types_function_expression_returns = false
|
||||||
|
ij_javascript_prefer_explicit_types_function_returns = false
|
||||||
|
ij_javascript_prefer_explicit_types_vars_fields = false
|
||||||
|
ij_javascript_prefer_parameters_wrap = false
|
||||||
|
ij_javascript_property_prefix =
|
||||||
|
ij_javascript_reformat_c_style_comments = true
|
||||||
|
ij_javascript_space_after_colon = true
|
||||||
|
ij_javascript_space_after_comma = true
|
||||||
|
ij_javascript_space_after_dots_in_rest_parameter = false
|
||||||
|
ij_javascript_space_after_generator_mult = true
|
||||||
|
ij_javascript_space_after_property_colon = true
|
||||||
|
ij_javascript_space_after_quest = true
|
||||||
|
ij_javascript_space_after_type_colon = true
|
||||||
|
ij_javascript_space_after_unary_not = false
|
||||||
|
ij_javascript_space_before_async_arrow_lparen = true
|
||||||
|
ij_javascript_space_before_catch_keyword = true
|
||||||
|
ij_javascript_space_before_catch_left_brace = true
|
||||||
|
ij_javascript_space_before_catch_parentheses = true
|
||||||
|
ij_javascript_space_before_class_lbrace = true
|
||||||
|
ij_javascript_space_before_class_left_brace = true
|
||||||
|
ij_javascript_space_before_colon = true
|
||||||
|
ij_javascript_space_before_comma = false
|
||||||
|
ij_javascript_space_before_do_left_brace = true
|
||||||
|
ij_javascript_space_before_else_keyword = true
|
||||||
|
ij_javascript_space_before_else_left_brace = true
|
||||||
|
ij_javascript_space_before_finally_keyword = true
|
||||||
|
ij_javascript_space_before_finally_left_brace = true
|
||||||
|
ij_javascript_space_before_for_left_brace = true
|
||||||
|
ij_javascript_space_before_for_parentheses = true
|
||||||
|
ij_javascript_space_before_for_semicolon = false
|
||||||
|
ij_javascript_space_before_function_left_parenth = false
|
||||||
|
ij_javascript_space_before_generator_mult = false
|
||||||
|
ij_javascript_space_before_if_left_brace = true
|
||||||
|
ij_javascript_space_before_if_parentheses = true
|
||||||
|
ij_javascript_space_before_method_call_parentheses = false
|
||||||
|
ij_javascript_space_before_method_left_brace = true
|
||||||
|
ij_javascript_space_before_method_parentheses = false
|
||||||
|
ij_javascript_space_before_property_colon = false
|
||||||
|
ij_javascript_space_before_quest = true
|
||||||
|
ij_javascript_space_before_switch_left_brace = true
|
||||||
|
ij_javascript_space_before_switch_parentheses = true
|
||||||
|
ij_javascript_space_before_try_left_brace = true
|
||||||
|
ij_javascript_space_before_type_colon = false
|
||||||
|
ij_javascript_space_before_unary_not = false
|
||||||
|
ij_javascript_space_before_while_keyword = true
|
||||||
|
ij_javascript_space_before_while_left_brace = true
|
||||||
|
ij_javascript_space_before_while_parentheses = true
|
||||||
|
ij_javascript_spaces_around_additive_operators = true
|
||||||
|
ij_javascript_spaces_around_arrow_function_operator = true
|
||||||
|
ij_javascript_spaces_around_assignment_operators = true
|
||||||
|
ij_javascript_spaces_around_bitwise_operators = true
|
||||||
|
ij_javascript_spaces_around_equality_operators = true
|
||||||
|
ij_javascript_spaces_around_logical_operators = true
|
||||||
|
ij_javascript_spaces_around_multiplicative_operators = true
|
||||||
|
ij_javascript_spaces_around_relational_operators = true
|
||||||
|
ij_javascript_spaces_around_shift_operators = true
|
||||||
|
ij_javascript_spaces_around_unary_operator = false
|
||||||
|
ij_javascript_spaces_within_array_initializer_brackets = false
|
||||||
|
ij_javascript_spaces_within_brackets = false
|
||||||
|
ij_javascript_spaces_within_catch_parentheses = false
|
||||||
|
ij_javascript_spaces_within_for_parentheses = false
|
||||||
|
ij_javascript_spaces_within_if_parentheses = false
|
||||||
|
ij_javascript_spaces_within_imports = true
|
||||||
|
ij_javascript_spaces_within_interpolation_expressions = false
|
||||||
|
ij_javascript_spaces_within_method_call_parentheses = false
|
||||||
|
ij_javascript_spaces_within_method_parentheses = false
|
||||||
|
ij_javascript_spaces_within_object_literal_braces = true
|
||||||
|
ij_javascript_spaces_within_object_type_braces = true
|
||||||
|
ij_javascript_spaces_within_parentheses = false
|
||||||
|
ij_javascript_spaces_within_switch_parentheses = false
|
||||||
|
ij_javascript_spaces_within_type_assertion = false
|
||||||
|
ij_javascript_spaces_within_union_types = true
|
||||||
|
ij_javascript_spaces_within_while_parentheses = false
|
||||||
|
ij_javascript_special_else_if_treatment = true
|
||||||
|
ij_javascript_ternary_operation_signs_on_next_line = false
|
||||||
|
ij_javascript_ternary_operation_wrap = off
|
||||||
|
ij_javascript_union_types_wrap = on_every_item
|
||||||
|
ij_javascript_use_chained_calls_group_indents = false
|
||||||
|
ij_javascript_use_double_quotes = false
|
||||||
|
ij_javascript_use_explicit_js_extension = never
|
||||||
|
ij_javascript_use_import_type = auto
|
||||||
|
ij_javascript_use_path_mapping = always
|
||||||
|
ij_javascript_use_public_modifier = false
|
||||||
|
ij_javascript_use_semicolon_after_statement = true
|
||||||
|
ij_javascript_var_declaration_wrap = normal
|
||||||
|
ij_javascript_while_brace_force = never
|
||||||
|
ij_javascript_while_on_new_line = false
|
||||||
|
ij_javascript_wrap_comments = true
|
||||||
|
|
||||||
|
[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}]
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
|
ij_continuation_indent_size = 4
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_php_align_assignments = false
|
||||||
|
ij_php_align_class_constants = true
|
||||||
|
ij_php_align_enum_cases = false
|
||||||
|
ij_php_align_group_field_declarations = false
|
||||||
|
ij_php_align_inline_comments = false
|
||||||
|
ij_php_align_key_value_pairs = true
|
||||||
|
ij_php_align_match_arm_bodies = true
|
||||||
|
ij_php_align_multiline_array_initializer_expression = false
|
||||||
|
ij_php_align_multiline_binary_operation = false
|
||||||
|
ij_php_align_multiline_chained_methods = true
|
||||||
|
ij_php_align_multiline_extends_list = false
|
||||||
|
ij_php_align_multiline_for = true
|
||||||
|
ij_php_align_multiline_parameters = true
|
||||||
|
ij_php_align_multiline_parameters_in_calls = false
|
||||||
|
ij_php_align_multiline_ternary_operation = false
|
||||||
|
ij_php_align_named_arguments = false
|
||||||
|
ij_php_align_phpdoc_comments = true
|
||||||
|
ij_php_align_phpdoc_param_names = true
|
||||||
|
ij_php_anonymous_brace_style = end_of_line
|
||||||
|
ij_php_api_weight = 28
|
||||||
|
ij_php_array_initializer_new_line_after_left_brace = false
|
||||||
|
ij_php_array_initializer_right_brace_on_new_line = true
|
||||||
|
ij_php_array_initializer_wrap = off
|
||||||
|
ij_php_assignment_wrap = off
|
||||||
|
ij_php_attributes_wrap = off
|
||||||
|
ij_php_author_weight = 1
|
||||||
|
ij_php_binary_operation_sign_on_next_line = false
|
||||||
|
ij_php_binary_operation_wrap = off
|
||||||
|
ij_php_blank_lines_after_class_header = 0
|
||||||
|
ij_php_blank_lines_after_function = 1
|
||||||
|
ij_php_blank_lines_after_imports = 1
|
||||||
|
ij_php_blank_lines_after_opening_tag = 0
|
||||||
|
ij_php_blank_lines_after_package = 0
|
||||||
|
ij_php_blank_lines_around_class = 1
|
||||||
|
ij_php_blank_lines_around_constants = 0
|
||||||
|
ij_php_blank_lines_around_enum_cases = 0
|
||||||
|
ij_php_blank_lines_around_field = 1
|
||||||
|
ij_php_blank_lines_around_method = 1
|
||||||
|
ij_php_blank_lines_before_class_end = 0
|
||||||
|
ij_php_blank_lines_before_imports = 1
|
||||||
|
ij_php_blank_lines_before_method_body = 0
|
||||||
|
ij_php_blank_lines_before_package = 1
|
||||||
|
ij_php_blank_lines_before_return_statement = 0
|
||||||
|
ij_php_blank_lines_between_imports = 0
|
||||||
|
ij_php_block_brace_style = end_of_line
|
||||||
|
ij_php_call_parameters_new_line_after_left_paren = false
|
||||||
|
ij_php_call_parameters_right_paren_on_new_line = false
|
||||||
|
ij_php_call_parameters_wrap = off
|
||||||
|
ij_php_catch_on_new_line = false
|
||||||
|
ij_php_category_weight = 28
|
||||||
|
ij_php_class_brace_style = next_line
|
||||||
|
ij_php_comma_after_last_argument = false
|
||||||
|
ij_php_comma_after_last_argument_style = when_multiline
|
||||||
|
ij_php_comma_after_last_array_element = true
|
||||||
|
ij_php_comma_after_last_closure_use_var = false
|
||||||
|
ij_php_comma_after_last_closure_use_var_style = when_multiline
|
||||||
|
ij_php_comma_after_last_match_arm = false
|
||||||
|
ij_php_comma_after_last_parameter = false
|
||||||
|
ij_php_comma_after_last_parameter_style = when_multiline
|
||||||
|
ij_php_concat_spaces = true
|
||||||
|
ij_php_copyright_weight = 28
|
||||||
|
ij_php_deprecated_weight = 28
|
||||||
|
ij_php_do_while_brace_force = never
|
||||||
|
ij_php_else_if_style = as_is
|
||||||
|
ij_php_else_on_new_line = false
|
||||||
|
ij_php_example_weight = 28
|
||||||
|
ij_php_extends_keyword_wrap = off
|
||||||
|
ij_php_extends_list_wrap = off
|
||||||
|
ij_php_fields_default_visibility = private
|
||||||
|
ij_php_filesource_weight = 28
|
||||||
|
ij_php_finally_on_new_line = false
|
||||||
|
ij_php_for_brace_force = never
|
||||||
|
ij_php_for_statement_new_line_after_left_paren = false
|
||||||
|
ij_php_for_statement_right_paren_on_new_line = false
|
||||||
|
ij_php_for_statement_wrap = off
|
||||||
|
ij_php_force_empty_classes_in_one_line = false
|
||||||
|
ij_php_force_empty_methods_in_one_line = false
|
||||||
|
ij_php_force_short_declaration_array_style = false
|
||||||
|
ij_php_getters_setters_naming_style = camel_case
|
||||||
|
ij_php_getters_setters_order_style = getters_first
|
||||||
|
ij_php_global_weight = 28
|
||||||
|
ij_php_group_use_wrap = on_every_item
|
||||||
|
ij_php_heredoc_on_same_line = false
|
||||||
|
ij_php_if_brace_force = never
|
||||||
|
ij_php_if_lparen_on_next_line = false
|
||||||
|
ij_php_if_rparen_on_next_line = false
|
||||||
|
ij_php_ignore_weight = 28
|
||||||
|
ij_php_import_sorting = alphabetic
|
||||||
|
ij_php_indent_break_from_case = true
|
||||||
|
ij_php_indent_case_from_switch = true
|
||||||
|
ij_php_indent_code_in_php_tags = false
|
||||||
|
ij_php_internal_weight = 28
|
||||||
|
ij_php_keep_blank_lines_after_lbrace = 2
|
||||||
|
ij_php_keep_blank_lines_before_right_brace = 2
|
||||||
|
ij_php_keep_blank_lines_in_code = 2
|
||||||
|
ij_php_keep_blank_lines_in_declarations = 2
|
||||||
|
ij_php_keep_control_statement_in_one_line = true
|
||||||
|
ij_php_keep_first_column_comment = false
|
||||||
|
ij_php_keep_indents_on_empty_lines = false
|
||||||
|
ij_php_keep_line_breaks = true
|
||||||
|
ij_php_keep_rparen_and_lbrace_on_one_line = true
|
||||||
|
ij_php_keep_simple_classes_in_one_line = true
|
||||||
|
ij_php_keep_simple_methods_in_one_line = true
|
||||||
|
ij_php_lambda_brace_style = end_of_line
|
||||||
|
ij_php_license_weight = 28
|
||||||
|
ij_php_line_comment_add_space = false
|
||||||
|
ij_php_line_comment_at_first_column = true
|
||||||
|
ij_php_link_weight = 28
|
||||||
|
ij_php_lower_case_boolean_const = true
|
||||||
|
ij_php_lower_case_keywords = true
|
||||||
|
ij_php_lower_case_null_const = true
|
||||||
|
ij_php_method_brace_style = next_line
|
||||||
|
ij_php_method_call_chain_wrap = normal
|
||||||
|
ij_php_method_parameters_new_line_after_left_paren = true
|
||||||
|
ij_php_method_parameters_right_paren_on_new_line = true
|
||||||
|
ij_php_method_parameters_wrap = on_every_item
|
||||||
|
ij_php_method_weight = 5
|
||||||
|
ij_php_modifier_list_wrap = false
|
||||||
|
ij_php_multiline_chained_calls_first_call_on_new_line = false
|
||||||
|
ij_php_multiline_chained_calls_semicolon_on_new_line = false
|
||||||
|
ij_php_multiline_closure_lambda_on_new_line = false
|
||||||
|
ij_php_namespace_brace_style = 2
|
||||||
|
ij_php_new_line_after_php_opening_tag = false
|
||||||
|
ij_php_null_type_position = in_the_end
|
||||||
|
ij_php_package_weight = 0
|
||||||
|
ij_php_param_weight = 2
|
||||||
|
ij_php_parameters_attributes_wrap = off
|
||||||
|
ij_php_parentheses_expression_new_line_after_left_paren = false
|
||||||
|
ij_php_parentheses_expression_right_paren_on_new_line = false
|
||||||
|
ij_php_phpdoc_blank_line_before_tags = true
|
||||||
|
ij_php_phpdoc_blank_lines_around_parameters = true
|
||||||
|
ij_php_phpdoc_keep_blank_lines = true
|
||||||
|
ij_php_phpdoc_param_spaces_between_name_and_description = 1
|
||||||
|
ij_php_phpdoc_param_spaces_between_tag_and_type = 1
|
||||||
|
ij_php_phpdoc_param_spaces_between_type_and_name = 1
|
||||||
|
ij_php_phpdoc_use_fqcn = false
|
||||||
|
ij_php_phpdoc_wrap_long_lines = true
|
||||||
|
ij_php_place_assignment_sign_on_next_line = false
|
||||||
|
ij_php_place_parens_for_constructor = 0
|
||||||
|
ij_php_property_read_weight = 28
|
||||||
|
ij_php_property_weight = 28
|
||||||
|
ij_php_property_write_weight = 28
|
||||||
|
ij_php_return_type_on_new_line = false
|
||||||
|
ij_php_return_weight = 3
|
||||||
|
ij_php_see_weight = 28
|
||||||
|
ij_php_since_weight = 28
|
||||||
|
ij_php_sort_phpdoc_elements = true
|
||||||
|
ij_php_space_after_colon = true
|
||||||
|
ij_php_space_after_colon_in_enum_backed_type = true
|
||||||
|
ij_php_space_after_colon_in_named_argument = true
|
||||||
|
ij_php_space_after_colon_in_return_type = true
|
||||||
|
ij_php_space_after_comma = true
|
||||||
|
ij_php_space_after_for_semicolon = true
|
||||||
|
ij_php_space_after_quest = true
|
||||||
|
ij_php_space_after_type_cast = false
|
||||||
|
ij_php_space_after_unary_not = false
|
||||||
|
ij_php_space_before_array_initializer_left_brace = false
|
||||||
|
ij_php_space_before_catch_keyword = true
|
||||||
|
ij_php_space_before_catch_left_brace = true
|
||||||
|
ij_php_space_before_catch_parentheses = true
|
||||||
|
ij_php_space_before_class_left_brace = true
|
||||||
|
ij_php_space_before_closure_left_parenthesis = true
|
||||||
|
ij_php_space_before_colon = true
|
||||||
|
ij_php_space_before_colon_in_enum_backed_type = false
|
||||||
|
ij_php_space_before_colon_in_named_argument = false
|
||||||
|
ij_php_space_before_colon_in_return_type = false
|
||||||
|
ij_php_space_before_comma = false
|
||||||
|
ij_php_space_before_do_left_brace = true
|
||||||
|
ij_php_space_before_else_keyword = true
|
||||||
|
ij_php_space_before_else_left_brace = true
|
||||||
|
ij_php_space_before_finally_keyword = true
|
||||||
|
ij_php_space_before_finally_left_brace = true
|
||||||
|
ij_php_space_before_for_left_brace = true
|
||||||
|
ij_php_space_before_for_parentheses = true
|
||||||
|
ij_php_space_before_for_semicolon = false
|
||||||
|
ij_php_space_before_if_left_brace = true
|
||||||
|
ij_php_space_before_if_parentheses = true
|
||||||
|
ij_php_space_before_method_call_parentheses = false
|
||||||
|
ij_php_space_before_method_left_brace = true
|
||||||
|
ij_php_space_before_method_parentheses = false
|
||||||
|
ij_php_space_before_quest = true
|
||||||
|
ij_php_space_before_short_closure_left_parenthesis = false
|
||||||
|
ij_php_space_before_switch_left_brace = true
|
||||||
|
ij_php_space_before_switch_parentheses = true
|
||||||
|
ij_php_space_before_try_left_brace = true
|
||||||
|
ij_php_space_before_unary_not = false
|
||||||
|
ij_php_space_before_while_keyword = true
|
||||||
|
ij_php_space_before_while_left_brace = true
|
||||||
|
ij_php_space_before_while_parentheses = true
|
||||||
|
ij_php_space_between_ternary_quest_and_colon = false
|
||||||
|
ij_php_spaces_around_additive_operators = true
|
||||||
|
ij_php_spaces_around_arrow = false
|
||||||
|
ij_php_spaces_around_assignment_in_declare = false
|
||||||
|
ij_php_spaces_around_assignment_operators = true
|
||||||
|
ij_php_spaces_around_bitwise_operators = true
|
||||||
|
ij_php_spaces_around_equality_operators = true
|
||||||
|
ij_php_spaces_around_logical_operators = true
|
||||||
|
ij_php_spaces_around_multiplicative_operators = true
|
||||||
|
ij_php_spaces_around_null_coalesce_operator = true
|
||||||
|
ij_php_spaces_around_pipe_in_union_type = false
|
||||||
|
ij_php_spaces_around_relational_operators = true
|
||||||
|
ij_php_spaces_around_shift_operators = true
|
||||||
|
ij_php_spaces_around_unary_operator = false
|
||||||
|
ij_php_spaces_around_var_within_brackets = false
|
||||||
|
ij_php_spaces_within_array_initializer_braces = false
|
||||||
|
ij_php_spaces_within_brackets = false
|
||||||
|
ij_php_spaces_within_catch_parentheses = false
|
||||||
|
ij_php_spaces_within_for_parentheses = false
|
||||||
|
ij_php_spaces_within_if_parentheses = false
|
||||||
|
ij_php_spaces_within_method_call_parentheses = false
|
||||||
|
ij_php_spaces_within_method_parentheses = false
|
||||||
|
ij_php_spaces_within_parentheses = false
|
||||||
|
ij_php_spaces_within_short_echo_tags = true
|
||||||
|
ij_php_spaces_within_switch_parentheses = false
|
||||||
|
ij_php_spaces_within_while_parentheses = false
|
||||||
|
ij_php_special_else_if_treatment = false
|
||||||
|
ij_php_subpackage_weight = 28
|
||||||
|
ij_php_ternary_operation_signs_on_next_line = true
|
||||||
|
ij_php_ternary_operation_wrap = off
|
||||||
|
ij_php_throws_weight = 4
|
||||||
|
ij_php_todo_weight = 28
|
||||||
|
ij_php_treat_multiline_arrays_and_lambdas_multiline = false
|
||||||
|
ij_php_unknown_tag_weight = 28
|
||||||
|
ij_php_upper_case_boolean_const = false
|
||||||
|
ij_php_upper_case_null_const = false
|
||||||
|
ij_php_uses_weight = 28
|
||||||
|
ij_php_var_weight = 28
|
||||||
|
ij_php_variable_naming_style = mixed
|
||||||
|
ij_php_version_weight = 28
|
||||||
|
ij_php_while_brace_force = never
|
||||||
|
ij_php_while_on_new_line = false
|
||||||
|
|
||||||
|
[{*.har,*.jsb2,*.jsb3,*.json,*.jsonc,*.postman_collection,*.postman_collection.json,*.postman_environment,*.postman_environment.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,.ws-context,composer.lock,jest.config}]
|
||||||
|
indent_size = 2
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_json_array_wrapping = split_into_lines
|
||||||
|
ij_json_keep_blank_lines_in_code = 0
|
||||||
|
ij_json_keep_indents_on_empty_lines = false
|
||||||
|
ij_json_keep_line_breaks = true
|
||||||
|
ij_json_keep_trailing_comma = false
|
||||||
|
ij_json_object_wrapping = split_into_lines
|
||||||
|
ij_json_property_alignment = do_not_align
|
||||||
|
ij_json_space_after_colon = true
|
||||||
|
ij_json_space_after_comma = true
|
||||||
|
ij_json_space_before_colon = false
|
||||||
|
ij_json_space_before_comma = false
|
||||||
|
ij_json_spaces_within_braces = false
|
||||||
|
ij_json_spaces_within_brackets = false
|
||||||
|
ij_json_wrap_long_lines = false
|
||||||
|
|
||||||
|
[{*.htm,*.html,*.sht,*.shtm,*.shtml}]
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_continuation_indent_size = 2
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
|
||||||
|
ij_html_align_attributes = true
|
||||||
|
ij_html_align_text = false
|
||||||
|
ij_html_attribute_wrap = on_every_item
|
||||||
|
ij_html_block_comment_add_space = false
|
||||||
|
ij_html_block_comment_at_first_column = true
|
||||||
|
ij_html_do_not_align_children_of_min_lines = 0
|
||||||
|
ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6
|
||||||
|
ij_html_do_not_indent_children_of_tags = thead,tbody,tfoot
|
||||||
|
ij_html_enforce_quotes = true
|
||||||
|
ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,strike,strong,sub,sup,textarea,tt,u,var
|
||||||
|
ij_html_keep_blank_lines = 2
|
||||||
|
ij_html_keep_indents_on_empty_lines = false
|
||||||
|
ij_html_keep_line_breaks = true
|
||||||
|
ij_html_keep_line_breaks_in_text = true
|
||||||
|
ij_html_keep_whitespaces = false
|
||||||
|
ij_html_keep_whitespaces_inside = span,pre,textarea
|
||||||
|
ij_html_line_comment_at_first_column = true
|
||||||
|
ij_html_new_line_after_last_attribute = when_multiline
|
||||||
|
ij_html_new_line_before_first_attribute = when_multiline
|
||||||
|
ij_html_quote_style = double
|
||||||
|
ij_html_remove_new_line_before_tags = br
|
||||||
|
ij_html_space_after_tag_name = false
|
||||||
|
ij_html_space_around_equality_in_attribute = false
|
||||||
|
ij_html_space_inside_empty_tag = true
|
||||||
|
ij_html_self_closing_tag = true
|
||||||
|
ij_html_text_wrap = normal
|
||||||
|
|
||||||
|
[{*.http,*.rest}]
|
||||||
|
ij_continuation_indent_size = 4
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_http-request_call_parameters_wrap = normal
|
||||||
|
ij_http-request_method_parameters_wrap = split_into_lines
|
||||||
|
ij_http-request_space_before_comma = true
|
||||||
|
ij_http-request_spaces_around_assignment_operators = true
|
||||||
|
|
||||||
|
[{*.markdown,*.md}]
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_markdown_force_one_space_after_blockquote_symbol = true
|
||||||
|
ij_markdown_force_one_space_after_header_symbol = true
|
||||||
|
ij_markdown_force_one_space_after_list_bullet = true
|
||||||
|
ij_markdown_force_one_space_between_words = true
|
||||||
|
ij_markdown_format_tables = true
|
||||||
|
ij_markdown_insert_quote_arrows_on_wrap = true
|
||||||
|
ij_markdown_keep_indents_on_empty_lines = false
|
||||||
|
ij_markdown_keep_line_breaks_inside_text_blocks = true
|
||||||
|
ij_markdown_max_lines_around_block_elements = 1
|
||||||
|
ij_markdown_max_lines_around_header = 1
|
||||||
|
ij_markdown_max_lines_between_paragraphs = 1
|
||||||
|
ij_markdown_min_lines_around_block_elements = 1
|
||||||
|
ij_markdown_min_lines_around_header = 1
|
||||||
|
ij_markdown_min_lines_between_paragraphs = 1
|
||||||
|
ij_markdown_wrap_text_if_long = true
|
||||||
|
ij_markdown_wrap_text_inside_blockquotes = true
|
||||||
|
|
||||||
|
[{*.mk,GNUmakefile,GNUmakefile.inc,makefile,makefile.inc}]
|
||||||
|
ij_visual_guides =
|
||||||
|
|
||||||
|
[{*.yaml,*.yml}]
|
||||||
|
indent_size = 2
|
||||||
|
ij_visual_guides =
|
||||||
|
ij_yaml_align_values_properties = do_not_align
|
||||||
|
ij_yaml_autoinsert_sequence_marker = true
|
||||||
|
ij_yaml_block_mapping_on_new_line = false
|
||||||
|
ij_yaml_indent_sequence_value = true
|
||||||
|
ij_yaml_keep_indents_on_empty_lines = false
|
||||||
|
ij_yaml_keep_line_breaks = true
|
||||||
|
ij_yaml_line_comment_add_space = false
|
||||||
|
ij_yaml_line_comment_add_space_on_reformat = false
|
||||||
|
ij_yaml_line_comment_at_first_column = true
|
||||||
|
ij_yaml_sequence_on_new_line = false
|
||||||
|
ij_yaml_space_before_colon = false
|
||||||
|
ij_yaml_spaces_within_braces = true
|
||||||
|
ij_yaml_spaces_within_brackets = true
|
||||||
51
.env.dist
51
.env.dist
@@ -4,17 +4,56 @@
|
|||||||
|
|
||||||
###> symfony/framework-bundle ###
|
###> symfony/framework-bundle ###
|
||||||
APP_ENV=dev
|
APP_ENV=dev
|
||||||
APP_SECRET=c1c278747d952ea66326352b72bb8ec6
|
APP_SECRET=changethis
|
||||||
|
APP_NAME=mineseeker
|
||||||
|
# APP_PUBLIC_HOSTNAME: The public hostname for your application (used for generating absolute URLs in emails)
|
||||||
|
# For production, set this to your domain (e.g., mineseeker.com)
|
||||||
|
APP_PUBLIC_HOSTNAME=localhost
|
||||||
|
# TRUSTED_PROXIES: Only needed for bare-metal dev behind a reverse proxy
|
||||||
|
# For Docker development, this is set in compose.override.yaml
|
||||||
|
# For production, set in PROD_ENV_FILE Gitea secret (use 172.18.0.0/16 initially)
|
||||||
#TRUSTED_PROXIES=127.0.0.1,127.0.0.2
|
#TRUSTED_PROXIES=127.0.0.1,127.0.0.2
|
||||||
#TRUSTED_HOSTS=localhost,example.com
|
#TRUSTED_HOSTS=localhost,example.com
|
||||||
###< symfony/framework-bundle ###
|
###< symfony/framework-bundle ###
|
||||||
|
|
||||||
###> doctrine/doctrine-bundle ###
|
###> doctrine/doctrine-bundle ###
|
||||||
# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
|
# Docker PostgreSQL is exposed on port 15432 — use that for bare-metal dev.
|
||||||
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
|
# Replace POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB with the values from your .env.
|
||||||
# Configure your db driver and server_version in config/packages/doctrine.yaml
|
DATABASE_URL=postgresql://POSTGRES_USER:POSTGRES_PASSWORD@127.0.0.1:15432/POSTGRES_DB?serverVersion=18&charset=utf8
|
||||||
DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name
|
|
||||||
###< doctrine/doctrine-bundle ###
|
###< doctrine/doctrine-bundle ###
|
||||||
|
|
||||||
|
###> google/recaptcha ###
|
||||||
|
RECAPTCHA_SITE_KEY=changethis
|
||||||
|
RECAPTCHA_SECRET_KEY=changethis
|
||||||
|
###< google/recaptcha ###
|
||||||
|
|
||||||
|
###> minio/minio ###
|
||||||
|
MINIO_ROOT_USER=changethis
|
||||||
|
MINIO_ROOT_PASSWORD=changethis
|
||||||
|
MINIO_ENDPOINT=http://localhost:9000
|
||||||
|
MINIO_PUBLIC_URL=https://your-minio-subdomain.example.com
|
||||||
|
###< minio/minio ###
|
||||||
|
|
||||||
###> symfony/mailer ###
|
###> symfony/mailer ###
|
||||||
# MAILER_DSN=smtp://localhost
|
MAILER_DSN=smtp://localhost:1025
|
||||||
|
MAIL_DOMAIN=localhost
|
||||||
###< symfony/mailer ###
|
###< symfony/mailer ###
|
||||||
|
|
||||||
|
###> symfony/mercure-bundle ###
|
||||||
|
# See https://symfony.com/doc/current/mercure.html#configuration
|
||||||
|
# The URL of the Mercure hub, used by the app to publish updates (can be a local URL)
|
||||||
|
MERCURE_URL=https://mine.local/.well-known/mercure
|
||||||
|
# The public URL of the Mercure hub, used by the browser to connect
|
||||||
|
MERCURE_PUBLIC_URL=https://mine.local/.well-known/mercure
|
||||||
|
# The secret used to sign the JWTs
|
||||||
|
MERCURE_JWT_SECRET="!ChangeThisMercureHubJWTSecretKey!"
|
||||||
|
# Publisher JWT (must match publisher_jwt in your Caddyfile)
|
||||||
|
MERCURE_JWT_TOKEN=changethis
|
||||||
|
# Subscriber JWT sent to the browser so it can authenticate its EventSource connection
|
||||||
|
MERCURE_SUBSCRIBER_JWT=changethis
|
||||||
|
###< symfony/mercure-bundle ###
|
||||||
|
|
||||||
|
###> web-auth/webauthn-framework ###
|
||||||
|
WEBAUTHN_RP_ID=mine.local
|
||||||
|
WEBAUTHN_ORIGIN=https://mine.local
|
||||||
|
###< web-auth/webauthn-framework ###
|
||||||
|
|||||||
32
.gitchangelog.rc
Normal file
32
.gitchangelog.rc
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
ignore_regexps = [
|
||||||
|
r'@minor', r'!minor',
|
||||||
|
r'@cosmetic', r'!cosmetic',
|
||||||
|
r'@refactor', r'!refactor',
|
||||||
|
r'@wip', r'!wip',
|
||||||
|
r'^(.{3,3}\s*:)?\s*[fF]irst commit.?\s*$',
|
||||||
|
r'^$', ## ignore commits with empty messages
|
||||||
|
r'@skipChangelog', r'!skipChangelog', r'skipChangeLog', r'!skipChangeLog',
|
||||||
|
r'Merge branch', r'Merge remote-tracking branch', r'!deploy',
|
||||||
|
]
|
||||||
|
section_regexps = [
|
||||||
|
('New', [
|
||||||
|
r'^[nN]ew\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
||||||
|
]),
|
||||||
|
('Changes', [
|
||||||
|
r'^[cC]hg\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
||||||
|
]),
|
||||||
|
('Fix', [
|
||||||
|
r'^[fF]ix\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
||||||
|
]),
|
||||||
|
('Other', None ## Match all lines
|
||||||
|
),
|
||||||
|
]
|
||||||
|
body_process = ReSub(r'((^|\n)[A-Z]\w+(-\w+)*: .*(\n\s+.*)*)+$', r'') | strip
|
||||||
|
subject_process = (strip |
|
||||||
|
ReSub(r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n@]*)(@[a-z]+\s+)*$', r'\4') |
|
||||||
|
SetIfEmpty("No commit message.") | ucfirst | final_dot)
|
||||||
|
tag_filter_regexp = r'^(v)?[0-9]+\.[0-9]+(\.[0-9]+)?(\-[0-9]+)?$'
|
||||||
|
unreleased_version_label = "(unreleased)"
|
||||||
|
output_engine = mustache("markdown")
|
||||||
|
include_merge = True
|
||||||
|
revs = []
|
||||||
39
.gitea/workflows/deploy.yml
Normal file
39
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: Deploy to Production
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: splendid-bear
|
||||||
|
steps:
|
||||||
|
- name: Checkout tag
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ gitea.token }}
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
export HOME=/tmp
|
||||||
|
git config --global credential.helper '!f() { echo "username=oauth2"; echo "password=$GITEA_TOKEN"; }; f'
|
||||||
|
git config --global --add safe.directory "${{ vars.PROD_APP_DIR }}"
|
||||||
|
cd "${{ vars.PROD_APP_DIR }}"
|
||||||
|
git remote set-url origin "${{ gitea.server_url }}/${{ gitea.repository }}.git"
|
||||||
|
git fetch --tags --force
|
||||||
|
git checkout "${{ gitea.ref_name }}"
|
||||||
|
|
||||||
|
- name: Write .env
|
||||||
|
env:
|
||||||
|
PROD_ENV_FILE: ${{ secrets.PROD_ENV_FILE }}
|
||||||
|
run: |
|
||||||
|
printf '%s' "$PROD_ENV_FILE" > "${{ vars.PROD_APP_DIR }}/.env"
|
||||||
|
|
||||||
|
- name: Build image
|
||||||
|
run: |
|
||||||
|
cd "${{ vars.PROD_APP_DIR }}"
|
||||||
|
docker compose build
|
||||||
|
|
||||||
|
- name: Start services
|
||||||
|
run: |
|
||||||
|
cd "${{ vars.PROD_APP_DIR }}"
|
||||||
|
docker compose up -d
|
||||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,8 +1,11 @@
|
|||||||
###> system7 - jotunheimr ###
|
###> system7 - jotunheimr ###
|
||||||
|
compose.override.yaml
|
||||||
+bak/
|
+bak/
|
||||||
.idea/
|
.idea/
|
||||||
node_modules/
|
node_modules/
|
||||||
nohup.out
|
nohup.out
|
||||||
|
/public/build/
|
||||||
|
/config/reference.php
|
||||||
###< system7 - jotunheimr ###
|
###< system7 - jotunheimr ###
|
||||||
|
|
||||||
###> symfony/framework-bundle ###
|
###> symfony/framework-bundle ###
|
||||||
@@ -11,10 +14,3 @@ nohup.out
|
|||||||
/var/
|
/var/
|
||||||
/vendor/
|
/vendor/
|
||||||
###< symfony/framework-bundle ###
|
###< symfony/framework-bundle ###
|
||||||
|
|
||||||
###> symfony/webpack-encore-bundle ###
|
|
||||||
/node_modules/
|
|
||||||
/public/build/
|
|
||||||
npm-debug.log
|
|
||||||
yarn-error.log
|
|
||||||
###< symfony/webpack-encore-bundle ###
|
|
||||||
|
|||||||
500
CHANGELOG.md
500
CHANGELOG.md
@@ -1,105 +1,403 @@
|
|||||||
Changelog
|
# Changelog
|
||||||
=========
|
|
||||||
|
|
||||||
|
|
||||||
0.4.0 (2019-10-26)
|
## v2026.2.2-9 (2026-04-18)
|
||||||
------------------
|
|
||||||
- Change session driver to REDIS. [Lang]
|
### New
|
||||||
- Add created, updated field to db && improve graph design. [Lang]
|
|
||||||
- Cache setup && optimalize for google pagespeed && optimalize all
|
* Add rules page #4. [Lang]
|
||||||
images. [Lang]
|
|
||||||
- Improve graph design on homepage && add footer and techs && add
|
### Fix
|
||||||
official pages. [Lang]
|
|
||||||
- Bugfix mine websocket periodic mysql calling. [Lang]
|
* The font-awesome simplifying to work on bare-metal - & fix all warnings at build time #4. [Lang]
|
||||||
- Bugfix hwioauth remember me && centralize hwioauth and facebook
|
|
||||||
settings. [Lang]
|
* The css problem had been solved on reponsive gfx on homepage #4. [Lang]
|
||||||
- Centralize jquery && bugfix mysql auto-termination problem w/ user
|
|
||||||
auth. [Lang]
|
|
||||||
- Release beta4. [Lang]
|
## v2026.2.1-8 (2026-04-18)
|
||||||
- Gitignore npm debug log. [Lang]
|
|
||||||
- Add english lang everywhere && add snowfall && add centralized version
|
### Fix
|
||||||
nbr && improve stylesheet && slack integration. [Lang]
|
|
||||||
- Bugfix #30 && random bg in game. [Lang]
|
* Quickfix for https-only login - & add user data when the user is not logged in #4. [Lang]
|
||||||
- Add google analytics and facebook scripts && improve url share method
|
|
||||||
w/ fb && enforce https in prod. [Lang]
|
|
||||||
- Reg and login buttons on index && remove list method && facebook
|
## v2026.2.1-7 (2026-04-16)
|
||||||
centralize. [Lang]
|
|
||||||
- Redesign user frontend. [Lang]
|
### Changes
|
||||||
- Mods for performance; one js.min file on prod. [Lang]
|
|
||||||
- Improve webpack config for prod compile #23. [Lang]
|
* Add consent checkbox to user's registration - and fix the sharing pics #4. [Lang]
|
||||||
- Ssl handling #22 && reconnection issues #20, #21. [Lang]
|
|
||||||
- Facebook prod settings w/ app; hwi/HWIOAuthBundle. [Lang]
|
* Add correct version numbering and CHANGELOG - and add the LICENSE #4. [Lang]
|
||||||
- Refact && game reconnection and restore w/o refresh #3 && bugfix bomb
|
|
||||||
explosion on opponent mines #19. [Lang]
|
|
||||||
- Typo in rpc. [Lang]
|
## v2026.2.1-6 (2026-04-16)
|
||||||
- Handle prod mysql timeout && graphics improve. [Lang]
|
|
||||||
- Gitignore webpacked index.js. [Lang]
|
### Changes
|
||||||
- Add production mods. [Lang]
|
|
||||||
- Bugfix points saving and exploded bombs to db && you can resign #6.
|
* Update all texts on all pages - extend them with the game specific things #4. [Lang]
|
||||||
[Lang]
|
|
||||||
- Bugfix resign button existence #11. [Lang]
|
|
||||||
- Bugfix opponent bomb btn buzz on hover #10. [Lang]
|
## v2026.2.1-5 (2026-04-16)
|
||||||
- Bugfix points problem in the end #16. [Lang]
|
|
||||||
- Add desc to every user #9. [Lang]
|
### Fix
|
||||||
- Clipboard - not working #8. [Lang]
|
|
||||||
- Random player on start #5. [Lang]
|
* The meta tags does not have https scheme - nothing worked in configuration #4. [Lang]
|
||||||
- Show left mines after end #2 && reduce network traffic && better
|
|
||||||
active field checking method. [Lang]
|
|
||||||
- Some refactor #13. [Lang]
|
## v2026.2.1-4 (2026-04-15)
|
||||||
- Bugfix grid field render #12. [Lang]
|
|
||||||
- Game ends after x mines. [Lang]
|
### New
|
||||||
- Add new sounds && refactor && new bg images && form redesigns. [Lang]
|
|
||||||
- Bugfix entities gridrow, grid && improve graph design on homepage.
|
* Add notification email when a user is registered #4. [Lang]
|
||||||
[Lang]
|
|
||||||
- Some refactor && prod settings. [Lang]
|
### Changes
|
||||||
- Improve graphics design in game. [Lang]
|
|
||||||
- Bugfix grid row in entity. [Lang]
|
* Add notification on activation too #4. [Lang]
|
||||||
- Bugfix changePlayer after bomb explosion. [Lang]
|
|
||||||
- Improve game graph design. [Lang]
|
|
||||||
- Login and register form more design. [Lang]
|
## v2026.2.1-2 (2026-04-15)
|
||||||
- Add basic design to userbundle && refactor. [Lang]
|
|
||||||
- Add font-awesome. [Lang]
|
### Fix
|
||||||
- Working user authentication w/ fb and plain login. [Lang]
|
|
||||||
- Add facebook login module, hwi/HWIOAuthBundle. [Lang]
|
* Another attempt to fix the email assets #4. [Lang]
|
||||||
- Login && register form overrided. [Lang]
|
|
||||||
- Js and config refactor. [Lang]
|
|
||||||
- Replace gridcol object to json array in db. [Lang]
|
## v2026.2.1-1 (2026-04-15)
|
||||||
- Refactor. [Lang]
|
|
||||||
- Save steps and point info to db. [Lang]
|
### Fix
|
||||||
- Save the step data to db. [Lang]
|
|
||||||
- Renamed the acme to mineseeker && handle when the user connection has
|
* The images does not shows in emails #4. [Lang]
|
||||||
been lost. [Lang]
|
|
||||||
- Add player names to UI. [Lang]
|
|
||||||
- Add overlay && game do not start until the opponent came. [Lang]
|
## v2026.2.1-0 (2026-04-15)
|
||||||
- Add base64 encryption to grid when it has been sended to server.
|
|
||||||
[Lang]
|
### New
|
||||||
- On click opponents bomb, you cannot target && refactor. [Lang]
|
|
||||||
- Warning when player has been found more than 20 mines. [Lang]
|
* Add Contact page with email sending behaviour #4. [Lang]
|
||||||
- Bugfix center mine counter animation. [Lang]
|
|
||||||
- The opponent is the next when bomb is exploded. [Lang]
|
### Changes
|
||||||
- Current username checked && refactor && remove players in channel when
|
|
||||||
they are more than 2. [Lang]
|
* Add missing .env variable and increase the version number and add missing data from front-end and back-end deps descriptor #4. [Lang]
|
||||||
- Send bomb info and use it on opponent. [Lang]
|
|
||||||
- Add sounds w/ howler. [Lang]
|
* Change the shareable battle - add avatars to it - even on the og tags #4. [Lang]
|
||||||
- Bugfix multiple empty fields w/ one click on opponent view. [Lang]
|
|
||||||
- Refact && remove sound and logging && bugfix BIGBUG - handleGridField
|
* Change text #4. [Lang]
|
||||||
and showAppropriateFields sort order... [Lang]
|
|
||||||
- Create first working communication. [Lang]
|
### Fix
|
||||||
- Create entities and repositories. [Lang]
|
|
||||||
- Changed websocket default port && debug RPC. [Lang]
|
* The mailhog is crashed on development env #4. [Lang]
|
||||||
- Created working session and client handler w/ websocket. [Lang]
|
|
||||||
- Working websocket client and server w/o session handling and storage.
|
* The og tags did not have proper http schema - they should have https #4. [Lang]
|
||||||
[Lang]
|
|
||||||
- Composer update. [Lang]
|
|
||||||
- Improve game && start sound creating. [Lang]
|
## v2026.2.0-5 (2026-04-14)
|
||||||
- Refactor grid control and grid field. [Lang]
|
|
||||||
- Created basic game w/ table and animations. [Lang]
|
### Changes
|
||||||
- Websocket basic setup FE & BE && working basic game w/ react &&
|
|
||||||
webpack & babel config. [Lang]
|
* Add donation button #4. [Lang]
|
||||||
- Gitignore node_modules && add symlink to node_modules (just for
|
|
||||||
install) && basic react. [Lang]
|
|
||||||
- Add react hello world. [Lang]
|
## v2026.2.0-4 (2026-04-14)
|
||||||
- Rename project in config. [Lang]
|
|
||||||
- Initial commit && create project in symfony3. [Lang]
|
### New
|
||||||
|
|
||||||
|
* Add timer for the acceptance of the challenge #4. [Lang]
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
* Protect the gameplay with recaptcha #4. [Lang]
|
||||||
|
|
||||||
|
* The waiting dialog is uncloseable until the time is up #4. [Lang]
|
||||||
|
|
||||||
|
* Add share button to the overlay when the game ends #4. [Lang]
|
||||||
|
|
||||||
|
* Make fancy og tags - and create a special one for battle sharing #4. [Lang]
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
* Missing font-awesome icons on bare-metal environment #4. [Lang]
|
||||||
|
|
||||||
|
|
||||||
|
## v2026.2.0-3 (2026-04-14)
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
* The user's avatar will be saved as a uuid.extension #4. [Lang]
|
||||||
|
|
||||||
|
|
||||||
|
## v2026.2.0-1 (2026-04-14)
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
* Quickfix for email sending #4. [Lang]
|
||||||
|
|
||||||
|
|
||||||
|
## v2026.2.0-0 (2026-04-14)
|
||||||
|
|
||||||
|
### New
|
||||||
|
|
||||||
|
* Registered users have avatars next to the timer #4. [Lang]
|
||||||
|
|
||||||
|
* Add opportunity to use profile picture. #4. [Lang]
|
||||||
|
|
||||||
|
* Add more stats and a dialog for the recent battle that can be shareable #4. [Lang]
|
||||||
|
|
||||||
|
* Implement the 2FA authentication (TOTP and backup codes) #4. [Lang]
|
||||||
|
|
||||||
|
* Add beta logo to the corner #3. [Lang]
|
||||||
|
|
||||||
|
* Add mineseeker game to the symfony 4 project #3. [Lang]
|
||||||
|
|
||||||
|
* Upgrade to the latest symfony v4 #3. [Lang]
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
* Implement CD script to Gitea and add docs to the process #4. [Lang]
|
||||||
|
|
||||||
|
* Remove unnecessary cdn based fonts #4. [Lang]
|
||||||
|
|
||||||
|
* Update docs #4. [Lang]
|
||||||
|
|
||||||
|
* Add JWT generation script to make Mercure safe #4. [Lang]
|
||||||
|
|
||||||
|
* Fix missing favicon #4. [Lang]
|
||||||
|
|
||||||
|
* Make compatible the whole project with bare metal AND with docker #4. [Lang]
|
||||||
|
|
||||||
|
* Add modern Webauthn authentication #4. [Lang]
|
||||||
|
|
||||||
|
* Refactor all forms to have Symfony Form Types & Validation Constrainsts - & implement Google ReCapthca v3 #4. [Lang]
|
||||||
|
|
||||||
|
* Add forgot password functionality #4. [Lang]
|
||||||
|
|
||||||
|
* Increase the minimum PHP version to the latest major - and massive refactor on back-end, like Controllers and Repositories #4. [Lang]
|
||||||
|
|
||||||
|
* Redesign the resign dialog #4. [Lang]
|
||||||
|
|
||||||
|
* Re-implement the waiting for opponent dialog - refactor its gfx - & add online user selection dialog #4. [Lang]
|
||||||
|
|
||||||
|
* Improve the gfx on homepage - implement login/register and activation for authentication - and add the first version of profile page #4. [Lang]
|
||||||
|
|
||||||
|
* Refactor and redesign the gfx on front-end #4. [Lang]
|
||||||
|
|
||||||
|
* Upgrade to the latest LTS Symfony package and backend #4. [Lang]
|
||||||
|
|
||||||
|
* Add timers to each player - renew the whole migration #4. [Lang]
|
||||||
|
|
||||||
|
* Update the vite related stuff because CORS and React errors - reinit the miration #4. [Lang]
|
||||||
|
|
||||||
|
* Use namespaces for front-end #4. [Lang]
|
||||||
|
|
||||||
|
* Replace webpack w/ vite & remove old, legacy jQuery from the code #4. [Lang]
|
||||||
|
|
||||||
|
* More, massive refactor for front-end #4. [Lang]
|
||||||
|
|
||||||
|
* Massive refactor on front-end - and remove unnecessary deps #4. [Lang]
|
||||||
|
|
||||||
|
* Change the code style to fit the current standard #4. [Lang]
|
||||||
|
|
||||||
|
* Refactor to use Attributes instead of yaml markdown #4. [Lang]
|
||||||
|
|
||||||
|
* Outsource the Grid generation and interactions to the backend #4. [Lang]
|
||||||
|
|
||||||
|
* Remove unnecessary variables and prune the Facebook registration method #4. [Lang]
|
||||||
|
|
||||||
|
* Replace the legacy gos/web-socket-bundle & replace it with Mercure protocol #4. [Lang]
|
||||||
|
|
||||||
|
* Make a massive refactor to the backend and remove all unnecessary deps - and make small refactors for the frontend too #4. [Lang]
|
||||||
|
|
||||||
|
* Created the first working solution since 7 yrs #4. [Lang]
|
||||||
|
|
||||||
|
* Add some changes on BE - add eslint and editorconfig - and add some deps #4. [Lang]
|
||||||
|
|
||||||
|
* Make the first working version - the stepping is broken due to the algorythm structure #4. [Lang]
|
||||||
|
|
||||||
|
* Change the composer default php minimum environment #3. [Lang]
|
||||||
|
|
||||||
|
* Change the default url to wss on frontend #3. [Lang]
|
||||||
|
|
||||||
|
* Refactor Rpc and Topic classes #3. [Lang]
|
||||||
|
|
||||||
|
* Refactor classes and reformat some layout #3. [Lang]
|
||||||
|
|
||||||
|
* Remove deprecated files #3. [Lang]
|
||||||
|
|
||||||
|
* Doc in README.md #3. [Lang]
|
||||||
|
|
||||||
|
* Gitignore a js.map file #2. [Lang]
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
* Pkg: usr: solve the not-working mailing on dev env under docker #4. [Lang]
|
||||||
|
|
||||||
|
|
||||||
|
## 1.1.0 (2019-10-26)
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
* Reinit project - disable redis module and make the project compatible w/ PHP7.3 #2. [Lang]
|
||||||
|
|
||||||
|
|
||||||
|
## 0.4.0 (2019-10-26)
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
* Change session driver to REDIS. [Lang]
|
||||||
|
|
||||||
|
* Add created, updated field to db && improve graph design. [Lang]
|
||||||
|
|
||||||
|
* Cache setup && optimalize for google pagespeed && optimalize all images. [Lang]
|
||||||
|
|
||||||
|
* Improve graph design on homepage && add footer and techs && add official pages. [Lang]
|
||||||
|
|
||||||
|
* Bugfix mine websocket periodic mysql calling. [Lang]
|
||||||
|
|
||||||
|
* Bugfix hwioauth remember me && centralize hwioauth and facebook settings. [Lang]
|
||||||
|
|
||||||
|
* Centralize jquery && bugfix mysql auto-termination problem w/ user auth. [Lang]
|
||||||
|
|
||||||
|
* Release beta4. [Lang]
|
||||||
|
|
||||||
|
* Gitignore npm debug log. [Lang]
|
||||||
|
|
||||||
|
* Add english lang everywhere && add snowfall && add centralized version nbr && improve stylesheet && slack integration. [Lang]
|
||||||
|
|
||||||
|
* Bugfix #30 && random bg in game. [Lang]
|
||||||
|
|
||||||
|
* Add google analytics and facebook scripts && improve url share method w/ fb && enforce https in prod. [Lang]
|
||||||
|
|
||||||
|
* Reg and login buttons on index && remove list method && facebook centralize. [Lang]
|
||||||
|
|
||||||
|
* Redesign user frontend. [Lang]
|
||||||
|
|
||||||
|
* Mods for performance; one js.min file on prod. [Lang]
|
||||||
|
|
||||||
|
* Improve webpack config for prod compile #23. [Lang]
|
||||||
|
|
||||||
|
* Ssl handling #22 && reconnection issues #20, #21. [Lang]
|
||||||
|
|
||||||
|
* Facebook prod settings w/ app; hwi/HWIOAuthBundle. [Lang]
|
||||||
|
|
||||||
|
* Refact && game reconnection and restore w/o refresh #3 && bugfix bomb explosion on opponent mines #19. [Lang]
|
||||||
|
|
||||||
|
* Typo in rpc. [Lang]
|
||||||
|
|
||||||
|
* Handle prod mysql timeout && graphics improve. [Lang]
|
||||||
|
|
||||||
|
* Gitignore webpacked index.js. [Lang]
|
||||||
|
|
||||||
|
* Add production mods. [Lang]
|
||||||
|
|
||||||
|
* Bugfix points saving and exploded bombs to db && you can resign #6. [Lang]
|
||||||
|
|
||||||
|
* Bugfix resign button existence #11. [Lang]
|
||||||
|
|
||||||
|
* Bugfix opponent bomb btn buzz on hover #10. [Lang]
|
||||||
|
|
||||||
|
* Bugfix points problem in the end #16. [Lang]
|
||||||
|
|
||||||
|
* Add desc to every user #9. [Lang]
|
||||||
|
|
||||||
|
* Clipboard - not working #8. [Lang]
|
||||||
|
|
||||||
|
* Random player on start #5. [Lang]
|
||||||
|
|
||||||
|
* Show left mines after end #2 && reduce network traffic && better active field checking method. [Lang]
|
||||||
|
|
||||||
|
* Some refactor #13. [Lang]
|
||||||
|
|
||||||
|
* Bugfix grid field render #12. [Lang]
|
||||||
|
|
||||||
|
* Game ends after x mines. [Lang]
|
||||||
|
|
||||||
|
* Add new sounds && refactor && new bg images && form redesigns. [Lang]
|
||||||
|
|
||||||
|
* Bugfix entities gridrow, grid && improve graph design on homepage. [Lang]
|
||||||
|
|
||||||
|
* Some refactor && prod settings. [Lang]
|
||||||
|
|
||||||
|
* Improve graphics design in game. [Lang]
|
||||||
|
|
||||||
|
* Bugfix grid row in entity. [Lang]
|
||||||
|
|
||||||
|
* Bugfix changePlayer after bomb explosion. [Lang]
|
||||||
|
|
||||||
|
* Improve game graph design. [Lang]
|
||||||
|
|
||||||
|
* Login and register form more design. [Lang]
|
||||||
|
|
||||||
|
* Add basic design to userbundle && refactor. [Lang]
|
||||||
|
|
||||||
|
* Add font-awesome. [Lang]
|
||||||
|
|
||||||
|
* Working user authentication w/ fb and plain login. [Lang]
|
||||||
|
|
||||||
|
* Add facebook login module, hwi/HWIOAuthBundle. [Lang]
|
||||||
|
|
||||||
|
* Login && register form overrided. [Lang]
|
||||||
|
|
||||||
|
* Js and config refactor. [Lang]
|
||||||
|
|
||||||
|
* Replace gridcol object to json array in db. [Lang]
|
||||||
|
|
||||||
|
* Refactor. [Lang]
|
||||||
|
|
||||||
|
* Save steps and point info to db. [Lang]
|
||||||
|
|
||||||
|
* Save the step data to db. [Lang]
|
||||||
|
|
||||||
|
* Renamed the acme to mineseeker && handle when the user connection has been lost. [Lang]
|
||||||
|
|
||||||
|
* Add player names to UI. [Lang]
|
||||||
|
|
||||||
|
* Add overlay && game do not start until the opponent came. [Lang]
|
||||||
|
|
||||||
|
* Add base64 encryption to grid when it has been sended to server. [Lang]
|
||||||
|
|
||||||
|
* On click opponents bomb, you cannot target && refactor. [Lang]
|
||||||
|
|
||||||
|
* Warning when player has been found more than 20 mines. [Lang]
|
||||||
|
|
||||||
|
* Bugfix center mine counter animation. [Lang]
|
||||||
|
|
||||||
|
* The opponent is the next when bomb is exploded. [Lang]
|
||||||
|
|
||||||
|
* Current username checked && refactor && remove players in channel when they are more than 2. [Lang]
|
||||||
|
|
||||||
|
* Send bomb info and use it on opponent. [Lang]
|
||||||
|
|
||||||
|
* Add sounds w/ howler. [Lang]
|
||||||
|
|
||||||
|
* Bugfix multiple empty fields w/ one click on opponent view. [Lang]
|
||||||
|
|
||||||
|
* Refact && remove sound and logging && bugfix BIGBUG - handleGridField and showAppropriateFields sort order... [Lang]
|
||||||
|
|
||||||
|
* Create first working communication. [Lang]
|
||||||
|
|
||||||
|
* Create entities and repositories. [Lang]
|
||||||
|
|
||||||
|
* Changed websocket default port && debug RPC. [Lang]
|
||||||
|
|
||||||
|
* Created working session and client handler w/ websocket. [Lang]
|
||||||
|
|
||||||
|
* Working websocket client and server w/o session handling and storage. [Lang]
|
||||||
|
|
||||||
|
* Composer update. [Lang]
|
||||||
|
|
||||||
|
* Improve game && start sound creating. [Lang]
|
||||||
|
|
||||||
|
* Refactor grid control and grid field. [Lang]
|
||||||
|
|
||||||
|
* Created basic game w/ table and animations. [Lang]
|
||||||
|
|
||||||
|
* Websocket basic setup FE & BE && working basic game w/ react && webpack & babel config. [Lang]
|
||||||
|
|
||||||
|
* Gitignore node_modules && add symlink to node_modules (just for install) && basic react. [Lang]
|
||||||
|
|
||||||
|
* Add react hello world. [Lang]
|
||||||
|
|
||||||
|
* Rename project in config. [Lang]
|
||||||
|
|
||||||
|
* Initial commit && create project in symfony3. [Lang]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
29
Caddyfile
Normal file
29
Caddyfile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
{$CADDY_GLOBAL_OPTIONS}
|
||||||
|
|
||||||
|
frankenphp {
|
||||||
|
{$FRANKENPHP_CONFIG}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{$SERVER_NAME:localhost} {
|
||||||
|
log
|
||||||
|
|
||||||
|
root * /app/public
|
||||||
|
|
||||||
|
encode zstd br gzip
|
||||||
|
|
||||||
|
# Forward scheme information to the PHP application
|
||||||
|
header X-Forwarded-Proto {scheme}
|
||||||
|
header X-Forwarded-Host {host}
|
||||||
|
|
||||||
|
mercure {
|
||||||
|
transport_url {$MERCURE_TRANSPORT_URL:bolt:///data/mercure.db}
|
||||||
|
publisher_jwt {$MERCURE_JWT_SECRET} HS256
|
||||||
|
subscriber_jwt {$MERCURE_JWT_SECRET} HS256
|
||||||
|
anonymous
|
||||||
|
subscriptions
|
||||||
|
}
|
||||||
|
|
||||||
|
php_server
|
||||||
|
}
|
||||||
58
Dockerfile
Normal file
58
Dockerfile
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
FROM oven/bun:1-alpine AS assets
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json bun.lock ./
|
||||||
|
RUN bun install --frozen-lockfile
|
||||||
|
|
||||||
|
COPY vite.config.js ./
|
||||||
|
COPY assets/ assets/
|
||||||
|
COPY public/ public/
|
||||||
|
|
||||||
|
RUN bun run build
|
||||||
|
|
||||||
|
FROM dunglas/frankenphp:latest
|
||||||
|
|
||||||
|
RUN install-php-extensions \
|
||||||
|
pdo_pgsql \
|
||||||
|
gd \
|
||||||
|
intl \
|
||||||
|
zip \
|
||||||
|
opcache \
|
||||||
|
apcu \
|
||||||
|
sodium
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
fonts-dejavu-core \
|
||||||
|
fontconfig \
|
||||||
|
&& fc-cache -f -v \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
|
||||||
|
RUN printf '[opcache]\nopcache.enable=1\nopcache.memory_consumption=256\nopcache.max_accelerated_files=20000\nopcache.validate_timestamps=0\n' \
|
||||||
|
> "$PHP_INI_DIR/conf.d/opcache.ini"
|
||||||
|
|
||||||
|
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
||||||
|
|
||||||
|
ENV APP_ENV=prod
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN composer install \
|
||||||
|
--no-dev \
|
||||||
|
--no-interaction \
|
||||||
|
--no-scripts \
|
||||||
|
--optimize-autoloader
|
||||||
|
|
||||||
|
COPY --from=assets /app/public/build ./public/build
|
||||||
|
|
||||||
|
RUN mkdir -p var/cache var/log var/sessions && \
|
||||||
|
chown -R www-data:www-data var/
|
||||||
|
|
||||||
|
COPY Caddyfile /etc/caddy/Caddyfile
|
||||||
|
COPY docker/entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
124
LICENSE
Normal file
124
LICENSE
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2026 SplendidBear (https://www.splendidbear.org)
|
||||||
|
|
||||||
|
MineSeeker is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
MineSeeker is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS:
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of works.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this License.
|
||||||
|
|
||||||
|
"You" refers to each licensee.
|
||||||
|
|
||||||
|
"Legal entities" means the union of the acting entity and all other entities
|
||||||
|
that control, are controlled by, or are under common control with that entity.
|
||||||
|
|
||||||
|
"Modify" means to copy from or adapt all or part of the work in a fashion
|
||||||
|
requiring copyright permission, other than the making of an exact copy.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work for making
|
||||||
|
modifications to it. "Object code" means any non-source form of a work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of copyright
|
||||||
|
on the Program. You are granted all permissions necessary to run, modify and
|
||||||
|
propagate covered works by this License.
|
||||||
|
|
||||||
|
3. Copyleft - Derivative Works.
|
||||||
|
|
||||||
|
If you modify the Program, your modified version must:
|
||||||
|
|
||||||
|
- Carry prominent notices stating that you have modified it
|
||||||
|
- License the entire work under this License or a compatible license
|
||||||
|
- Make the source code available to recipients
|
||||||
|
- Preserve all notices of previous licensing
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you receive it,
|
||||||
|
provided that you:
|
||||||
|
|
||||||
|
- Keep intact all notices of authorship and licensing
|
||||||
|
- Give recipients access to the source code along with this License
|
||||||
|
- Do not modify anything except the License itself
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program under this License provided that:
|
||||||
|
|
||||||
|
- The work must be licensed as a whole under this License
|
||||||
|
- You must give prominent notice of any modifications
|
||||||
|
- You must provide access to the Corresponding Source code
|
||||||
|
- You preserve all licensing notices
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
If you convey object code or compiled versions, you must also provide:
|
||||||
|
|
||||||
|
- The Corresponding Source code (in machine-readable form)
|
||||||
|
- A notice of the terms under which it is licensed
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
No additional restrictions may be placed on the exercise of the rights
|
||||||
|
granted or affirmed under this License.
|
||||||
|
|
||||||
|
8. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
9. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING
|
||||||
|
ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF
|
||||||
|
THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS
|
||||||
|
OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR
|
||||||
|
THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For the complete GPL-3.0-or-later license text, visit:
|
||||||
|
https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
|
For more information about GNU GPL, visit:
|
||||||
|
https://www.gnu.org/licenses/
|
||||||
|
|
||||||
|
MineSeeker is a multiplayer minesweeper game inspired by MSN Messenger's game.
|
||||||
|
Project: https://www.mineseeker.hu
|
||||||
|
Author: SplendidBear (https://www.splendidbear.org)
|
||||||
|
|
||||||
|
|
||||||
84
Makefile
Normal file
84
Makefile
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
.PHONY: help start start-build stop build down ps logs prune-everything db-reset mercure-jwt cache-clear og-cache-clear
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Available commands:"
|
||||||
|
@echo " make start - Start services without building (uses existing images)"
|
||||||
|
@echo " make start-build - Start services and build images if needed"
|
||||||
|
@echo " make stop - Stop running services"
|
||||||
|
@echo " make build - Build Docker images only"
|
||||||
|
@echo " make down - Stop and remove containers/networks"
|
||||||
|
@echo " make prune-everything - Prune volumes, networks and images (DANGEROUS!)"
|
||||||
|
@echo " make db-reset - Reset the database (drop, create, migrate) (DANGEROUS!)"
|
||||||
|
@echo " make ccp - Clear the production cache"
|
||||||
|
@echo " make cache-clear - Clear all caches (Vite, Symfony, node_modules)"
|
||||||
|
@echo " make og-cache-clear - Clear Open Graph cache only"
|
||||||
|
|
||||||
|
start:
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
start-build:
|
||||||
|
composer i
|
||||||
|
bun run build
|
||||||
|
docker compose up -d --build
|
||||||
|
|
||||||
|
stop:
|
||||||
|
docker compose stop
|
||||||
|
|
||||||
|
build:
|
||||||
|
docker compose build
|
||||||
|
|
||||||
|
down:
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
prune-everything:
|
||||||
|
@echo "WARNING: This will remove ALL containers, volumes, networks and images!"
|
||||||
|
@read -p "Type 'yes' to confirm: " confirm; \
|
||||||
|
if [ "$$confirm" != "yes" ]; then \
|
||||||
|
echo "Aborted."; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
docker compose down -v --rmi all --remove-orphans
|
||||||
|
|
||||||
|
mercure-jwt:
|
||||||
|
@php bin/generate-mercure-jwt.php
|
||||||
|
|
||||||
|
db-reset:
|
||||||
|
@echo "WARNING: This will DROP and RECREATE the database!"
|
||||||
|
@read -p "Type 'yes' to confirm: " confirm; \
|
||||||
|
if [ "$$confirm" != "yes" ]; then \
|
||||||
|
echo "Aborted."; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
bin/console doctrine:database:drop --force --if-exists --no-interaction
|
||||||
|
bin/console doctrine:database:create --if-not-exists --no-interaction
|
||||||
|
bin/console doctrine:migrations:migrate --no-interaction
|
||||||
|
|
||||||
|
ccp:
|
||||||
|
bin/console cache:clear --no-warmup --env=prod
|
||||||
|
|
||||||
|
cache-clear:
|
||||||
|
@echo "Clearing all caches..."
|
||||||
|
@rm -rf node_modules/.vite
|
||||||
|
@rm -rf .vite
|
||||||
|
@rm -rf var/og-cache
|
||||||
|
@php bin/console cache:clear --no-warmup
|
||||||
|
@echo "✓ Vite cache cleared"
|
||||||
|
@echo "✓ OG cache cleared"
|
||||||
|
@echo "✓ Symfony cache cleared"
|
||||||
|
@echo ""
|
||||||
|
@echo "Rebuilding assets..."
|
||||||
|
@bun run build
|
||||||
|
@echo ""
|
||||||
|
@echo "✓ All caches cleared and assets rebuilt!"
|
||||||
|
@echo " Next step: Refresh browser with Ctrl+Shift+R"
|
||||||
|
|
||||||
|
og-cache-clear:
|
||||||
|
@echo "Clearing Open Graph cache..."
|
||||||
|
@rm -rf var/og-cache
|
||||||
|
@echo "✓ OG cache cleared!"
|
||||||
|
@echo " Battle card images will be regenerated on next access"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
308
README.md
308
README.md
@@ -1,40 +1,302 @@
|
|||||||
mineseeker
|
# MineSeeker
|
||||||
=========
|
|
||||||
|
|
||||||
A Symfony project created on September 22, 2016, 13:56 pm.
|
A real-time **1v1 multiplayer minesweeper** game played in the browser.
|
||||||
|
Two players race on the same hidden minefield — uncover safe cells to score points, but hit a mine and you hand the advantage to your opponent.
|
||||||
|
Games are live and synchronised instantly via a Mercure hub; no page reloads, no polling.
|
||||||
|
|
||||||
PROJECT VERSION 1.1.0-20191026
|
Created by [SplendidBear](https://www.splendidbear.org).
|
||||||
|
|
||||||
This is a Symfony 3 project w/ React JS in standalone mode and w/ WebSocket.
|
---
|
||||||
|
|
||||||
#### Must installed modules w/ npm are in package.json + to global:
|
## Features
|
||||||
|
|
||||||
$ npm install webpack -g
|
- **Real-time 1v1 gameplay** — moves broadcast instantly over Mercure (server-sent events)
|
||||||
|
- **Guest & registered play** — jump in anonymously or create an account for stats and history
|
||||||
|
- **Full authentication stack** — email/password, passkeys (WebAuthn), TOTP 2FA with backup codes
|
||||||
|
- **Player profiles** — win/loss/draw stats, per-month charts, recent battle history with shareable replay links
|
||||||
|
- **Profile pictures** — uploaded to MinIO object storage, thumbnails generated on-the-fly by LiipImagine
|
||||||
|
- **Battle replay sharing** — share a direct link to any finished game
|
||||||
|
- **Docker-ready** — single `make start-build` brings up the full production-like stack
|
||||||
|
|
||||||
You will need a
|
---
|
||||||
.babelrc file w/ the presets
|
|
||||||
webpack.config.js - https://webpack.github.io/docs/webpack-for-browserify-users.html
|
|
||||||
same as dir where the package.json!!
|
|
||||||
|
|
||||||
__(!) Tutorial: https://egghead.io/lessons/react-introduction-to-properties__
|
## Tech stack
|
||||||
|
|
||||||
#### Backend WebSocket server start as daemon - GeniusesOfSymfony/WebSocketBundle
|
| Layer | Technology |
|
||||||
|
|---|---|
|
||||||
|
| Backend | PHP 8.5, Symfony 7.4, Doctrine ORM |
|
||||||
|
| Frontend | React 19, Vite, MUI, SCSS |
|
||||||
|
| Database | PostgreSQL 18 |
|
||||||
|
| Real-time | Mercure (built into FrankenPHP / Caddy) |
|
||||||
|
| File storage | MinIO (S3-compatible) |
|
||||||
|
| Image processing | LiipImagine + Flysystem |
|
||||||
|
| Server | FrankenPHP (Caddy + PHP in one binary) |
|
||||||
|
| Auth | Symfony Security, Scheb 2FA, web-auth/webauthn-framework |
|
||||||
|
|
||||||
$ nohup bin/console gos:websocket:server &
|
---
|
||||||
|
|
||||||
#### React JS WebPack watch generator w/ babel presets: es2015, react
|
## Requirements
|
||||||
|
|
||||||
$ webpack -p --config=webpack-prod.config.js
|
### Bare-metal development
|
||||||
|
- PHP >= 8.5 with extensions: `pdo_pgsql`, `gd`, `intl`, `zip`, `sodium`
|
||||||
|
- Composer 2
|
||||||
|
- Node.js 22 + Bun
|
||||||
|
- PostgreSQL 18
|
||||||
|
- Caddy with FrankenPHP and the Mercure module
|
||||||
|
- MailHog (or any SMTP server on port 1025)
|
||||||
|
- MinIO (listening on port 9000)
|
||||||
|
|
||||||
PROD
|
### Docker
|
||||||
|
- Docker Engine 24+
|
||||||
|
- Docker Compose v2
|
||||||
|
- Bun (for building frontend assets before `make start-build`)
|
||||||
|
- Composer (for `make start-build`)
|
||||||
|
|
||||||
$ webpack --progress --colors --watch -d
|
---
|
||||||
|
|
||||||
DEV
|
## Installation
|
||||||
|
|
||||||
-d --> Debugger; If you write this line somewhere: debugger;
|
### 1. Clone the repository
|
||||||
The browser will stop the code here!!!
|
|
||||||
|
|
||||||
#### Connect to Prod
|
```bash
|
||||||
|
git clone https://github.com/splendidbear/mineseeker.git
|
||||||
|
cd mineseeker
|
||||||
|
```
|
||||||
|
|
||||||
ssh xxsvci@laszlolang.com -i ~/.ssh/id_rsa_laszlolang
|
### 2. Configure environment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.dist .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit `.env` and fill in every value. Key ones:
|
||||||
|
|
||||||
|
| Variable | What to set |
|
||||||
|
|---|---|
|
||||||
|
| `APP_SECRET` | Random 32-byte hex: `openssl rand -hex 32` |
|
||||||
|
| `POSTGRES_USER/PASSWORD/DB` | Your PostgreSQL credentials |
|
||||||
|
| `MINIO_ROOT_USER/PASSWORD` | MinIO admin credentials |
|
||||||
|
| `MINIO_ENDPOINT` | `http://localhost:9000` (bare-metal) |
|
||||||
|
| `MINIO_PUBLIC_URL` | Public URL browsers use to reach MinIO |
|
||||||
|
| `RECAPTCHA_SITE_KEY/SECRET_KEY` | Google reCAPTCHA v3 keys for your domain |
|
||||||
|
| `MERCURE_JWT_SECRET` | Random secret (generated in step 3) |
|
||||||
|
| `MERCURE_JWT_TOKEN` | Signed publisher JWT (generated in step 3) |
|
||||||
|
| `MERCURE_SUBSCRIBER_JWT` | Signed subscriber JWT (generated in step 3) |
|
||||||
|
| `MAILER_DSN` | `smtp://localhost:1025` for MailHog in dev |
|
||||||
|
|
||||||
|
### 3. Generate Mercure JWT tokens
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
make mercure-jwt
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy the printed values into `.env` and into the `publisher_jwt` / `subscriber_jwt` lines of your Caddy Mercure block, then reload Caddy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl reload caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4a. Run with Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make start-build
|
||||||
|
```
|
||||||
|
|
||||||
|
This installs PHP dependencies, builds the frontend assets with Bun, builds the Docker image, and starts all services (`app`, `db`, `mail`, `minio`, `minio_init`).
|
||||||
|
|
||||||
|
The app is available at `http://localhost:10080` (or the domain set in `APP_PUBLIC_HOSTNAME`).
|
||||||
|
|
||||||
|
To apply any code changes later, run the same command again.
|
||||||
|
|
||||||
|
### 4b. Run bare-metal (development)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
bun install
|
||||||
|
bun run dev # Vite dev server with hot-reload
|
||||||
|
php bin/console doctrine:migrations:migrate --no-interaction
|
||||||
|
```
|
||||||
|
|
||||||
|
Start MinIO and MailHog, then open the URL configured in your Caddy vhost.
|
||||||
|
|
||||||
|
### 5. MinIO bucket setup
|
||||||
|
|
||||||
|
**Docker** — handled automatically on first start by the `minio_init` service. No action needed.
|
||||||
|
|
||||||
|
**Bare-metal** — run once after MinIO is up:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mc alias set local http://localhost:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD
|
||||||
|
mc mb local/mineseeker
|
||||||
|
echo '' | mc pipe local/mineseeker/media/.keep
|
||||||
|
echo '' | mc pipe local/mineseeker/cache/.keep
|
||||||
|
# Apply public-read policy for media/ and cache/ — see docker/minio-init.sh for the JSON
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Development environment
|
||||||
|
|
||||||
|
When running the Docker stack locally you typically want to catch outgoing emails instead of relaying them through a real SMTP server.
|
||||||
|
The production `compose.yaml` uses Postfix for actual mail delivery and must not be edited for local overrides.
|
||||||
|
Use a `compose.override.yaml` file instead — Docker Compose merges it automatically on top of the base file whenever both are present.
|
||||||
|
|
||||||
|
### Email: replace Postfix with MailHog
|
||||||
|
|
||||||
|
Create `compose.override.yaml` in the project root (it is git-ignored and never reaches production):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
environment:
|
||||||
|
MAILER_DSN: smtp://mail:1025?verify_peer=0
|
||||||
|
TRUSTED_PROXIES: "0.0.0.0/0"
|
||||||
|
mail:
|
||||||
|
image: mailhog/mailhog:latest
|
||||||
|
ports:
|
||||||
|
- "1025:1025"
|
||||||
|
- "8025:8025"
|
||||||
|
```
|
||||||
|
|
||||||
|
This replaces the `mail` service image with MailHog and points the application's mailer at its SMTP port (`1025`).
|
||||||
|
No other files need to change.
|
||||||
|
|
||||||
|
After adding the file, restart the stack:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make start-build
|
||||||
|
```
|
||||||
|
|
||||||
|
All emails sent by the application are now captured by MailHog.
|
||||||
|
Open the web UI at **http://localhost:8025** to inspect them.
|
||||||
|
|
||||||
|
> **Production note** — `compose.override.yaml` is listed in `.gitignore`.
|
||||||
|
> Never commit it; the production server must only see `compose.yaml` with Postfix.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deploying to production
|
||||||
|
|
||||||
|
Releases are automated via Gitea Actions. Pushing a tag that starts with `v` (e.g. `v2026.01`) triggers the workflow at `.gitea/workflows/deploy.yml`.
|
||||||
|
The job runs on a **self-hosted runner** installed on the production server — the server only needs an outbound connection to Gitea, no open SSH port required.
|
||||||
|
The `app` image is rebuilt with the new code; the database and storage containers are untouched so all data is preserved.
|
||||||
|
|
||||||
|
### Gitea repository variables and secrets
|
||||||
|
|
||||||
|
**Variable** (plaintext, editable — **Repository → Settings → Variables**):
|
||||||
|
|
||||||
|
| Variable | Value |
|
||||||
|
|---|---|
|
||||||
|
| `PROD_APP_DIR` | Absolute path on the server (e.g. `/var/www/mineseeker`) |
|
||||||
|
|
||||||
|
**Secret** (encrypted, write-only — **Repository → Settings → Secrets**):
|
||||||
|
|
||||||
|
| Secret | Value |
|
||||||
|
|---|---|
|
||||||
|
| `PROD_ENV_FILE` | Full content of the production `.env` file (see below) |
|
||||||
|
|
||||||
|
The workflow writes `PROD_ENV_FILE` to `.env` on every deploy, so you never need to manage the file on the server manually. To update a credential, overwrite the secret in Gitea and push a new tag.
|
||||||
|
|
||||||
|
#### `PROD_ENV_FILE` contents
|
||||||
|
|
||||||
|
Paste the filled-in `.env` file as the secret value:
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
APP_NAME=mineseeker
|
||||||
|
APP_ENV=prod
|
||||||
|
APP_SECRET="<openssl rand -hex 32>"
|
||||||
|
|
||||||
|
DATABASE_URL=postgresql://POSTGRES_USER:POSTGRES_PASSWORD@db:5432/POSTGRES_DB?serverVersion=18&charset=utf8
|
||||||
|
|
||||||
|
POSTGRES_USER=mineseeker
|
||||||
|
POSTGRES_PASSWORD="<strong password>"
|
||||||
|
POSTGRES_DB=mineseeker
|
||||||
|
POSTGRES_VERSION=18
|
||||||
|
|
||||||
|
MINIO_ROOT_USER=mineseeker
|
||||||
|
MINIO_ROOT_PASSWORD="<strong password>"
|
||||||
|
MINIO_ENDPOINT=http://minio:9000
|
||||||
|
MINIO_PUBLIC_URL=https://aws.mineseeker.hu
|
||||||
|
|
||||||
|
MAILER_DSN=smtp://mail:25?verify_peer=0
|
||||||
|
MAIL_DOMAIN=mineseeker.hu
|
||||||
|
|
||||||
|
RECAPTCHA_SITE_KEY="<your reCAPTCHA v3 site key>"
|
||||||
|
RECAPTCHA_SECRET_KEY="<your reCAPTCHA v3 secret key>"
|
||||||
|
|
||||||
|
MERCURE_URL=https://mineseeker.hu/.well-known/mercure
|
||||||
|
MERCURE_PUBLIC_URL=https://mineseeker.hu/.well-known/mercure
|
||||||
|
MERCURE_JWT_SECRET="<generated by make mercure-jwt>"
|
||||||
|
MERCURE_JWT_TOKEN="<generated by make mercure-jwt>"
|
||||||
|
MERCURE_SUBSCRIBER_JWT="<generated by make mercure-jwt>"
|
||||||
|
|
||||||
|
APP_PUBLIC_HOSTNAME=mineseeker.hu
|
||||||
|
WEBAUTHN_RP_ID=mineseeker.hu
|
||||||
|
WEBAUTHN_ORIGIN=https://mineseeker.hu
|
||||||
|
|
||||||
|
# OG Tags & Social Media Sharing (IMPORTANT for Docker deployments)
|
||||||
|
# TRUSTED_PROXIES: IP address (or range) of your reverse proxy (Caddy/Nginx)
|
||||||
|
# This ensures OG image tags use HTTPS URLs instead of HTTP
|
||||||
|
TRUSTED_PROXIES="172.18.0.0/16"
|
||||||
|
TRUSTED_HOSTS="mineseeker.hu,www.mineseeker.hu"
|
||||||
|
```
|
||||||
|
### Production server: one-time setup
|
||||||
|
|
||||||
|
The server needs Docker, Git, and a self-hosted `act_runner` registered against the Gitea repository. Bun and Composer run inside the multi-stage Dockerfile, so they are not needed on the server.
|
||||||
|
|
||||||
|
#### 1. Clone the repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://gitea.mineseeker.hu/youruser/mineseeker.git /var/www/mineseeker
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Generate Mercure JWT tokens (run once locally)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer install # only needed for this step
|
||||||
|
make mercure-jwt
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy the three printed values into the `PROD_ENV_FILE` secret.
|
||||||
|
|
||||||
|
#### 3. First deploy
|
||||||
|
|
||||||
|
Trigger it by pushing the first tag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git tag v2026.01
|
||||||
|
git push origin v2026.01
|
||||||
|
```
|
||||||
|
|
||||||
|
This writes `.env`, builds the Docker image, starts all services, runs migrations, and initialises the MinIO buckets automatically via `minio_init`.
|
||||||
|
|
||||||
|
#### 4. Verify
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose ps # all services should be healthy/running
|
||||||
|
docker compose logs app # look for "Starting FrankenPHP"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Releasing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git tag v2026.01
|
||||||
|
git push origin v2026.01
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Game Documentation
|
||||||
|
|
||||||
|
For detailed information about game mechanics, bonus systems, and scoring rules, see the [docs](./docs/) directory:
|
||||||
|
|
||||||
|
- **[Bonus Points System](./docs/game-mechanics/BONUS_POINTS_SYSTEM.md)** — Complete reference for all bonus point types, calculation rules, and implementation details
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
LGPL-3.0 — see [LICENSE](LICENSE) for details.
|
||||||
|
|
||||||
|
© 2026 [SplendidBear](https://www.splendidbear.org)
|
||||||
|
|||||||
10
assets/css/_fontawesome-config.scss
Normal file
10
assets/css/_fontawesome-config.scss
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/*!*
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import '@fortawesome/fontawesome-free/css/all.min.css';
|
||||||
12
assets/css/_fonts-config.scss
Normal file
12
assets/css/_fonts-config.scss
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*!*
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import '@fontsource/rajdhani';
|
||||||
|
@import '@fontsource/changa-one';
|
||||||
|
@import '@fontsource/open-sans/700';
|
||||||
9
assets/css/homepage/_animations.scss
Normal file
9
assets/css/homepage/_animations.scss
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
@keyframes appear {
|
||||||
|
from { opacity: 0; transform: scale(0.94); }
|
||||||
|
to { opacity: 1; transform: scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rise {
|
||||||
|
from { opacity: 0; transform: translateY(16px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
119
assets/css/homepage/_auth-bar.scss
Normal file
119
assets/css/homepage/_auth-bar.scss
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#hero-auth {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.hero-auth {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-auth-user {
|
||||||
|
font: 600 13px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.75);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
i { font-size: 15px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1100px) {
|
||||||
|
.hero-auth {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-auth-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font: 600 12px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 7px 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 200ms ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(255, 255, 255, 0.12);
|
||||||
|
border-color: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--register {
|
||||||
|
color: rgba(149, 207, 245, 0.8);
|
||||||
|
border-color: rgba(35, 111, 135, 0.4);
|
||||||
|
background: rgba(35, 111, 135, 0.12);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(35, 111, 135, 0.28);
|
||||||
|
border-color: rgba(149, 207, 245, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--security {
|
||||||
|
color: rgba(149, 207, 245, 0.55);
|
||||||
|
border-color: rgba(35, 111, 135, 0.22);
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgba(149, 207, 245, 0.9);
|
||||||
|
background: rgba(35, 111, 135, 0.14);
|
||||||
|
border-color: rgba(35, 111, 135, 0.45);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--out {
|
||||||
|
background: transparent;
|
||||||
|
border-color: rgba(173, 10, 5, 0.3);
|
||||||
|
color: rgba(246, 125, 82, 0.7);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(173, 10, 5, 0.15);
|
||||||
|
border-color: rgba(246, 125, 82, 0.5);
|
||||||
|
color: #f67d52;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--profile {
|
||||||
|
color: rgba(149, 207, 245, 0.8);
|
||||||
|
border-color: rgba(35, 111, 135, 0.35);
|
||||||
|
background: rgba(35, 111, 135, 0.08);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(35, 111, 135, 0.22);
|
||||||
|
border-color: rgba(149, 207, 245, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-auth-avatar {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.5);
|
||||||
|
|
||||||
|
&--initials {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: linear-gradient(135deg, rgba(35, 111, 135, 0.45) 0%, rgba(173, 10, 5, 0.3) 100%);
|
||||||
|
font: 800 9px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.9);
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
465
assets/css/homepage/_auth.scss
Normal file
465
assets/css/homepage/_auth.scss
Normal file
@@ -0,0 +1,465 @@
|
|||||||
|
.auth-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 40px 20px 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-flash {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 420px;
|
||||||
|
padding: 12px 18px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font: 600 14px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
&--success {
|
||||||
|
background: rgba(26, 104, 68, 0.25);
|
||||||
|
border: 1px solid rgba(42, 158, 96, 0.4);
|
||||||
|
color: #a0f0c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--error {
|
||||||
|
background: rgba(173, 10, 5, 0.18);
|
||||||
|
border: 1px solid rgba(173, 10, 5, 0.4);
|
||||||
|
color: #f6a090;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Check your inbox" confirmation card
|
||||||
|
.auth-card--sent {
|
||||||
|
text-align: center;
|
||||||
|
padding: 48px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-sent-icon {
|
||||||
|
font-size: 52px;
|
||||||
|
color: rgba(149, 207, 245, 0.6);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
filter: drop-shadow(0 0 16px rgba(35, 111, 135, 0.4));
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-sent-email {
|
||||||
|
font: 700 16px 'Rajdhani', sans-serif;
|
||||||
|
color: #95cff5;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin: 0 0 20px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-sent-note {
|
||||||
|
font: 400 14px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
line-height: 1.7;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
strong { color: rgba(255, 255, 255, 0.75); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.2);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 44px 48px 40px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 420px;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
box-shadow: 0 8px 48px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-title {
|
||||||
|
font: 800 30px 'Rajdhani', sans-serif;
|
||||||
|
color: #ffffff;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-sub {
|
||||||
|
font: 400 14px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.6);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-error {
|
||||||
|
background: rgba(173, 10, 5, 0.18);
|
||||||
|
border: 1px solid rgba(173, 10, 5, 0.4);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
font: 600 13px 'Rajdhani', sans-serif;
|
||||||
|
color: #f6a090;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-label {
|
||||||
|
font: 700 11px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: rgba(255, 255, 255, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-input-wrap {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-input-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 11px;
|
||||||
|
color: rgba(149, 207, 245, 0.4);
|
||||||
|
font-size: 13px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-input {
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.3);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 11px 14px 11px 34px;
|
||||||
|
font: 500 15px 'Rajdhani', sans-serif;
|
||||||
|
color: #ffffff;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
transition: border-color 200ms ease, background 200ms ease;
|
||||||
|
|
||||||
|
&::placeholder { color: rgba(255, 255, 255, 0.2); }
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
background: rgba(35, 111, 135, 0.1);
|
||||||
|
border-color: rgba(149, 207, 245, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--error {
|
||||||
|
border-color: rgba(173, 10, 5, 0.6) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-field-error {
|
||||||
|
font: 500 12px 'Rajdhani', sans-serif;
|
||||||
|
color: #f6a090;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-below-password {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-remember {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font: 500 13px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.45);
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
margin-top: -4px;
|
||||||
|
|
||||||
|
input[type="checkbox"] { accent-color: #236f87; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-checkbox {
|
||||||
|
accent-color: #236f87;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-checkbox-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
font: 400 14px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #95cff5;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: color 180ms;
|
||||||
|
|
||||||
|
&:hover { color: #c5e8ff; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.auth-input {
|
||||||
|
padding: 11px 14px;
|
||||||
|
min-height: 120px;
|
||||||
|
resize: vertical;
|
||||||
|
font-family: 'Rajdhani', sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-submit {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: linear-gradient(to bottom, #ad0a05 0%, #d4401a 55%, #f67d52 100%);
|
||||||
|
border: 1px solid rgba(246, 125, 82, 0.3);
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #ffffff;
|
||||||
|
font: 700 16px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
padding: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 6px;
|
||||||
|
box-shadow: 0 4px 20px rgba(173, 10, 5, 0.35);
|
||||||
|
transition: all 220ms ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(to bottom, #c91008 0%, #e5521e 55%, #ff8c61 100%);
|
||||||
|
box-shadow: 0 6px 28px rgba(173, 10, 5, 0.6);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active { transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-cancel {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: linear-gradient(to bottom, #1a1a1a 0%, #2d2d2d 55%, #404040 100%);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #ffffff;
|
||||||
|
font: 700 16px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
padding: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 6px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.35);
|
||||||
|
transition: all 220ms ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(to bottom, #2d2d2d 0%, #3d3d3d 55%, #505050 100%);
|
||||||
|
box-shadow: 0 6px 28px rgba(0, 0, 0, 0.6);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active { transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
width: 100%;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
form {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&.auth-form {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-cancel-form {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.auth-cancel {
|
||||||
|
flex: 1;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-cancel-standalone {
|
||||||
|
margin-top: 12px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-cancel--block {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-switch {
|
||||||
|
font: 400 13px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.35);
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 24px;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #95cff5;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: color 180ms;
|
||||||
|
|
||||||
|
&:hover { color: #c5e8ff; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-forgot-password {
|
||||||
|
font: 400 13px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.35);
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #95cff5;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: color 180ms;
|
||||||
|
|
||||||
|
&:hover { color: #c5e8ff; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-divider {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 20px 0;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-divider::before,
|
||||||
|
.auth-divider::after {
|
||||||
|
content: '';
|
||||||
|
flex: 1;
|
||||||
|
height: 1px;
|
||||||
|
background: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-divider span {
|
||||||
|
margin: 0 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-passkey-btn {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-passkey-btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-passkey-btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-input--code {
|
||||||
|
font: 700 22px 'Courier New', monospace;
|
||||||
|
letter-spacing: 6px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-field-hint {
|
||||||
|
font: 400 12px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.45);
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
margin: 6px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-card--wide {
|
||||||
|
max-width: 520px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-back {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
a {
|
||||||
|
font: 500 13px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.5);
|
||||||
|
text-decoration: none;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
transition: color 180ms;
|
||||||
|
|
||||||
|
&:hover { color: rgba(149, 207, 245, 0.9); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.twofa-setup {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
&__qr {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 2px solid rgba(35, 111, 135, 0.3);
|
||||||
|
background: #fff;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__manual {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__manual-label {
|
||||||
|
font: 400 12px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.45);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin: 0 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__secret {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 14px;
|
||||||
|
background: rgba(0, 0, 0, 0.35);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.3);
|
||||||
|
border-radius: 4px;
|
||||||
|
font: 700 14px 'Courier New', monospace;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
color: #95cff5;
|
||||||
|
word-break: break-all;
|
||||||
|
user-select: all;
|
||||||
|
}
|
||||||
|
}
|
||||||
43
assets/css/homepage/_content.scss
Normal file
43
assets/css/homepage/_content.scss
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
main div.txt {
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 60px 40px 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main div.txt h2 {
|
||||||
|
font: bold 28px 'Rajdhani', sans-serif;
|
||||||
|
color: #ffffff;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main div.txt h3 {
|
||||||
|
font: bold 17px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.9);
|
||||||
|
margin: 28px 0 10px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main div.txt p,
|
||||||
|
main div.txt li {
|
||||||
|
font: 400 15px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.72);
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
main div.txt a {
|
||||||
|
color: #95cff5;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 180ms;
|
||||||
|
|
||||||
|
&:hover { color: #c5e8ff; }
|
||||||
|
}
|
||||||
|
|
||||||
|
main div.txt img {
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main div.txt .img-container {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
75
assets/css/homepage/_cta.scss
Normal file
75
assets/css/homepage/_cta.scss
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
.hero-cta {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
font: 800 28px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 6px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 22px 100px 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid rgba(246, 125, 82, 0.25);
|
||||||
|
|
||||||
|
background: linear-gradient(to bottom, #b30c06 0%, #d63d15 50%, #f67d52 100%);
|
||||||
|
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px rgba(173, 10, 5, 0.2),
|
||||||
|
0 0 30px rgba(173, 10, 5, 0.35),
|
||||||
|
0 6px 24px rgba(0, 0, 0, 0.5),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.12);
|
||||||
|
|
||||||
|
transition: transform 220ms ease, box-shadow 220ms ease, letter-spacing 220ms ease;
|
||||||
|
animation: rise 0.8s 0.42s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outer glow layer (blurred duplicate, always visible)
|
||||||
|
.hero-cta::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: -4px;
|
||||||
|
border-radius: 7px;
|
||||||
|
background: linear-gradient(to bottom, #ad0a05, #f67d52);
|
||||||
|
filter: blur(18px);
|
||||||
|
opacity: 0.3;
|
||||||
|
z-index: -1;
|
||||||
|
transition: opacity 220ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-cta:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
letter-spacing: 8px;
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px rgba(246, 125, 82, 0.3),
|
||||||
|
0 0 50px rgba(173, 10, 5, 0.65),
|
||||||
|
0 10px 32px rgba(0, 0, 0, 0.45),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-cta:hover::before {
|
||||||
|
opacity: 0.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-cta:active {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version / copyright line
|
||||||
|
.hero-meta {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
font: 400 12px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin-top: 58px;
|
||||||
|
animation: rise 0.8s 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-meta a {
|
||||||
|
color: rgba(149, 207, 245, 0.65);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 180ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-meta a:hover {
|
||||||
|
color: #95cff5;
|
||||||
|
}
|
||||||
72
assets/css/homepage/_donate.scss
Normal file
72
assets/css/homepage/_donate.scss
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/*!*
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.hero-donate-text {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
font: 400 12px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin-top: 42px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
animation: rise 0.8s 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-donate {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
font: 500 12px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: rgba(255, 184, 82, 0.75);
|
||||||
|
padding: 6px 14px;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px solid rgba(255, 184, 82, 0.25);
|
||||||
|
|
||||||
|
background: rgba(255, 140, 30, 0.05);
|
||||||
|
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px rgba(255, 140, 30, 0.1),
|
||||||
|
0 0 8px rgba(255, 140, 30, 0.08);
|
||||||
|
|
||||||
|
transition: transform 200ms ease, box-shadow 200ms ease, color 200ms ease, background 200ms ease;
|
||||||
|
animation: rise 0.8s 0.58s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-donate::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: -2px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: rgba(255, 140, 30, 0.15);
|
||||||
|
filter: blur(8px);
|
||||||
|
opacity: 0.1;
|
||||||
|
z-index: -1;
|
||||||
|
transition: opacity 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-donate:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
color: rgba(255, 200, 100, 0.9);
|
||||||
|
background: rgba(255, 140, 30, 0.1);
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px rgba(255, 140, 30, 0.2),
|
||||||
|
0 0 12px rgba(255, 140, 30, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-donate:hover::before {
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-donate:active {
|
||||||
|
transform: translateY(0px);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
354
assets/css/homepage/_features.scss
Normal file
354
assets/css/homepage/_features.scss
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
.feature-block {
|
||||||
|
width: 100%;
|
||||||
|
padding: 80px 40px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:first-of-type {
|
||||||
|
border-top: 1px solid rgba(35, 111, 135, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
& + & {
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-block__inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 80px;
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-block--reverse .feature-block__inner {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visual side
|
||||||
|
.feature-block__visual {
|
||||||
|
flex: 0 0 340px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats icons cluster
|
||||||
|
.feature-block__visual--stats {
|
||||||
|
height: 260px;
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
|
i {
|
||||||
|
position: absolute;
|
||||||
|
color: rgba(35, 111, 135, 0.5);
|
||||||
|
transition: color 300ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bar chart — large, centre
|
||||||
|
i.fa-chart-bar {
|
||||||
|
font-size: 160px;
|
||||||
|
color: rgba(35, 111, 135, 0.5);
|
||||||
|
filter: drop-shadow(0 0 40px rgba(35, 111, 135, 0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trophy — top right
|
||||||
|
i.fa-trophy {
|
||||||
|
font-size: 80px;
|
||||||
|
top: 0px;
|
||||||
|
right: 20px;
|
||||||
|
color: rgba(246, 125, 82, 0.7);
|
||||||
|
filter: drop-shadow(0 0 25px rgba(246, 125, 82, 0.4));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clock history — bottom left
|
||||||
|
i.fa-clock-rotate-left {
|
||||||
|
font-size: 68px;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 20px;
|
||||||
|
color: rgba(149, 207, 245, 0.65);
|
||||||
|
filter: drop-shadow(0 0 20px rgba(149, 207, 245, 0.35));
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover i.fa-chart-bar { color: rgba(35, 111, 135, 0.8); filter: drop-shadow(0 0 50px rgba(35, 111, 135, 0.7)); }
|
||||||
|
&:hover i.fa-trophy { color: rgba(246, 125, 82, 0.9); filter: drop-shadow(0 0 35px rgba(246, 125, 82, 0.6)); }
|
||||||
|
&:hover i.fa-clock-rotate-left { color: rgba(149, 207, 245, 0.9); filter: drop-shadow(0 0 30px rgba(149, 207, 245, 0.5)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MSN visual
|
||||||
|
.feature-block__visual--msn {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msn-logo {
|
||||||
|
width: 90px;
|
||||||
|
height: 90px;
|
||||||
|
object-fit: contain;
|
||||||
|
filter: drop-shadow(0 0 18px rgba(149, 207, 245, 0.3)) brightness(1.1);
|
||||||
|
flex-shrink: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msn-screenshot {
|
||||||
|
width: 340px;
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
box-shadow:
|
||||||
|
0 8px 40px rgba(0, 0, 0, 0.6),
|
||||||
|
0 0 0 1px rgba(35, 111, 135, 0.12);
|
||||||
|
filter: saturate(0.85) brightness(0.9);
|
||||||
|
transition: filter 300ms ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: saturate(1) brightness(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Privacy visual
|
||||||
|
.feature-block__visual--privacy {
|
||||||
|
height: 260px;
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
|
i {
|
||||||
|
position: absolute;
|
||||||
|
color: rgba(35, 111, 135, 0.5);
|
||||||
|
transition: color 300ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shield — centre, large
|
||||||
|
i.fa-shield {
|
||||||
|
font-size: 140px;
|
||||||
|
color: rgba(34, 197, 94, 0.3);
|
||||||
|
filter: drop-shadow(0 0 30px rgba(34, 197, 94, 0.25));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock — top right
|
||||||
|
i.fa-lock {
|
||||||
|
font-size: 56px;
|
||||||
|
top: 20px;
|
||||||
|
right: 35px;
|
||||||
|
color: rgba(168, 85, 247, 0.5);
|
||||||
|
filter: drop-shadow(0 0 16px rgba(168, 85, 247, 0.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eye slash — bottom left
|
||||||
|
i.fa-eye-slash {
|
||||||
|
font-size: 48px;
|
||||||
|
bottom: 28px;
|
||||||
|
left: 40px;
|
||||||
|
color: rgba(59, 130, 246, 0.5);
|
||||||
|
filter: drop-shadow(0 0 12px rgba(59, 130, 246, 0.15));
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover i.fa-shield { color: rgba(34, 197, 94, 0.6); }
|
||||||
|
&:hover i.fa-lock { color: rgba(168, 85, 247, 0.75); }
|
||||||
|
&:hover i.fa-eye-slash { color: rgba(59, 130, 246, 0.7); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Practice visual
|
||||||
|
.feature-block__visual--practice {
|
||||||
|
height: 260px;
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
|
i {
|
||||||
|
position: absolute;
|
||||||
|
color: rgba(35, 111, 135, 0.5);
|
||||||
|
transition: color 300ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Laptop — centre, large
|
||||||
|
i.fa-laptop {
|
||||||
|
font-size: 140px;
|
||||||
|
color: rgba(251, 146, 60, 0.3);
|
||||||
|
filter: drop-shadow(0 0 30px rgba(251, 146, 60, 0.25));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linux — top left
|
||||||
|
i.fa-linux {
|
||||||
|
font-size: 48px;
|
||||||
|
top: 20px;
|
||||||
|
left: 35px;
|
||||||
|
color: rgba(245, 158, 11, 0.5);
|
||||||
|
filter: drop-shadow(0 0 16px rgba(245, 158, 11, 0.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apple — top right
|
||||||
|
i.fa-apple {
|
||||||
|
font-size: 56px;
|
||||||
|
top: 20px;
|
||||||
|
right: 35px;
|
||||||
|
color: rgba(156, 163, 175, 0.5);
|
||||||
|
filter: drop-shadow(0 0 16px rgba(156, 163, 175, 0.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows — bottom left
|
||||||
|
i.fa-windows {
|
||||||
|
font-size: 48px;
|
||||||
|
bottom: 28px;
|
||||||
|
left: 40px;
|
||||||
|
color: rgba(59, 130, 246, 0.5);
|
||||||
|
filter: drop-shadow(0 0 12px rgba(59, 130, 246, 0.15));
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover i.fa-laptop { color: rgba(251, 146, 60, 0.6); }
|
||||||
|
&:hover i.fa-linux { color: rgba(245, 158, 11, 0.75); }
|
||||||
|
&:hover i.fa-apple { color: rgba(156, 163, 175, 0.75); }
|
||||||
|
&:hover i.fa-windows { color: rgba(59, 130, 246, 0.7); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text side
|
||||||
|
.feature-block__text {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-block__label {
|
||||||
|
font: 700 11px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
color: rgba(149, 207, 245, 0.55);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-block__title {
|
||||||
|
font: 800 40px 'Rajdhani', sans-serif;
|
||||||
|
color: #ffffff;
|
||||||
|
line-height: 1.1;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-block__body {
|
||||||
|
font: 400 16px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.62);
|
||||||
|
line-height: 1.8;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-block__cta {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 28px;
|
||||||
|
font: 700 14px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: rgba(149, 207, 245, 0.85);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.4);
|
||||||
|
background: rgba(35, 111, 135, 0.1);
|
||||||
|
padding: 11px 24px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 200ms ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(35, 111, 135, 0.25);
|
||||||
|
border-color: rgba(149, 207, 245, 0.55);
|
||||||
|
color: #fff;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 16px rgba(35, 111, 135, 0.25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.practice-links {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.practice-link {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
font: 700 13px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: rgba(149, 207, 245, 0.85);
|
||||||
|
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||||
|
background: rgba(59, 130, 246, 0.08);
|
||||||
|
padding: 10px 18px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 200ms ease;
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(59, 130, 246, 0.2);
|
||||||
|
border-color: rgba(59, 130, 246, 0.6);
|
||||||
|
color: #fff;
|
||||||
|
transform: translateX(4px);
|
||||||
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.practice-link-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 8px;
|
||||||
|
object-fit: contain;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
padding: 4px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: all 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.practice-link:hover .practice-link-icon {
|
||||||
|
background: rgba(59, 130, 246, 0.15);
|
||||||
|
border-color: rgba(59, 130, 246, 0.4);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 900px) {
|
||||||
|
.feature-block__inner,
|
||||||
|
.feature-block--reverse .feature-block__inner {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 48px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-block__visual {
|
||||||
|
flex: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-block__visual--stats {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-block__visual--msn {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msn-screenshot {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-block__label,
|
||||||
|
.feature-block__cta {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-block__title { font-size: 32px; }
|
||||||
|
|
||||||
|
.feature-block {
|
||||||
|
padding: 60px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.practice-links {
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.practice-link {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
112
assets/css/homepage/_footer.scss
Normal file
112
assets/css/homepage/_footer.scss
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
footer {
|
||||||
|
background: #040608;
|
||||||
|
border-top: 1px solid rgba(35, 111, 135, 0.12);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 60px 60px 52px;
|
||||||
|
gap: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-brand {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-logo {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
opacity: 0.55;
|
||||||
|
filter:
|
||||||
|
drop-shadow(0 0 12px rgba(35, 111, 135, 0.4))
|
||||||
|
brightness(1.1);
|
||||||
|
transition: opacity 250ms ease, filter 250ms ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
filter:
|
||||||
|
drop-shadow(0 0 20px rgba(35, 111, 135, 0.65))
|
||||||
|
brightness(1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-name {
|
||||||
|
font: 700 22px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.75);
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-tagline {
|
||||||
|
font: 400 13px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.7);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
max-width: 240px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-nav-label {
|
||||||
|
font: 700 11px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
margin-bottom: 18px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-nav ul {
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-nav ul li a {
|
||||||
|
display: block;
|
||||||
|
font: 500 15px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 6px 0;
|
||||||
|
transition: color 180ms ease, letter-spacing 180ms ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #95cff5;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-copy {
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
padding: 16px 60px;
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font: 400 11px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.45);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgba(149, 207, 245, 0.6);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 180ms;
|
||||||
|
|
||||||
|
&:hover { color: #95cff5; }
|
||||||
|
}
|
||||||
|
}
|
||||||
39
assets/css/homepage/_header.scss
Normal file
39
assets/css/homepage/_header.scss
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
header {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
// Minesweeper grid texture
|
||||||
|
background-color: #07090d;
|
||||||
|
background-image:
|
||||||
|
linear-gradient(rgba(35, 111, 135, 0.1) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(35, 111, 135, 0.1) 1px, transparent 1px);
|
||||||
|
background-size: 46px 46px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deep radial vignette – grid fades toward the centre
|
||||||
|
header::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: radial-gradient(
|
||||||
|
ellipse 85% 75% at 50% 50%,
|
||||||
|
#07090d 10%,
|
||||||
|
transparent 75%
|
||||||
|
);
|
||||||
|
z-index: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smoke at the bottom so header bleeds into body
|
||||||
|
header::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 160px;
|
||||||
|
background: linear-gradient(to bottom, transparent, #07090d);
|
||||||
|
z-index: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
49
assets/css/homepage/_hero-compact.scss
Normal file
49
assets/css/homepage/_hero-compact.scss
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
.hero--compact {
|
||||||
|
min-height: unset;
|
||||||
|
padding: 36px 60px 48px;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: left;
|
||||||
|
gap: 52px;
|
||||||
|
|
||||||
|
&::before, &::after { display: none; }
|
||||||
|
|
||||||
|
.hero-logo {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
img { width: 180px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-body {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-sub {
|
||||||
|
font-size: 14px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 26px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-cta {
|
||||||
|
padding: 12px 52px 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-meta {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also shrink the bottom fade on sub-pages
|
||||||
|
header:has(.hero--compact)::after {
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
90
assets/css/homepage/_hero.scss
Normal file
90
assets/css/homepage/_hero.scss
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
.hero {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 80px 40px 160px;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decorative glow blobs in opposite corners
|
||||||
|
.hero::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -60px;
|
||||||
|
left: -60px;
|
||||||
|
width: 420px;
|
||||||
|
height: 420px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: radial-gradient(circle, rgba(173, 10, 5, 0.09) 0%, transparent 65%);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 100px;
|
||||||
|
right: -60px;
|
||||||
|
width: 380px;
|
||||||
|
height: 380px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: radial-gradient(circle, rgba(35, 111, 135, 0.1) 0%, transparent 65%);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logo
|
||||||
|
.hero-logo {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 72px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
animation: appear 0.9s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-logo img {
|
||||||
|
width: 400px;
|
||||||
|
max-width: 82vw;
|
||||||
|
filter:
|
||||||
|
drop-shadow(0 0 40px rgba(35, 111, 135, 0.35))
|
||||||
|
drop-shadow(0 6px 20px rgba(0, 0, 0, 0.8));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body text block
|
||||||
|
.hero-body {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-sub {
|
||||||
|
font: 300 17px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.9);
|
||||||
|
letter-spacing: 3px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
animation: rise 0.8s 0.15s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-sub strong {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #95cff5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
font: 800 58px 'Rajdhani', sans-serif;
|
||||||
|
color: #ffffff;
|
||||||
|
line-height: 1.1;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin-bottom: 56px;
|
||||||
|
text-shadow: 0 4px 30px rgba(0, 0, 0, 0.7);
|
||||||
|
animation: rise 0.8s 0.28s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||||
|
}
|
||||||
996
assets/css/homepage/_profile.scss
Normal file
996
assets/css/homepage/_profile.scss
Normal file
@@ -0,0 +1,996 @@
|
|||||||
|
/*!*
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.profile-page {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 48px 24px 80px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 28px;
|
||||||
|
padding: 32px 36px;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.2);
|
||||||
|
border-radius: 10px;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
box-shadow: 0 8px 48px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-avatar {
|
||||||
|
position: relative;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, rgba(35, 111, 135, 0.45) 0%, rgba(173, 10, 5, 0.3) 100%);
|
||||||
|
border: 2px solid rgba(35, 111, 135, 0.45);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font: 800 28px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.9);
|
||||||
|
letter-spacing: 2px;
|
||||||
|
box-shadow: 0 0 0 4px rgba(35, 111, 135, 0.08), 0 0 24px rgba(35, 111, 135, 0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: box-shadow 200ms ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 0 0 4px rgba(35, 111, 135, 0.18), 0 0 32px rgba(35, 111, 135, 0.4);
|
||||||
|
|
||||||
|
.profile-avatar__overlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--loading {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__initials {
|
||||||
|
font: 800 28px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.9);
|
||||||
|
letter-spacing: 2px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(0, 0, 0, 0.55);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 200ms ease;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-name {
|
||||||
|
font: 800 32px 'Rajdhani', sans-serif;
|
||||||
|
color: #ffffff;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-email {
|
||||||
|
font: 400 14px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.6);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 11px;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-role {
|
||||||
|
font: 600 11px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2.5px;
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stats {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px 16px 20px;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
transition: border-color 200ms ease, background 200ms ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-color: rgba(35, 111, 135, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--win {
|
||||||
|
border-color: rgba(42, 158, 96, 0.18);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(42, 158, 96, 0.45);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--loss {
|
||||||
|
border-color: rgba(173, 10, 5, 0.18);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(173, 10, 5, 0.45);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--bomb {
|
||||||
|
border-color: rgba(246, 125, 82, 0.18);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(246, 125, 82, 0.45);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--draw {
|
||||||
|
border-color: rgba(149, 207, 245, 0.15);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(149, 207, 245, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--rate {
|
||||||
|
border-color: rgba(168, 130, 255, 0.18);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(168, 130, 255, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--avg {
|
||||||
|
border-color: rgba(80, 200, 220, 0.18);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(80, 200, 220, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--best {
|
||||||
|
border-color: rgba(255, 215, 0, 0.15);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(255, 215, 0, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat__icon {
|
||||||
|
font-size: 18px;
|
||||||
|
color: rgba(255, 255, 255, 0.15);
|
||||||
|
margin-bottom: 2px;
|
||||||
|
|
||||||
|
.profile-stat--win & {
|
||||||
|
color: rgba(94, 232, 154, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat--loss & {
|
||||||
|
color: rgba(246, 125, 82, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat--bomb & {
|
||||||
|
color: rgba(246, 125, 82, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat--draw & {
|
||||||
|
color: rgba(149, 207, 245, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat--rate & {
|
||||||
|
color: rgba(168, 130, 255, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat--avg & {
|
||||||
|
color: rgba(80, 200, 220, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat--best & {
|
||||||
|
color: rgba(255, 215, 0, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat__value {
|
||||||
|
display: block;
|
||||||
|
font: 800 40px 'Rajdhani', sans-serif;
|
||||||
|
color: #ffffff;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
.profile-stat--win & {
|
||||||
|
color: #5ee89a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat--loss & {
|
||||||
|
color: #f67d52;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat--bomb & {
|
||||||
|
color: rgba(246, 125, 82, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat--draw & {
|
||||||
|
color: #95cff5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat--rate & {
|
||||||
|
color: #c8a8ff;
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.55em;
|
||||||
|
opacity: 0.7;
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat--avg & {
|
||||||
|
color: #50c8dc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat--best & {
|
||||||
|
color: #ffd700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stat__label {
|
||||||
|
font: 600 10px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-action-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 9px 18px;
|
||||||
|
background: rgba(35, 111, 135, 0.12);
|
||||||
|
color: rgba(149, 207, 245, 0.75);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.3);
|
||||||
|
border-radius: 6px;
|
||||||
|
text-decoration: none;
|
||||||
|
font: 600 12px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: background 200ms ease, border-color 200ms ease, color 200ms ease;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 11px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(35, 111, 135, 0.22);
|
||||||
|
border-color: rgba(35, 111, 135, 0.55);
|
||||||
|
color: rgba(149, 207, 245, 1);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-section__description {
|
||||||
|
font-size: 13px;
|
||||||
|
color: rgba(149, 207, 245, 0.65);
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-section__title {
|
||||||
|
font: 700 18px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
color: rgba(149, 207, 245, 0.45);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-games {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-game {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 26px 76px 22px 1fr 18px auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 11px 16px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: rgba(255, 255, 255, 0.025);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.055);
|
||||||
|
border-left-width: 3px;
|
||||||
|
font: 500 14px 'Rajdhani', sans-serif;
|
||||||
|
transition: background 180ms ease, border-color 180ms ease;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.055);
|
||||||
|
border-color: rgba(35, 111, 135, 0.35);
|
||||||
|
border-left-width: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--win {
|
||||||
|
border-left-color: rgba(42, 158, 96, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--loss {
|
||||||
|
border-left-color: rgba(173, 10, 5, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--draw {
|
||||||
|
border-left-color: rgba(149, 207, 245, 0.25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-game__badge {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font: 800 10px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 0;
|
||||||
|
|
||||||
|
.profile-game--win & {
|
||||||
|
background: rgba(42, 158, 96, 0.18);
|
||||||
|
color: #5ee89a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-game--loss & {
|
||||||
|
background: rgba(173, 10, 5, 0.18);
|
||||||
|
color: #f67d52;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-game--draw & {
|
||||||
|
background: rgba(149, 207, 245, 0.1);
|
||||||
|
color: rgba(149, 207, 245, 0.65);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-game__score {
|
||||||
|
font: 700 14px 'Rajdhani', sans-serif;
|
||||||
|
color: #fff;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-game__vs {
|
||||||
|
font: 400 10px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.22);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-game__opponent {
|
||||||
|
color: rgba(149, 207, 245, 0.7);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-game__color {
|
||||||
|
font-size: 10px;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-game__date {
|
||||||
|
font: 400 11px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-charts {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-chart-block {
|
||||||
|
flex: 1 1 300px;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.2);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 24px 20px 16px;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
box-shadow: 0 8px 48px rgba(0, 0, 0, 0.4);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.profile-section__title {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-chart-inner {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
svg text {
|
||||||
|
font-family: 'Rajdhani', sans-serif !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 24px 24px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-header-left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-label {
|
||||||
|
font: 600 10px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
color: rgba(149, 207, 245, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-title {
|
||||||
|
font: 800 22px 'Rajdhani', sans-serif;
|
||||||
|
color: #fff;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: rgba(149, 207, 245, 0.5);
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-share {
|
||||||
|
background: rgba(35, 111, 135, 0.12);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.35);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: rgba(149, 207, 245, 0.7);
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font: 600 11px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
transition: all 180ms ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(35, 111, 135, 0.22);
|
||||||
|
color: #95cff5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--copied {
|
||||||
|
background: rgba(42, 158, 96, 0.15);
|
||||||
|
border-color: rgba(42, 158, 96, 0.4);
|
||||||
|
color: #5ee89a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-close {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: rgba(255, 255, 255, 0.4);
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 180ms ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-vs-panel {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 28px 32px 24px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-vs-center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-vs-label {
|
||||||
|
font: 800 11px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-vs-score {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
&__red, &__blue {
|
||||||
|
font: 800 52px 'Rajdhani', sans-serif;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__red { color: #f67d52; text-shadow: 0 0 24px rgba(173, 10, 5, 0.5); }
|
||||||
|
&__blue { color: #95cff5; text-shadow: 0 0 24px rgba(35, 111, 135, 0.5); }
|
||||||
|
|
||||||
|
&__sep {
|
||||||
|
font: 800 32px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.2);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-result-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 5px 14px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font: 700 12px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-stats {
|
||||||
|
padding: 0 24px 24px;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
margin-top: 4px;
|
||||||
|
padding-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 48px 20px;
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 40px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font: 400 15px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #95cff5;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: color 180ms;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #c5e8ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-page {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 48px 20px 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 560px;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.25);
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 8px 64px rgba(0, 0, 0, 0.6);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
|
||||||
|
&__eyebrow {
|
||||||
|
padding: 18px 28px 0;
|
||||||
|
font: 600 10px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
color: rgba(149, 207, 245, 0.4);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-vs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 24px 28px;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-player {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
font: 700 16px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 120px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__side {
|
||||||
|
font: 600 10px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--red .bshare-player__name { color: #f67d52; }
|
||||||
|
&--blue .bshare-player__name { color: #95cff5; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-avatar {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font: 800 24px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
|
||||||
|
&__img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--red {
|
||||||
|
background: linear-gradient(135deg, rgba(173, 10, 5, 0.6) 0%, rgba(246, 125, 82, 0.4) 100%);
|
||||||
|
border: 2px solid rgba(173, 10, 5, 0.5);
|
||||||
|
box-shadow: 0 0 0 3px rgba(173, 10, 5, 0.12), 0 0 28px rgba(173, 10, 5, 0.3);
|
||||||
|
color: #f67d52;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--blue {
|
||||||
|
background: linear-gradient(135deg, rgba(35, 111, 135, 0.6) 0%, rgba(41, 128, 185, 0.4) 100%);
|
||||||
|
border: 2px solid rgba(35, 111, 135, 0.5);
|
||||||
|
box-shadow: 0 0 0 3px rgba(35, 111, 135, 0.12), 0 0 28px rgba(35, 111, 135, 0.3);
|
||||||
|
color: #95cff5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-vs__center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-vs__label {
|
||||||
|
font: 800 11px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
color: rgba(255, 255, 255, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-score {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
&__red, &__blue {
|
||||||
|
font: 800 56px 'Rajdhani', sans-serif;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__red { color: #f67d52; text-shadow: 0 0 24px rgba(173, 10, 5, 0.5); }
|
||||||
|
&__blue { color: #95cff5; text-shadow: 0 0 24px rgba(35, 111, 135, 0.5); }
|
||||||
|
|
||||||
|
&__sep {
|
||||||
|
font: 800 32px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.18);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--na {
|
||||||
|
font: 800 40px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 5px 14px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font: 700 12px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
|
||||||
|
&--red { background: rgba(173, 10, 5, 0.15); border: 1px solid rgba(173, 10, 5, 0.4); color: #f67d52; }
|
||||||
|
&--blue { background: rgba(35, 111, 135, 0.15); border: 1px solid rgba(35, 111, 135, 0.4); color: #95cff5; }
|
||||||
|
&--draw { background: rgba(149, 207, 245, 0.08); border: 1px solid rgba(149, 207, 245, 0.3); color: #95cff5; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-details {
|
||||||
|
padding: 16px 28px;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-detail {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
font: 500 13px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.45);
|
||||||
|
|
||||||
|
i {
|
||||||
|
width: 14px;
|
||||||
|
color: rgba(149, 207, 245, 0.4);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--bomb {
|
||||||
|
color: rgba(246, 125, 82, 0.7);
|
||||||
|
|
||||||
|
i { color: rgba(246, 125, 82, 0.5); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-cta {
|
||||||
|
padding: 20px 28px 28px;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-bonus {
|
||||||
|
padding: 28px 28px 0;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-bonus__title {
|
||||||
|
font: 700 13px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: #ffd700;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
i { font-size: 14px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-bonus__grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-bonus__player {
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
|
||||||
|
&--red {
|
||||||
|
border-color: rgba(246, 125, 82, 0.15);
|
||||||
|
background: rgba(246, 125, 82, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--blue {
|
||||||
|
border-color: rgba(149, 207, 245, 0.15);
|
||||||
|
background: rgba(149, 207, 245, 0.04);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-bonus__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-bonus__points {
|
||||||
|
font: 700 24px 'Rajdhani', sans-serif;
|
||||||
|
background: linear-gradient(135deg, #ffd700, #ffed4e);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-bonus__label {
|
||||||
|
font: 700 11px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: rgba(255, 215, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-bonus__stats {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-bonus__stat {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-bonus__stat-label {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
font: 500 11px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-bonus__stat-value {
|
||||||
|
font: 700 13px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 215, 0, 0.9);
|
||||||
|
min-width: 24px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-bonus__stat--empty {
|
||||||
|
color: rgba(255, 255, 255, 0.4);
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bshare-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 11px 20px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font: 700 13px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 200ms ease;
|
||||||
|
|
||||||
|
background: linear-gradient(to bottom, #ad0a05 0%, #d4401a 55%, #f67d52 100%);
|
||||||
|
border: 1px solid rgba(246, 125, 82, 0.3);
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 4px 20px rgba(173, 10, 5, 0.3);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 6px 28px rgba(173, 10, 5, 0.55);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--ghost {
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
assets/css/homepage/_reset.scss
Normal file
23
assets/css/homepage/_reset.scss
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
* {
|
||||||
|
outline: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
// Grid lives on html so it tiles across all pages including content pages
|
||||||
|
background-color: #07090d;
|
||||||
|
background-image:
|
||||||
|
linear-gradient(rgba(35, 111, 135, 0.1) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(35, 111, 135, 0.1) 1px, transparent 1px);
|
||||||
|
background-size: 46px 46px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: transparent;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
100
assets/css/homepage/_responsive.scss
Normal file
100
assets/css/homepage/_responsive.scss
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
@media screen and (max-width: 900px) {
|
||||||
|
.hero h1 {
|
||||||
|
font-size: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-cta {
|
||||||
|
padding: 20px 72px 18px;
|
||||||
|
font-size: 24px;
|
||||||
|
letter-spacing: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.hero--compact {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
padding: 36px 24px 44px;
|
||||||
|
gap: 28px;
|
||||||
|
|
||||||
|
.hero-body { align-items: center; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-stats {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-header {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
padding: 28px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-email,
|
||||||
|
.profile-role {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-game {
|
||||||
|
grid-template-columns: 26px 64px 18px 1fr 14px;
|
||||||
|
|
||||||
|
.profile-game__date { display: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-inner {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
padding: 48px 30px 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-brand {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-tagline {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-nav-label {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-nav ul {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-copy {
|
||||||
|
padding: 16px 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 550px) {
|
||||||
|
.hero {
|
||||||
|
padding: 60px 24px 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-logo img {
|
||||||
|
width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-logo {
|
||||||
|
margin-bottom: 52px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
font-size: 32px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-sub {
|
||||||
|
font-size: 14px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-cta {
|
||||||
|
padding: 18px 48px 16px;
|
||||||
|
font-size: 20px;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
60
assets/css/homepage/_tech.scss
Normal file
60
assets/css/homepage/_tech.scss
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
main {
|
||||||
|
background: #07090d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-section {
|
||||||
|
padding: 48px 20px 72px;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-label {
|
||||||
|
font: 600 11px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 6px;
|
||||||
|
color: rgba(255, 255, 255, 0.14);
|
||||||
|
margin-bottom: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-link {
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-logos img {
|
||||||
|
display: inline-block;
|
||||||
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
|
object-fit: contain;
|
||||||
|
margin: 8px 24px;
|
||||||
|
// Force all logos to white, then tint with the game's blue on hover
|
||||||
|
filter: brightness(0) invert(1) opacity(0.35);
|
||||||
|
transition: filter 220ms ease, transform 220ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-logos img:hover {
|
||||||
|
filter:
|
||||||
|
brightness(0) invert(1)
|
||||||
|
sepia(1) saturate(3) hue-rotate(175deg) brightness(1.1)
|
||||||
|
opacity(0.9);
|
||||||
|
transform: translateY(-4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-oss {
|
||||||
|
margin-top: 36px;
|
||||||
|
font: 400 15px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
max-width: 680px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
padding: 0 24px;
|
||||||
|
line-height: 1.7;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: rgba(220, 60, 50, 0.85);
|
||||||
|
margin-right: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
assets/css/mineseeker/_back-button.scss
Normal file
24
assets/css/mineseeker/_back-button.scss
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
.back-from-game {
|
||||||
|
display: inline-block;
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
left: 20px;
|
||||||
|
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
-webkit-transition: all 250ms cubic-bezier(.17, .67, .83, .67);
|
||||||
|
transition: all 250ms cubic-bezier(.17, .67, .83, .67);
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-from-game img {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-from-game:hover {
|
||||||
|
-ms-transform: scale(1.2);
|
||||||
|
-webkit-transform: scale(1.2);
|
||||||
|
transform: scale(1.2);
|
||||||
|
-webkit-transition: all 250ms cubic-bezier(.17, .67, .83, .67);
|
||||||
|
transition: all 250ms cubic-bezier(.17, .67, .83, .67);
|
||||||
|
}
|
||||||
56
assets/css/mineseeker/_base.scss
Normal file
56
assets/css/mineseeker/_base.scss
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mine-container {
|
||||||
|
background-size: cover;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear {
|
||||||
|
clear: both
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper,
|
||||||
|
#mine-wrapper * {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper {
|
||||||
|
display: table;
|
||||||
|
width: 842px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper {
|
||||||
|
background: #000;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
-webkit-border-radius: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
225
assets/css/mineseeker/_bomb.scss
Normal file
225
assets/css/mineseeker/_bomb.scss
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-control {
|
||||||
|
background: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.5) 0%, rgba(125, 185, 232, 0) 100%);
|
||||||
|
background: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.5) 0%, rgba(125, 185, 232, 0) 100%);
|
||||||
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.5) 0%, rgba(125, 185, 232, 0) 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#007db9e8', GradientType=1);
|
||||||
|
position: relative;
|
||||||
|
padding: 5px;
|
||||||
|
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-control > img {
|
||||||
|
position: absolute;
|
||||||
|
width: 55px;
|
||||||
|
left: -5px;
|
||||||
|
bottom: 10px;
|
||||||
|
|
||||||
|
-ms-transform: rotate(-15deg);
|
||||||
|
-webkit-transform: rotate(-15deg);
|
||||||
|
transform: rotate(-15deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-control .user-control-mines {
|
||||||
|
display: inline-block;
|
||||||
|
background: #FFFFFF;
|
||||||
|
font-size: 25px;
|
||||||
|
text-align: center;
|
||||||
|
width: 45px;
|
||||||
|
height: 35px;
|
||||||
|
margin-left: 25px;
|
||||||
|
margin-top: 5px;
|
||||||
|
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-blue .user-control .user-control-mines {
|
||||||
|
color: #1a3955;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-red .user-control .user-control-mines {
|
||||||
|
color: #b10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-control .bomb-container {
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
width: 65px;
|
||||||
|
height: 45px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
|
||||||
|
-webkit-border-radius: 7px;
|
||||||
|
border-radius: 7px;
|
||||||
|
-webkit-transform: translateZ(0);
|
||||||
|
transform: translateZ(0);
|
||||||
|
-webkit-backface-visibility: hidden;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
box-shadow: 0 0 1px rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-control .bomb-container.buzz:hover {
|
||||||
|
-webkit-animation-name: hvr-buzz-out;
|
||||||
|
animation-name: hvr-buzz-out;
|
||||||
|
-webkit-animation-duration: 0.75s;
|
||||||
|
animation-duration: 0.75s;
|
||||||
|
-webkit-animation-timing-function: linear;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
-webkit-animation-iteration-count: 1;
|
||||||
|
animation-iteration-count: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes hvr-buzz-out {
|
||||||
|
10% { -webkit-transform: translateX(3px) rotate(2deg); transform: translateX(3px) rotate(2deg); }
|
||||||
|
20% { -webkit-transform: translateX(-3px) rotate(-2deg); transform: translateX(-3px) rotate(-2deg); }
|
||||||
|
30% { -webkit-transform: translateX(3px) rotate(2deg); transform: translateX(3px) rotate(2deg); }
|
||||||
|
40% { -webkit-transform: translateX(-3px) rotate(-2deg); transform: translateX(-3px) rotate(-2deg); }
|
||||||
|
50% { -webkit-transform: translateX(2px) rotate(1deg); transform: translateX(2px) rotate(1deg); }
|
||||||
|
60% { -webkit-transform: translateX(-2px) rotate(-1deg); transform: translateX(-2px) rotate(-1deg); }
|
||||||
|
70% { -webkit-transform: translateX(2px) rotate(1deg); transform: translateX(2px) rotate(1deg); }
|
||||||
|
80% { -webkit-transform: translateX(-2px) rotate(-1deg); transform: translateX(-2px) rotate(-1deg); }
|
||||||
|
90% { -webkit-transform: translateX(1px) rotate(0); transform: translateX(1px) rotate(0); }
|
||||||
|
100% { -webkit-transform: translateX(-1px) rotate(0); transform: translateX(-1px) rotate(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes hvr-buzz-out {
|
||||||
|
10% { -webkit-transform: translateX(3px) rotate(2deg); transform: translateX(3px) rotate(2deg); }
|
||||||
|
20% { -webkit-transform: translateX(-3px) rotate(-2deg); transform: translateX(-3px) rotate(-2deg); }
|
||||||
|
30% { -webkit-transform: translateX(3px) rotate(2deg); transform: translateX(3px) rotate(2deg); }
|
||||||
|
40% { -webkit-transform: translateX(-3px) rotate(-2deg); transform: translateX(-3px) rotate(-2deg); }
|
||||||
|
50% { -webkit-transform: translateX(2px) rotate(1deg); transform: translateX(2px) rotate(1deg); }
|
||||||
|
60% { -webkit-transform: translateX(-2px) rotate(-1deg); transform: translateX(-2px) rotate(-1deg); }
|
||||||
|
70% { -webkit-transform: translateX(2px) rotate(1deg); transform: translateX(2px) rotate(1deg); }
|
||||||
|
80% { -webkit-transform: translateX(-2px) rotate(-1deg); transform: translateX(-2px) rotate(-1deg); }
|
||||||
|
90% { -webkit-transform: translateX(1px) rotate(0); transform: translateX(1px) rotate(0); }
|
||||||
|
100% { -webkit-transform: translateX(-1px) rotate(0); transform: translateX(-1px) rotate(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-control .bomb-container .bomb {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-control .bomb-container .bomb img {
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-control .bomb-container:hover .bomb img {
|
||||||
|
-webkit-animation-name: hvr-buzz;
|
||||||
|
animation-name: hvr-buzz;
|
||||||
|
-webkit-animation-duration: 0.15s;
|
||||||
|
animation-duration: 0.15s;
|
||||||
|
-webkit-animation-timing-function: linear;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
-webkit-animation-iteration-count: infinite;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-control .bomb-container .bomb {
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-blue .user-control .bomb-container .bomb {
|
||||||
|
background: rgb(131, 194, 245);
|
||||||
|
background: -moz-linear-gradient(top, rgba(131, 194, 245, 1) 0%, rgba(108, 190, 230, 1) 39%, rgba(221, 255, 252, 1) 100%);
|
||||||
|
background: -webkit-linear-gradient(top, rgba(131, 194, 245, 1) 0%, rgba(108, 190, 230, 1) 39%, rgba(221, 255, 252, 1) 100%);
|
||||||
|
background: linear-gradient(to bottom, rgba(131, 194, 245, 1) 0%, rgba(108, 190, 230, 1) 39%, rgba(221, 255, 252, 1) 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#83c2f5', endColorstr='#ddfffc', GradientType=0);
|
||||||
|
border: 3px solid #0b538e;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-red .user-control .bomb-container .bomb {
|
||||||
|
background: rgb(255, 175, 159);
|
||||||
|
background: -moz-linear-gradient(top, rgba(255, 175, 159, 1) 0%, rgba(231, 113, 7, 1) 54%, rgba(231, 113, 7, 1) 54%, rgba(237, 172, 16, 1) 100%);
|
||||||
|
background: -webkit-linear-gradient(top, rgba(255, 175, 159, 1) 0%, rgba(231, 113, 7, 1) 54%, rgba(231, 113, 7, 1) 54%, rgba(237, 172, 16, 1) 100%);
|
||||||
|
background: linear-gradient(to bottom, rgba(255, 175, 159, 1) 0%, rgba(231, 113, 7, 1) 54%, rgba(231, 113, 7, 1) 54%, rgba(237, 172, 16, 1) 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffaf9f', endColorstr='#edac10', GradientType=0);
|
||||||
|
border: 3px solid #c9221c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resign button
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .resign {
|
||||||
|
background: rgba(70, 73, 66, 1);
|
||||||
|
background: -moz-linear-gradient(top, rgba(70, 73, 66, 1) 0%, rgba(140, 138, 139, 1) 69%, rgba(96, 89, 97, 1) 100%);
|
||||||
|
background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(70, 73, 66, 1)), color-stop(69%, rgba(140, 138, 139, 1)), color-stop(100%, rgba(96, 89, 97, 1)));
|
||||||
|
background: -webkit-linear-gradient(top, rgba(70, 73, 66, 1) 0%, rgba(140, 138, 139, 1) 69%, rgba(96, 89, 97, 1) 100%);
|
||||||
|
background: -o-linear-gradient(top, rgba(70, 73, 66, 1) 0%, rgba(140, 138, 139, 1) 69%, rgba(96, 89, 97, 1) 100%);
|
||||||
|
background: -ms-linear-gradient(top, rgba(70, 73, 66, 1) 0%, rgba(140, 138, 139, 1) 69%, rgba(96, 89, 97, 1) 100%);
|
||||||
|
background: linear-gradient(to bottom, rgba(70, 73, 66, 1) 0%, rgba(140, 138, 139, 1) 69%, rgba(96, 89, 97, 1) 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#464942', endColorstr='#605961', GradientType=0);
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
width: 95%;
|
||||||
|
height: 50px;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 22px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 40px;
|
||||||
|
border: 3px solid #484742;
|
||||||
|
color: #fff;
|
||||||
|
margin: 10px auto 0 auto;
|
||||||
|
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
-webkit-transition: all 250ms ease-in-out;
|
||||||
|
-moz-transition: all 250ms ease-in-out;
|
||||||
|
-o-transition: all 250ms ease-in-out;
|
||||||
|
transition: all 250ms ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .resign:hover {
|
||||||
|
background: rgba(70, 73, 66, 1);
|
||||||
|
color: #FFFFFF;
|
||||||
|
transition: all 250ms ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .resign.disabled {
|
||||||
|
background: rgba(70, 73, 66, 1);
|
||||||
|
color: #848484;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .resign.disabled:hover {
|
||||||
|
background: rgba(70, 73, 66, 1);
|
||||||
|
color: #848484;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .resign .resign-shine {
|
||||||
|
background: rgba(255, 255, 255, 1);
|
||||||
|
background: -moz-linear-gradient(top, rgba(255, 255, 255, 1) 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(255, 255, 255, 1)), color-stop(100%, rgba(0, 0, 0, 0)));
|
||||||
|
background: -webkit-linear-gradient(top, rgba(255, 255, 255, 1) 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
background: -o-linear-gradient(top, rgba(255, 255, 255, 1) 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
background: -ms-linear-gradient(top, rgba(255, 255, 255, 1) 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
background: linear-gradient(to bottom, rgba(255, 255, 255, 1) 0%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#000000', GradientType=0);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 33%;
|
||||||
|
|
||||||
|
opacity: 0.7;
|
||||||
|
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: all 250ms ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .resign:hover .resign-shine,
|
||||||
|
#mine-wrapper .game-wrapper .users .resign.disabled .resign-shine {
|
||||||
|
display: none;
|
||||||
|
transition: all 250ms ease-in-out;
|
||||||
|
}
|
||||||
242
assets/css/mineseeker/_bonus-box.scss
Normal file
242
assets/css/mineseeker/_bonus-box.scss
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
/*!*
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#mine-wrapper .bonus-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 58px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
background: #07090d;
|
||||||
|
font-family: 'Rajdhani', sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.25s ease;
|
||||||
|
align-self: stretch;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
filter: brightness(1.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .bonus-box.red-bonus {
|
||||||
|
background: linear-gradient(to bottom, #2a0502 0%, #4a1510 100%);
|
||||||
|
border-color: rgba(246, 125, 82, 0.4);
|
||||||
|
color: rgba(246, 125, 82, 0.85);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(246, 125, 82, 0.85);
|
||||||
|
box-shadow: 0 0 12px rgba(173, 10, 5, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .bonus-box.blue-bonus {
|
||||||
|
background: linear-gradient(to bottom, #050f18 0%, #0f2838 100%);
|
||||||
|
border-color: rgba(149, 207, 245, 0.4);
|
||||||
|
color: rgba(149, 207, 245, 0.85);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(149, 207, 245, 0.85);
|
||||||
|
box-shadow: 0 0 12px rgba(35, 111, 135, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .bonus-box__icon {
|
||||||
|
font-size: 13px;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .bonus-box__value {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: 'Rajdhani', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 18px 22px 14px;
|
||||||
|
border-bottom: 1px solid rgba(35, 111, 135, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-header-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-label {
|
||||||
|
font-size: 11px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: rgba(149, 207, 245, 0.7);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
color: #f6d572;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-close {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.4);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
|
border-color: rgba(149, 207, 245, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-body {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 18px 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-column {
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 14px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-column--red {
|
||||||
|
border-color: rgba(246, 125, 82, 0.35);
|
||||||
|
background: linear-gradient(to bottom, rgba(74, 6, 3, 0.35), rgba(107, 37, 21, 0.15));
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-column--blue {
|
||||||
|
border-color: rgba(149, 207, 245, 0.35);
|
||||||
|
background: linear-gradient(to bottom, rgba(11, 37, 48, 0.35), rgba(22, 61, 85, 0.15));
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-column-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-column-name {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-column-total {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #f6d572;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-stats {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-stat {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 6px 2px;
|
||||||
|
border-bottom: 1px dashed rgba(255, 255, 255, 0.06);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-stat-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-stat-label {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-stat-desc {
|
||||||
|
font-size: 11px;
|
||||||
|
color: rgba(255, 255, 255, 0.48);
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-stat-value {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #fff;
|
||||||
|
min-width: 24px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bsd-note {
|
||||||
|
margin: 0;
|
||||||
|
padding: 12px 22px 18px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(255, 255, 255, 0.45);
|
||||||
|
text-align: center;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 520px) {
|
||||||
|
.bsd-body {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
207
assets/css/mineseeker/_grid.scss
Normal file
207
assets/css/mineseeker/_grid.scss
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
#mine-wrapper .grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 643px;
|
||||||
|
border: 1px solid #cac3e5;
|
||||||
|
|
||||||
|
cursor: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid-container {
|
||||||
|
background: #4E4E4E;
|
||||||
|
padding: 15px 10px;
|
||||||
|
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper > img.field-target {
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
width: 45px;
|
||||||
|
top: -2.5px;
|
||||||
|
left: -2.5px;
|
||||||
|
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper:hover > img.field-target {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper > img.field-bomb-target {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper > img.field-blue-last,
|
||||||
|
#mine-wrapper .grid .field-wrapper > img.field-red-last {
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper > img.field-blue-last.last-clicked,
|
||||||
|
#mine-wrapper .grid .field-wrapper > img.field-red-last.last-clicked {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field {
|
||||||
|
background: #61defa;
|
||||||
|
background: -moz-linear-gradient(left, #61defa 0%, #119dec 100%);
|
||||||
|
background: -webkit-linear-gradient(left, #61defa 0%, #119dec 100%);
|
||||||
|
background: linear-gradient(to right, #61defa 0%, #119dec 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#61defa', endColorstr='#119dec', GradientType=1);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: 2px solid #51c2fe;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 35px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field .field-corner {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active {
|
||||||
|
background: #fde717;
|
||||||
|
background: -moz-linear-gradient(left, #fde717 0%, #f5b807 100%);
|
||||||
|
background: -webkit-linear-gradient(left, #fde717 0%, #f5b807 100%);
|
||||||
|
background: linear-gradient(to right, #fde717 0%, #f5b807 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fde717', endColorstr='#f5b807', GradientType=1);
|
||||||
|
border: 2px solid #f6d762;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active .flag-number {
|
||||||
|
-webkit-animation: bubbleNumber 500ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
animation: bubbleNumber 500ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bubbleNumber {
|
||||||
|
0% {
|
||||||
|
background: #61defa;
|
||||||
|
background: -moz-linear-gradient(left, #61defa 0%, #119dec 100%);
|
||||||
|
background: -webkit-linear-gradient(left, #61defa 0%, #119dec 100%);
|
||||||
|
background: linear-gradient(to right, #61defa 0%, #119dec 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#61defa', endColorstr='#119dec', GradientType=1);
|
||||||
|
|
||||||
|
-webkit-border-radius: 50%;
|
||||||
|
border-radius: 50%;
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
-webkit-border-radius: 50%;
|
||||||
|
border-radius: 50%;
|
||||||
|
-webkit-transform: scale(0);
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-border-radius: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active .flag-mine {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active .flag-mine > img {
|
||||||
|
width: 75%;
|
||||||
|
margin-left: 15px;
|
||||||
|
|
||||||
|
-ms-transform: rotate(7deg);
|
||||||
|
-webkit-transform: rotate(7deg);
|
||||||
|
transform: rotate(7deg);
|
||||||
|
-webkit-animation: mineFlagLoad 500ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
animation: mineFlagLoad 500ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes mineFlagLoad {
|
||||||
|
0% {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-left: 15px;
|
||||||
|
-ms-transform: rotate(9deg);
|
||||||
|
-webkit-transform: rotate(9deg);
|
||||||
|
transform: rotate(9deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
margin-bottom: -5px;
|
||||||
|
margin-left: 7px;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
margin-left: 0;
|
||||||
|
-ms-transform: rotate(-9deg);
|
||||||
|
-webkit-transform: rotate(-9deg);
|
||||||
|
transform: rotate(-9deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active .flag-mine .flag-mine-base {
|
||||||
|
position: absolute;
|
||||||
|
background: #000000;
|
||||||
|
width: 25px;
|
||||||
|
height: 22px;
|
||||||
|
bottom: -12px;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -10.5px;
|
||||||
|
|
||||||
|
-webkit-border-radius: 50%;
|
||||||
|
border-radius: 50%;
|
||||||
|
-webkit-animation: mineBaseLoad 500ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
animation: mineBaseLoad 500ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes mineBaseLoad {
|
||||||
|
0% { margin-bottom: 0; }
|
||||||
|
50% { margin-bottom: -5px; }
|
||||||
|
100% { margin-bottom: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.mine {
|
||||||
|
background: #61defa;
|
||||||
|
background: -moz-linear-gradient(left, #61defa 0%, #119dec 100%);
|
||||||
|
background: -webkit-linear-gradient(left, #61defa 0%, #119dec 100%);
|
||||||
|
background: linear-gradient(to right, #61defa 0%, #119dec 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#61defa', endColorstr='#119dec', GradientType=1);
|
||||||
|
border: 2px solid #51c2fe;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-1 { color: #0000ff; }
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-2 { color: #079433; }
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-3 { color: #fd1400; }
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-4 { color: #0c099e; }
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-5 { color: #7b4c01; }
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-6 { color: #008388; }
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-7 { color: #000000; }
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-8 { color: #ff0000; }
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field img {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
90
assets/css/mineseeker/_mine-counter.scss
Normal file
90
assets/css/mineseeker/_mine-counter.scss
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#mine-wrapper .game-wrapper .users .active-mines-container {
|
||||||
|
background: -moz-radial-gradient(center, ellipse cover, rgba(255, 252, 252, 1) 0%, rgba(255, 252, 252, 0.99) 1%, rgba(106, 106, 106, 0.39) 61%, rgba(106, 106, 106, 0) 100%);
|
||||||
|
background: -webkit-radial-gradient(center, ellipse cover, rgba(255, 252, 252, 1) 0%, rgba(255, 252, 252, 0.99) 1%, rgba(106, 106, 106, 0.39) 61%, rgba(106, 106, 106, 0) 100%);
|
||||||
|
background: radial-gradient(ellipse at center, rgba(255, 252, 252, 1) 0%, rgba(255, 252, 252, 0.99) 1%, rgba(106, 106, 106, 0.39) 61%, rgba(106, 106, 106, 0) 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcfc', endColorstr='#006a6a6a', GradientType=1);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center center;
|
||||||
|
background-size: 72% 179%;
|
||||||
|
position: relative;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .active-mines-container i {
|
||||||
|
font-size: 27px;
|
||||||
|
color: #b1b1b3;
|
||||||
|
margin-top: 3px;
|
||||||
|
|
||||||
|
text-shadow: 0 0 3px #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .active-mines-container i:first-child {
|
||||||
|
float: left;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .active-mines-container i:last-child {
|
||||||
|
float: right;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .active-mines-container .active-mines {
|
||||||
|
background: -moz-linear-gradient(top, rgba(0, 0, 0, 1) 0%, rgba(135, 136, 131, 1) 100%);
|
||||||
|
background: -webkit-linear-gradient(top, rgba(0, 0, 0, 1) 0%, rgba(135, 136, 131, 1) 100%);
|
||||||
|
background: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(135, 136, 131, 1) 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#3e3f41', endColorstr='#878883', GradientType=0);
|
||||||
|
position: absolute;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
top: -7.5px;
|
||||||
|
left: 50%;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
font-size: 25px;
|
||||||
|
line-height: 39px;
|
||||||
|
text-align: center;
|
||||||
|
color: #FFFFFF;
|
||||||
|
border: 5px solid #000000;
|
||||||
|
margin-left: -25px;
|
||||||
|
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
-webkit-border-radius: 50%;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .active-mines-container .active-mines.found-mine {
|
||||||
|
-webkit-animation: bubbleLeftMine 750ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
animation: bubbleLeftMine 750ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bubbleLeftMine {
|
||||||
|
0% { -webkit-transform: scale(1); transform: scale(1); }
|
||||||
|
50% { border-color: #2e3337; -webkit-transform: scale(2); transform: scale(2); }
|
||||||
|
100% { -webkit-transform: scale(1); transform: scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .active-mines-container .active-mines .active-mines-shine {
|
||||||
|
background: -moz-linear-gradient(top, rgba(213, 214, 216, 1) 0%, rgba(106, 106, 106, 1) 100%);
|
||||||
|
background: -webkit-linear-gradient(top, rgba(213, 214, 216, 1) 0%, rgba(106, 106, 106, 1) 100%);
|
||||||
|
background: linear-gradient(to bottom, rgba(213, 214, 216, 1) 0%, rgba(106, 106, 106, 1) 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#d5d6d8', endColorstr='#6a6a6a', GradientType=0);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
width: 30px;
|
||||||
|
height: 20px;
|
||||||
|
margin-left: -14.5px;
|
||||||
|
|
||||||
|
z-index: 101;
|
||||||
|
|
||||||
|
-webkit-border-radius: 50%;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .active-mines-container .active-mines .active-mines-nbr {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
z-index: 102;
|
||||||
|
}
|
||||||
592
assets/css/mineseeker/_overlay.scss
Normal file
592
assets/css/mineseeker/_overlay.scss
Normal file
@@ -0,0 +1,592 @@
|
|||||||
|
#mine-wrapper .game-wrapper .game-overlay {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
z-index: 200;
|
||||||
|
|
||||||
|
-webkit-border-radius: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window {
|
||||||
|
background: linear-gradient(135deg, rgba(7, 9, 13, 0.98) 0%, rgba(10, 20, 35, 0.98) 100%);
|
||||||
|
border: 2px solid rgba(35, 111, 135, 0.4);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
font-family: 'Rajdhani', sans-serif;
|
||||||
|
color: #fff;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 680px;
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.7), 0 0 40px rgba(35, 111, 135, 0.15);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0;
|
||||||
|
animation: slideUp 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window h1 {
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 32px;
|
||||||
|
color: #fff;
|
||||||
|
margin: 0 0 50px 0;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window h2 {
|
||||||
|
font-size: 14px;
|
||||||
|
color: rgba(149, 207, 245, 0.6);
|
||||||
|
margin: 0 0 30px 0;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window .resign {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
width: 100%;
|
||||||
|
text-transform: none;
|
||||||
|
letter-spacing: normal;
|
||||||
|
font-size: inherit;
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
background: linear-gradient(to bottom, #236f87 0%, #1a5068 100%);
|
||||||
|
border: 2px solid #2e7a9a;
|
||||||
|
color: #e0f4ff;
|
||||||
|
font: 800 13px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 260px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 12px rgba(35, 111, 135, 0.25);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||||
|
transition: left 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(to bottom, #2d8aa8 0%, #236f87 100%);
|
||||||
|
border-color: #5ba4d4;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 8px 24px rgba(35, 111, 135, 0.4);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
background: linear-gradient(to bottom, #8a2323 0%, #681a1a 100%);
|
||||||
|
border-color: #9a2e2e;
|
||||||
|
color: #ffe0e0;
|
||||||
|
box-shadow: 0 4px 12px rgba(135, 35, 35, 0.25);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(to bottom, #a82d2d 0%, #872323 100%);
|
||||||
|
border-color: #d45b5b;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 8px 24px rgba(135, 35, 35, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window:has(.resign) h1 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window:has(.resign) h2 {
|
||||||
|
text-align: center;
|
||||||
|
color: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-transform: none;
|
||||||
|
font-weight: normal;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #236f87;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window .share-invite {
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window .share-invite-label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #386e8c;
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.waiting-options {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto 1fr;
|
||||||
|
grid-template-rows: auto;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: stretch;
|
||||||
|
width: 100%;
|
||||||
|
animation: fadeInUp 0.6s ease-out 0.2s both;
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.waiting-option {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 15px;
|
||||||
|
background: linear-gradient(135deg, rgba(35, 111, 135, 0.08) 0%, rgba(26, 80, 104, 0.08) 100%);
|
||||||
|
border: 2px solid rgba(35, 111, 135, 0.2);
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: all 350ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
animation: scaleIn 0.5s ease-out;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(35, 111, 135, 0.15), transparent);
|
||||||
|
transition: left 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba(35, 111, 135, 0.45);
|
||||||
|
background: linear-gradient(135deg, rgba(35, 111, 135, 0.12) 0%, rgba(26, 80, 104, 0.12) 100%);
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 12px 30px rgba(35, 111, 135, 0.2);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.waiting-option-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 17px;
|
||||||
|
color: #fff;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
background: linear-gradient(135deg, rgba(35, 111, 135, 0.4) 0%, rgba(35, 111, 135, 0.2) 100%);
|
||||||
|
border: 2px solid rgba(35, 111, 135, 0.5);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 400ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover i {
|
||||||
|
background: linear-gradient(135deg, rgba(35, 111, 135, 0.6) 0%, rgba(35, 111, 135, 0.4) 100%);
|
||||||
|
border-color: rgba(35, 111, 135, 0.8);
|
||||||
|
transform: scale(1.15) rotate(-8deg);
|
||||||
|
box-shadow: 0 0 20px rgba(35, 111, 135, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.waiting-option-desc {
|
||||||
|
font: 600 12px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.75);
|
||||||
|
margin: 0;
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.waiting-divider {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
margin: 0;
|
||||||
|
animation: slideIn 0.7s ease-out 0.4s both;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
width: 2px;
|
||||||
|
height: 20px;
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(35, 111, 135, 0.1),
|
||||||
|
rgba(35, 111, 135, 0.4),
|
||||||
|
rgba(35, 111, 135, 0.1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font: 700 11px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(35, 111, 135, 0.6);
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
flex-direction: row;
|
||||||
|
margin: 8px 0;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
flex: 1;
|
||||||
|
width: auto;
|
||||||
|
height: 1px;
|
||||||
|
background: linear-gradient(
|
||||||
|
to right,
|
||||||
|
rgba(35, 111, 135, 0),
|
||||||
|
rgba(35, 111, 135, 0.3),
|
||||||
|
rgba(35, 111, 135, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window .share-invite {
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window .share-url-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: linear-gradient(135deg, #d0e8f5 0%, #c5dff0 100%);
|
||||||
|
border: 2px solid #7ab8d8;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0 10px;
|
||||||
|
cursor: text;
|
||||||
|
transition: all 300ms ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #236f87;
|
||||||
|
box-shadow: 0 4px 12px rgba(35, 111, 135, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
border-color: #236f87;
|
||||||
|
box-shadow: 0 0 16px rgba(35, 111, 135, 0.35);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window .share-url-icon {
|
||||||
|
color: #236f87;
|
||||||
|
font-size: 13px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 8px;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window .share-url-input {
|
||||||
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
height: 40px;
|
||||||
|
color: #1a4a6a;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
cursor: text;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
&::selection {
|
||||||
|
background: rgba(35, 111, 135, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scaleIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window .browse-players-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
background: linear-gradient(to bottom, #236f87 0%, #1a5068 100%);
|
||||||
|
border: 2px solid #2e7a9a;
|
||||||
|
color: #e0f4ff;
|
||||||
|
font: 700 13px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
width: 100%;
|
||||||
|
font-weight: 800;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 12px rgba(35, 111, 135, 0.25);
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||||
|
transition: left 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(to bottom, #2d8aa8 0%, #236f87 100%);
|
||||||
|
border-color: #5ba4d4;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 8px 24px rgba(35, 111, 135, 0.4);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window .share-copy-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 9px;
|
||||||
|
background: linear-gradient(to bottom, #236f87 0%, #1a5068 100%);
|
||||||
|
border: 2px solid #2e7a9a;
|
||||||
|
color: #e0f4ff;
|
||||||
|
font-family: 'Rajdhani', sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 12px rgba(35, 111, 135, 0.25);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||||
|
transition: left 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(to bottom, #2d8aa8 0%, #236f87 100%);
|
||||||
|
border-color: #5ba4d4;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 8px 24px rgba(35, 111, 135, 0.4);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.copied {
|
||||||
|
background: linear-gradient(to bottom, #1a6844 0%, #135233 100%);
|
||||||
|
border-color: #2a9e60;
|
||||||
|
color: #a0f0c0;
|
||||||
|
box-shadow: 0 4px 12px rgba(26, 104, 68, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .game-overlay .game-overlay-window .game-overlay-share {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 9px;
|
||||||
|
background: linear-gradient(to bottom, #236f87 0%, #1a5068 100%);
|
||||||
|
border: 2px solid #2e7a9a;
|
||||||
|
color: #e0f4ff;
|
||||||
|
font-family: 'Rajdhani', sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 300ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 20px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 12px rgba(35, 111, 135, 0.25);
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||||
|
transition: left 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(to bottom, #2d8aa8 0%, #236f87 100%);
|
||||||
|
border-color: #5ba4d4;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 8px 24px rgba(35, 111, 135, 0.4);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.copied {
|
||||||
|
background: linear-gradient(to bottom, #1a6844 0%, #135233 100%);
|
||||||
|
border-color: #2a9e60;
|
||||||
|
color: #a0f0c0;
|
||||||
|
box-shadow: 0 4px 12px rgba(26, 104, 68, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
41
assets/css/mineseeker/_responsive.scss
Normal file
41
assets/css/mineseeker/_responsive.scss
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
@media screen and (max-width: 900px) {
|
||||||
|
#mine-wrapper .game-wrapper .users {
|
||||||
|
visibility: hidden;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid-container {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper {
|
||||||
|
width: 6.25%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper > img.field-target {
|
||||||
|
width: 105%;
|
||||||
|
top: -2.5%;
|
||||||
|
left: -2.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
102
assets/css/mineseeker/_timer.scss
Normal file
102
assets/css/mineseeker/_timer.scss
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#mine-wrapper .game-timer-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-timer {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 115px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 18px;
|
||||||
|
font-family: 'Rajdhani', sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
transition: all 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Red – waiting
|
||||||
|
#mine-wrapper .game-timer.red-timer {
|
||||||
|
background: linear-gradient(to bottom, #4a0603 0%, #6b2515 100%);
|
||||||
|
border-color: #7a1e10;
|
||||||
|
color: rgba(246, 125, 82, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Red – active (thinking)
|
||||||
|
#mine-wrapper .game-timer.red-timer.active {
|
||||||
|
background: linear-gradient(to bottom, #ad0a05 0%, #f67d52 100%);
|
||||||
|
border-color: #ff9b6b;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 0 16px rgba(173, 10, 5, 0.75), 0 0 5px rgba(246, 125, 82, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blue – waiting
|
||||||
|
#mine-wrapper .game-timer.blue-timer {
|
||||||
|
background: linear-gradient(to bottom, #0b2530 0%, #163d55 100%);
|
||||||
|
border-color: #173650;
|
||||||
|
color: rgba(149, 207, 245, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blue – active (thinking)
|
||||||
|
#mine-wrapper .game-timer.blue-timer.active {
|
||||||
|
background: linear-gradient(to bottom, #236f87 0%, #95cff5 100%);
|
||||||
|
border-color: #b8e5ff;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 0 16px rgba(35, 111, 135, 0.75), 0 0 5px rgba(149, 207, 245, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-timer .timer-avatar {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: 'Rajdhani', sans-serif;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-timer .timer-avatar__img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-timer.red-timer .timer-avatar__initials {
|
||||||
|
color: rgba(246, 125, 82, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-timer.blue-timer .timer-avatar__initials {
|
||||||
|
color: rgba(149, 207, 245, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-timer .timer-icon {
|
||||||
|
font-size: 15px;
|
||||||
|
opacity: 0.7;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-timer.active .timer-icon {
|
||||||
|
opacity: 1;
|
||||||
|
animation: timer-icon-pulse 1.6s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes timer-icon-pulse {
|
||||||
|
0%, 100% { transform: scale(1); opacity: 0.85; }
|
||||||
|
50% { transform: scale(1.2); opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-timer .timer-display {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 20px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
153
assets/css/mineseeker/_users.scss
Normal file
153
assets/css/mineseeker/_users.scss
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
#mine-wrapper .game-wrapper .users {
|
||||||
|
width: 180px;
|
||||||
|
padding: 0 10px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container {
|
||||||
|
background: #FFFFFF;
|
||||||
|
height: 40%;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
padding: 5px;
|
||||||
|
margin: 5px;
|
||||||
|
|
||||||
|
z-index: 99;
|
||||||
|
|
||||||
|
-webkit-border-radius: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-blue {
|
||||||
|
background: rgb(35, 111, 135);
|
||||||
|
background: -moz-linear-gradient(top, rgba(35, 111, 135, 1) 0%, rgba(149, 207, 245, 1) 100%);
|
||||||
|
background: -webkit-linear-gradient(top, rgba(35, 111, 135, 1) 0%, rgba(149, 207, 245, 1) 100%);
|
||||||
|
background: linear-gradient(to bottom, rgba(35, 111, 135, 1) 0%, rgba(149, 207, 245, 1) 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#236f87', endColorstr='#95cff5', GradientType=0);
|
||||||
|
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-red {
|
||||||
|
background: rgb(173, 10, 5);
|
||||||
|
background: -moz-linear-gradient(top, rgba(173, 10, 5, 1) 0%, rgba(246, 125, 82, 1) 100%);
|
||||||
|
background: -webkit-linear-gradient(top, rgba(173, 10, 5, 1) 0%, rgba(246, 125, 82, 1) 100%);
|
||||||
|
background: linear-gradient(to bottom, rgba(173, 10, 5, 1) 0%, rgba(246, 125, 82, 1) 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ad0a05', endColorstr='#f67d52', GradientType=0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-header {
|
||||||
|
background: -moz-linear-gradient(top, rgba(255, 255, 255, 0.7) 39%, rgba(255, 255, 255, 0.21) 87%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0.7) 39%, rgba(255, 255, 255, 0.21) 87%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.7) 39%, rgba(255, 255, 255, 0.21) 87%, rgba(0, 0, 0, 0) 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#00000000', GradientType=0);
|
||||||
|
position: relative;
|
||||||
|
font: bolder 25px 'Changa One', cursive;
|
||||||
|
letter-spacing: 5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
text-align: center;
|
||||||
|
padding: 6px 5px 20px 5px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
-webkit-text-shadow: 1px 1px 0 #FFF;
|
||||||
|
text-shadow: 1px 1px 0 #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-blue .user-header {
|
||||||
|
color: #236f87;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-red .user-header {
|
||||||
|
color: #AD0A05;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-header > img {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
bottom: 0;
|
||||||
|
width: 40%;
|
||||||
|
margin-left: -20%;
|
||||||
|
margin-bottom: -25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-header > img.user-cursor {
|
||||||
|
display: block;
|
||||||
|
width: 30%;
|
||||||
|
top: 20px;
|
||||||
|
left: 10px;
|
||||||
|
margin-left: 0;
|
||||||
|
|
||||||
|
-webkit-animation: cursorJumping 1.2s cubic-bezier(.36, .07, .19, .97) infinite;
|
||||||
|
animation: cursorJumping 1.2s cubic-bezier(.36, .07, .19, .97) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-header > img.user-cursor::after {
|
||||||
|
content: '';
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
background: #1A6844;
|
||||||
|
animation: animate .5s linear infinite;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cursorJumping {
|
||||||
|
0% { top: 15px; }
|
||||||
|
50% { top: 25px; }
|
||||||
|
100% { top: 15px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-name {
|
||||||
|
min-height: 30px;
|
||||||
|
font-weight: normal;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding: 3px 0;
|
||||||
|
margin: 0 5px;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-blue .user-name {
|
||||||
|
border-top: 1px dashed #0b3776;
|
||||||
|
border-bottom: 1px dashed #0b3776;
|
||||||
|
color: #0b3776;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-red .user-name {
|
||||||
|
color: #fdf612;
|
||||||
|
border-top: 1px dashed #fdf612;
|
||||||
|
border-bottom: 1px dashed #fdf612;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-caret {
|
||||||
|
height: 30px;
|
||||||
|
font-size: 30px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-blue .user-caret > i {
|
||||||
|
color: #0b3776;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-red .user-caret > i {
|
||||||
|
color: #fdf612;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container .user-desc {
|
||||||
|
height: 65px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-blue .user-desc {
|
||||||
|
color: #0b3776;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .game-wrapper .users .user-container.user-red .user-desc {
|
||||||
|
color: #fdf612;
|
||||||
|
}
|
||||||
347
assets/css/mineseeker/_waiting-dialog.scss
Normal file
347
assets/css/mineseeker/_waiting-dialog.scss
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
.opd-paper {
|
||||||
|
background: #07090d !important;
|
||||||
|
background-image: linear-gradient(rgba(35, 111, 135, 0.08) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(35, 111, 135, 0.08) 1px, transparent 1px) !important;
|
||||||
|
background-size: 46px 46px !important;
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.4) !important;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
box-shadow: 0 0 80px rgba(35, 111, 135, 0.15),
|
||||||
|
0 32px 80px rgba(0, 0, 0, 0.9) !important;
|
||||||
|
width: 500px;
|
||||||
|
max-width: 94vw !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd {
|
||||||
|
padding: 28px 28px 22px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 22px;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-header-text {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-label {
|
||||||
|
display: block;
|
||||||
|
font: 700 11px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
color: rgba(149, 207, 245, 0.55);
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-title {
|
||||||
|
font: 800 28px 'Rajdhani', sans-serif;
|
||||||
|
color: #fff;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 11px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: rgba(35, 111, 135, 0.9);
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-refresh,
|
||||||
|
.opd-close {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.3);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: rgba(149, 207, 245, 0.55);
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 200ms ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
border-color: rgba(149, 207, 245, 0.5);
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(35, 111, 135, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-refresh--spin i {
|
||||||
|
animation: opd-spin 0.7s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes opd-spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-search-wrap {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(35, 111, 135, 0.07);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.28);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0 14px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
transition: border-color 200ms ease, background 200ms ease;
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
border-color: rgba(35, 111, 135, 0.65);
|
||||||
|
background: rgba(35, 111, 135, 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-search-icon {
|
||||||
|
color: rgba(149, 207, 245, 0.38);
|
||||||
|
font-size: 13px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-search {
|
||||||
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
height: 44px;
|
||||||
|
font: 400 14px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: rgba(149, 207, 245, 0.32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-search-clear {
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
color: rgba(149, 207, 245, 0.4);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 0 0 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
transition: color 150ms ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgba(149, 207, 245, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-list {
|
||||||
|
min-height: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-empty {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 30px 0 22px;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 34px;
|
||||||
|
color: rgba(35, 111, 135, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font: 400 14px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 255, 255, 0.38);
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 11px 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
transition: all 180ms ease;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(35, 111, 135, 0.1);
|
||||||
|
border-color: rgba(35, 111, 135, 0.28);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-avatar {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, rgba(35, 111, 135, 0.55) 0%, rgba(35, 111, 135, 0.28) 100%);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font: 700 13px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.9);
|
||||||
|
letter-spacing: 1px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-name {
|
||||||
|
font: 700 15px 'Rajdhani', sans-serif;
|
||||||
|
color: #fff;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-since {
|
||||||
|
font: 400 12px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.48);
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-join {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
background: linear-gradient(to bottom, rgba(35, 111, 135, 0.75) 0%, rgba(26, 80, 104, 0.9) 100%);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.55);
|
||||||
|
color: rgba(149, 207, 245, 0.9);
|
||||||
|
font: 700 12px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 7px 16px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 200ms ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background: linear-gradient(to bottom, rgba(45, 138, 168, 0.9) 0%, rgba(35, 111, 135, 0.95) 100%);
|
||||||
|
border-color: rgba(149, 207, 245, 0.5);
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 0 14px rgba(35, 111, 135, 0.5);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 0.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.opd-join--waiting {
|
||||||
|
background: linear-gradient(to bottom, rgba(26, 80, 104, 0.6) 0%, rgba(15, 50, 70, 0.7) 100%);
|
||||||
|
border-color: rgba(35, 111, 135, 0.3);
|
||||||
|
color: rgba(149, 207, 245, 0.6);
|
||||||
|
opacity: 1;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-declined {
|
||||||
|
font: 600 12px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(255, 120, 120, 0.85);
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid rgba(180, 60, 60, 0.3);
|
||||||
|
background: rgba(180, 60, 60, 0.08);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-note {
|
||||||
|
font: 400 11px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.32);
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin-top: 14px;
|
||||||
|
padding-top: 14px;
|
||||||
|
border-top: 1px solid rgba(35, 111, 135, 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-header-actions {
|
||||||
|
.opd-refresh[disabled] {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-close[disabled] {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opd-waiting {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
background: rgba(35, 111, 135, 0.07);
|
||||||
|
border: 1px solid rgba(35, 111, 135, 0.28);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #95cff5;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 16px;
|
||||||
|
animation: opd-hourglass 1s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font: 600 14px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes opd-hourglass {
|
||||||
|
0%, 100% { transform: rotate(0deg); }
|
||||||
|
50% { transform: rotate(180deg); }
|
||||||
|
}
|
||||||
292
assets/css/passkey.scss
Normal file
292
assets/css/passkey.scss
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
@use "sass:color";
|
||||||
|
|
||||||
|
.twofa-status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font: 600 13px 'Rajdhani', sans-serif;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
|
||||||
|
&--enabled {
|
||||||
|
background: rgba(42, 158, 96, 0.12);
|
||||||
|
border: 1px solid rgba(42, 158, 96, 0.3);
|
||||||
|
color: #5ee89a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--disabled {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
color: rgba(255, 255, 255, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.twofa-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
&__form {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.twofa-backup-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
&__count {
|
||||||
|
font: 500 13px 'Rajdhani', sans-serif;
|
||||||
|
color: rgba(149, 207, 245, 0.55);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.twofa-backup-reveal {
|
||||||
|
background: rgba(246, 125, 82, 0.07);
|
||||||
|
border: 1px solid rgba(246, 125, 82, 0.25);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 18px 20px;
|
||||||
|
|
||||||
|
&__warning {
|
||||||
|
font: 600 12px 'Rajdhani', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
color: #f6a060;
|
||||||
|
margin: 0 0 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.twofa-backup-code {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
padding: 7px 10px;
|
||||||
|
background: rgba(0, 0, 0, 0.35);
|
||||||
|
border: 1px solid rgba(246, 125, 82, 0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
font: 700 13px 'Courier New', monospace;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: #e0c890;
|
||||||
|
user-select: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
$primary: #236f87;
|
||||||
|
$primary-dark: #1a5a70;
|
||||||
|
$danger: #c0392b;
|
||||||
|
$warning: #d68910;
|
||||||
|
$success: #388e3c;
|
||||||
|
$text: #e0e0e0;
|
||||||
|
$text-muted: #9e9e9e;
|
||||||
|
$border: rgba(35, 111, 135, 0.3);
|
||||||
|
$bg-card: #0a0e14;
|
||||||
|
$bg-hover: rgba(35, 111, 135, 0.15);
|
||||||
|
|
||||||
|
.passkey-manager {
|
||||||
|
&__actions {
|
||||||
|
margin: 20px 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.passkey-item {
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 15px;
|
||||||
|
background: $bg-card;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $bg-hover;
|
||||||
|
border-color: rgba(35, 111, 135, 0.5);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
margin: 0 0 5px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__meta {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: $text-muted;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__badges {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid $border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&--info {
|
||||||
|
background: rgba(25, 118, 210, 0.2);
|
||||||
|
color: #64b5f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--success {
|
||||||
|
background: rgba(56, 142, 60, 0.2);
|
||||||
|
color: #81c784;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&--primary {
|
||||||
|
background: $primary;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $primary-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--secondary {
|
||||||
|
background: #546e7a;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #455a64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--warning {
|
||||||
|
background: $warning;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: color.adjust($warning, $lightness: -10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--danger {
|
||||||
|
background: $danger;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: color.adjust($danger, $lightness: -10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--sm {
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 20px;
|
||||||
|
border: 1px dashed $border;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
font-size: 48px;
|
||||||
|
color: #455a64;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
font-size: 16px;
|
||||||
|
color: $text;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtext {
|
||||||
|
font-size: 13px;
|
||||||
|
color: $text-muted;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.registration-status {
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&--success {
|
||||||
|
background: rgba(56, 142, 60, 0.15);
|
||||||
|
color: #81c784;
|
||||||
|
border: 1px solid rgba(56, 142, 60, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--error {
|
||||||
|
background: rgba(192, 57, 43, 0.15);
|
||||||
|
color: #e57373;
|
||||||
|
border: 1px solid rgba(192, 57, 43, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--loading {
|
||||||
|
background: rgba(25, 118, 210, 0.15);
|
||||||
|
color: #64b5f6;
|
||||||
|
border: 1px solid rgba(25, 118, 210, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,181 +1,15 @@
|
|||||||
* {
|
@use 'homepage/reset';
|
||||||
outline: none;
|
@use 'homepage/animations';
|
||||||
padding: 0;
|
@use 'homepage/header';
|
||||||
margin: 0;
|
@use 'homepage/hero';
|
||||||
}
|
@use 'homepage/hero-compact';
|
||||||
|
@use 'homepage/cta';
|
||||||
html, body {
|
@use 'homepage/donate';
|
||||||
background: #ffffff;
|
@use 'homepage/auth-bar';
|
||||||
display: block;
|
@use 'homepage/auth';
|
||||||
width: 100%;
|
@use 'homepage/content';
|
||||||
height: 100%;
|
@use 'homepage/features';
|
||||||
}
|
@use 'homepage/tech';
|
||||||
|
@use 'homepage/footer';
|
||||||
header {
|
@use 'homepage/profile';
|
||||||
background: #d1e8ff;
|
@use 'homepage/responsive';
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 950px;
|
|
||||||
color: #ffffff;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
flex-direction: column;
|
|
||||||
max-width: 1300px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 20px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section div.logo img {
|
|
||||||
width: 350px;
|
|
||||||
margin: 50px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section div.logo a {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section > img {
|
|
||||||
position: absolute;
|
|
||||||
width: 1300px;
|
|
||||||
height: 1300px;
|
|
||||||
right: -20%;
|
|
||||||
top: -10%;
|
|
||||||
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section > div {
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section h1 {
|
|
||||||
font: bold 40px 'Rajdhani', sans-serif;
|
|
||||||
color: #434242;
|
|
||||||
margin-bottom: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section h2 {
|
|
||||||
font: normal 32px 'Rajdhani', sans-serif;
|
|
||||||
color: #434242;
|
|
||||||
margin-top: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section h3 {
|
|
||||||
display: block;
|
|
||||||
font: bold 16px 'Rajdhani', sans-serif;
|
|
||||||
color: #434242;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section h3 img {
|
|
||||||
width: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section div.buttons > a {
|
|
||||||
background: #69788e;
|
|
||||||
display: table;
|
|
||||||
font: bold 32px 'Rajdhani', sans-serif;
|
|
||||||
text-transform: uppercase;
|
|
||||||
text-decoration: none;
|
|
||||||
border: 5px solid #57667b;
|
|
||||||
color: #FFFFFF;
|
|
||||||
padding: 25px 150px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
|
|
||||||
border-radius: 20px;
|
|
||||||
transition: all 250ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section div.buttons > a:hover {
|
|
||||||
background: #57667b;
|
|
||||||
|
|
||||||
-webkit-box-shadow: 0 7px 15px rgba(0, 0, 0, 0.2);
|
|
||||||
box-shadow: 0 7px 15px rgba(0, 0, 0, 0.2);
|
|
||||||
-webkit-transition: all 250ms ease-in-out;
|
|
||||||
-moz-transition: all 250ms ease-in-out;
|
|
||||||
-o-transition: all 250ms ease-in-out;
|
|
||||||
transition: all 250ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section div.buttons > a.small {
|
|
||||||
background: #83aed9;
|
|
||||||
display: inline-block;
|
|
||||||
font: bold 22px 'Rajdhani', sans-serif;
|
|
||||||
border: 1px solid #6890ba;
|
|
||||||
color: #FFFFFF;
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
-webkit-box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
|
|
||||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
|
|
||||||
-webkit-transition: all 250ms ease-in-out;
|
|
||||||
-moz-transition: all 250ms ease-in-out;
|
|
||||||
-o-transition: all 250ms ease-in-out;
|
|
||||||
transition: all 250ms ease-in-out;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
header section div.buttons > a.small:hover {
|
|
||||||
background: #86b5e1;
|
|
||||||
border: 1px solid #658fb8;
|
|
||||||
color: #FFFFFF;
|
|
||||||
|
|
||||||
-webkit-box-shadow: 0 7px 15px rgba(0, 0, 0, 0.2);
|
|
||||||
box-shadow: 0 7px 15px rgba(0, 0, 0, 0.2);
|
|
||||||
-webkit-transition: all 250ms ease-in-out;
|
|
||||||
-moz-transition: all 250ms ease-in-out;
|
|
||||||
-o-transition: all 250ms ease-in-out;
|
|
||||||
transition: all 250ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 1100px) {
|
|
||||||
header section {
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section div.buttons > a {
|
|
||||||
margin: 0 auto 20px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section h1 {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section div.logo img {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section > img {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 500px) {
|
|
||||||
/*header {*/
|
|
||||||
/*min-height: 100%;*/
|
|
||||||
/*height: auto;*/
|
|
||||||
/*}*/
|
|
||||||
|
|
||||||
header section {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section div.logo img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
header section div.buttons > a {
|
|
||||||
display: block;
|
|
||||||
padding: 25px 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
@import url('https://fonts.googleapis.com/css?family=Rajdhani:300,400,500,600,700&subset=latin-ext');
|
/*!*
|
||||||
@import "~bootstrap/dist/css/bootstrap.min.css";
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import 'fonts-config';
|
||||||
|
@import 'fontawesome-config';
|
||||||
@import "style";
|
@import "style";
|
||||||
@import "style.homepage";
|
@import "style.homepage";
|
||||||
|
|
||||||
@@ -311,7 +320,6 @@ footer nav ul li {
|
|||||||
}
|
}
|
||||||
|
|
||||||
footer nav ul li:nth-child(even) {
|
footer nav ul li:nth-child(even) {
|
||||||
width: 50px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,8 +400,4 @@ footer nav ul li a:hover {
|
|||||||
footer nav ul li {
|
footer nav ul li {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer nav ul li:nth-child(even) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
170
assets/images/waiting-dialog-design.svg
Normal file
170
assets/images/waiting-dialog-design.svg
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<!---
|
||||||
|
- This file is part of the SplendidBear Websites' projects.
|
||||||
|
-
|
||||||
|
- Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
-
|
||||||
|
- For the full copyright and license information, please view the LICENSE
|
||||||
|
- file that was distributed with this source code.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<svg viewBox="0 0 800 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.bg-dark { fill: #07090d; }
|
||||||
|
.bg-grid { fill: url(#gridPattern); }
|
||||||
|
.border-accent { stroke: rgba(35, 111, 135, 0.4); stroke-width: 2; fill: none; }
|
||||||
|
.shadow { filter: drop-shadow(0 8px 20px rgba(0, 0, 0, 0.4)); }
|
||||||
|
.title-text { font: bold 28px 'Rajdhani', sans-serif; fill: #fff; letter-spacing: 0.5px; }
|
||||||
|
.header-text { font: bold 16px 'Rajdhani', sans-serif; fill: #236f87; letter-spacing: 0.5px; }
|
||||||
|
.desc-text { font: 13px 'Rajdhani', sans-serif; fill: rgba(149, 207, 245, 0.7); letter-spacing: 0.3px; }
|
||||||
|
.divider-line { stroke: rgba(35, 111, 135, 0.25); stroke-width: 1.5; }
|
||||||
|
.divider-text { font: bold 12px 'Rajdhani', sans-serif; fill: rgba(35, 111, 135, 0.5); letter-spacing: 1px; text-transform: uppercase; }
|
||||||
|
.icon-color { fill: rgba(35, 111, 135, 0.9); }
|
||||||
|
.button-gradient { fill: url(#buttonGradient); }
|
||||||
|
.button-text { font: bold 13px 'Rajdhani', sans-serif; fill: #e0f4ff; letter-spacing: 1.5px; text-transform: uppercase; }
|
||||||
|
.glow { filter: drop-shadow(0 0 12px rgba(35, 111, 135, 0.3)); }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- Grid Pattern Background -->
|
||||||
|
<pattern id="gridPattern" x="0" y="0" width="46" height="46" patternUnits="userSpaceOnUse">
|
||||||
|
<path d="M 46 0 L 0 0 0 46" fill="none" stroke="rgba(35, 111, 135, 0.08)" stroke-width="1"/>
|
||||||
|
</pattern>
|
||||||
|
|
||||||
|
<!-- Button Gradient -->
|
||||||
|
<linearGradient id="buttonGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#236f87;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#1a5068;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<!-- Icon Definitions -->
|
||||||
|
<g id="icon-link">
|
||||||
|
<path d="M 12 8 L 20 8 Q 22 8 22 10 L 22 16 Q 22 18 20 18 L 16 18 M 28 12 L 32 12 Q 34 12 34 14 L 34 20 Q 34 22 32 22 L 28 22 M 22 12 L 28 12"
|
||||||
|
stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g id="icon-users">
|
||||||
|
<circle cx="16" cy="10" r="3.5" fill="currentColor"/>
|
||||||
|
<path d="M 12 15 Q 12 13 16 13 Q 20 13 20 15 L 20 18 L 12 18 Z" fill="currentColor"/>
|
||||||
|
<circle cx="30" cy="10" r="3.5" fill="currentColor"/>
|
||||||
|
<path d="M 26 15 Q 26 13 30 13 Q 34 13 34 15 L 34 18 L 26 18 Z" fill="currentColor"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g id="icon-search">
|
||||||
|
<circle cx="18" cy="18" r="8" stroke="currentColor" stroke-width="2" fill="none"/>
|
||||||
|
<line x1="26" y1="26" x2="32" y2="32" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g id="icon-clipboard">
|
||||||
|
<rect x="12" y="8" width="16" height="24" rx="2" stroke="currentColor" stroke-width="2" fill="none"/>
|
||||||
|
<rect x="16" y="6" width="8" height="3" fill="currentColor"/>
|
||||||
|
<line x1="16" y1="14" x2="28" y2="14" stroke="currentColor" stroke-width="1.5"/>
|
||||||
|
<line x1="16" y1="18" x2="28" y2="18" stroke="currentColor" stroke-width="1.5"/>
|
||||||
|
<line x1="16" y1="22" x2="24" y2="22" stroke="currentColor" stroke-width="1.5"/>
|
||||||
|
</g>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Main Background -->
|
||||||
|
<rect class="bg-dark" width="800" height="500"/>
|
||||||
|
<rect class="bg-grid" width="800" height="500"/>
|
||||||
|
|
||||||
|
<!-- Dialog Container -->
|
||||||
|
<g class="shadow">
|
||||||
|
<rect class="bg-dark" x="60" y="40" width="680" height="420" rx="12" class="shadow"/>
|
||||||
|
<rect class="border-accent" x="60" y="40" width="680" height="420" rx="12"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Dialog Header -->
|
||||||
|
<g>
|
||||||
|
<text x="100" y="85" class="title-text">We are waiting...</text>
|
||||||
|
<g transform="translate(720, 60)">
|
||||||
|
<!-- Close Button -->
|
||||||
|
<circle cx="0" cy="0" r="16" stroke="rgba(35, 111, 135, 0.3)" stroke-width="1" fill="none"/>
|
||||||
|
<line x1="-6" y1="-6" x2="6" y2="6" stroke="rgba(149, 207, 245, 0.55)" stroke-width="2" stroke-linecap="round"/>
|
||||||
|
<line x1="6" y1="-6" x2="-6" y2="6" stroke="rgba(149, 207, 245, 0.55)" stroke-width="2" stroke-linecap="round"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Option 1: Invite a Friend -->
|
||||||
|
<g>
|
||||||
|
<!-- Option Container -->
|
||||||
|
<rect x="80" y="120" width="280" height="280" rx="8" fill="rgba(35, 111, 135, 0.05)" stroke="rgba(35, 111, 135, 0.15)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Icon -->
|
||||||
|
<g transform="translate(110, 145)">
|
||||||
|
<circle cx="0" cy="0" r="22" fill="rgba(35, 111, 135, 0.15)"/>
|
||||||
|
<g use="#icon-link" class="icon-color" transform="scale(1.8)"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Title -->
|
||||||
|
<text x="160" y="165" class="header-text">Invite a Friend</text>
|
||||||
|
|
||||||
|
<!-- Description -->
|
||||||
|
<text x="100" y="195" class="desc-text">Share this link with your opponent</text>
|
||||||
|
|
||||||
|
<!-- URL Box -->
|
||||||
|
<rect x="100" y="210" width="240" height="40" rx="6" fill="#d0e8f5" stroke="#7ab8d8" stroke-width="1"/>
|
||||||
|
<text x="118" y="237" style="font: 12px 'Courier New', monospace; fill: #1a4a6a; letter-spacing: 0.3px;">play.mineseeker.com/game-id</text>
|
||||||
|
|
||||||
|
<!-- Copy Button -->
|
||||||
|
<g class="glow">
|
||||||
|
<rect x="100" y="265" width="240" height="40" rx="5" class="button-gradient" stroke="#2e7a9a" stroke-width="1"/>
|
||||||
|
<g transform="translate(120, 285)">
|
||||||
|
<rect x="0" y="0" width="6" height="6" fill="#e0f4ff"/>
|
||||||
|
<rect x="2" y="2" width="4" height="4" fill="none" stroke="#e0f4ff" stroke-width="0.5"/>
|
||||||
|
</g>
|
||||||
|
<text x="140" y="287" class="button-text">Copy Link</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Divider OR -->
|
||||||
|
<g>
|
||||||
|
<line x1="400" y1="200" x2="400" y2="320" class="divider-line"/>
|
||||||
|
<circle cx="400" cy="260" r="18" fill="#07090d" stroke="rgba(35, 111, 135, 0.25)" stroke-width="1"/>
|
||||||
|
<text x="400" y="267" class="divider-text" text-anchor="middle">OR</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Option 2: Challenge a Player -->
|
||||||
|
<g>
|
||||||
|
<!-- Option Container -->
|
||||||
|
<rect x="440" y="120" width="280" height="280" rx="8" fill="rgba(35, 111, 135, 0.05)" stroke="rgba(35, 111, 135, 0.15)" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Icon -->
|
||||||
|
<g transform="translate(470, 145)">
|
||||||
|
<circle cx="0" cy="0" r="22" fill="rgba(35, 111, 135, 0.15)"/>
|
||||||
|
<g use="#icon-users" class="icon-color" transform="scale(1.5)"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Title -->
|
||||||
|
<text x="510" y="165" class="header-text">Challenge a Player</text>
|
||||||
|
|
||||||
|
<!-- Description -->
|
||||||
|
<text x="460" y="195" class="desc-text">Browse online players and challenge</text>
|
||||||
|
|
||||||
|
<!-- Browse Button -->
|
||||||
|
<g class="glow">
|
||||||
|
<rect x="460" y="265" width="240" height="40" rx="5" class="button-gradient" stroke="#2e7a9a" stroke-width="1"/>
|
||||||
|
<g transform="translate(480, 285)">
|
||||||
|
<!-- Search icon simplified -->
|
||||||
|
<circle cx="4" cy="4" r="3" fill="none" stroke="#e0f4ff" stroke-width="1"/>
|
||||||
|
<line x1="7" y1="7" x2="9" y2="9" stroke="#e0f4ff" stroke-width="1" stroke-linecap="round"/>
|
||||||
|
</g>
|
||||||
|
<text x="510" y="287" class="button-text">Browse Players</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Highlight: Players Online -->
|
||||||
|
<g>
|
||||||
|
<text x="460" y="230" class="desc-text" style="font-weight: bold;">5 players waiting</text>
|
||||||
|
<g transform="translate(680, 220)">
|
||||||
|
<circle r="6" fill="#236f87"/>
|
||||||
|
<circle cx="-8" cy="3" r="4" fill="rgba(35, 111, 135, 0.6)"/>
|
||||||
|
<circle cx="8" cy="3" r="4" fill="rgba(35, 111, 135, 0.6)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Bottom Info -->
|
||||||
|
<g>
|
||||||
|
<text x="100" y="435" style="font: 11px 'Rajdhani', sans-serif; fill: rgba(149, 207, 245, 0.35); letter-spacing: 0.5px;">Choose either option to start playing or find an opponent</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 7.5 KiB |
@@ -1,14 +0,0 @@
|
|||||||
/*
|
|
||||||
* Welcome to your app's main JavaScript file!
|
|
||||||
*
|
|
||||||
* We recommend including the built version of this JavaScript file
|
|
||||||
* (and its CSS file) in your base layout (base.html.twig).
|
|
||||||
*/
|
|
||||||
|
|
||||||
// any CSS you require will output into a single css file (app.css in this case)
|
|
||||||
require('../css/app.css');
|
|
||||||
|
|
||||||
// Need jQuery? Install it with "yarn add jquery", then uncomment to require it.
|
|
||||||
// const $ = require('jquery');
|
|
||||||
|
|
||||||
console.log('Hello Webpack Encore! Edit me in assets/js/app.js');
|
|
||||||
21
assets/js/app.jsx
Normal file
21
assets/js/app.jsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import MineSeeker from './mine-seeker/MineSeeker';
|
||||||
|
|
||||||
|
const wrapper = document.getElementById('mine-wrapper');
|
||||||
|
|
||||||
|
createRoot(wrapper).render(
|
||||||
|
<MineSeeker
|
||||||
|
env={wrapper.dataset.env}
|
||||||
|
gameId={wrapper.dataset.gameId}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
71
assets/js/components/AvatarUpload.jsx
Normal file
71
assets/js/components/AvatarUpload.jsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
|
||||||
|
export default function AvatarUpload({ uploadUrl, initialThumbUrl, initials }) {
|
||||||
|
const [thumbUrl, setThumbUrl] = useState(initialThumbUrl || null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const inputRef = useRef(null);
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
inputRef.current?.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(e) {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('avatar', file);
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
fetch(uploadUrl, { method: 'POST', body: fd })
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) {
|
||||||
|
setError(data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setThumbUrl(data.thumbUrl);
|
||||||
|
|
||||||
|
const navImg = document.querySelector('.hero-auth-avatar:not(.hero-auth-avatar--initials)');
|
||||||
|
const navInitials = document.querySelector('.hero-auth-avatar.hero-auth-avatar--initials');
|
||||||
|
if (navImg) {
|
||||||
|
navImg.src = data.thumbUrl;
|
||||||
|
} else if (navInitials) {
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.src = data.thumbUrl;
|
||||||
|
img.alt = navInitials.textContent.trim();
|
||||||
|
img.className = 'hero-auth-avatar';
|
||||||
|
navInitials.replaceWith(img);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => setError('Upload failed. Please try again.'))
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`profile-avatar${loading ? ' profile-avatar--loading' : ''}`}
|
||||||
|
title="Click to change profile picture"
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
{thumbUrl
|
||||||
|
? <img src={thumbUrl} alt={initials} className="profile-avatar__img" />
|
||||||
|
: <span className="profile-avatar__initials">{initials}</span>
|
||||||
|
}
|
||||||
|
<div className="profile-avatar__overlay">
|
||||||
|
<i className="fa fa-camera" />
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="file"
|
||||||
|
accept="image/jpeg,image/png,image/gif,image/webp"
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
{error && <div className="profile-avatar__error">{error}</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
338
assets/js/components/BattleDialog.jsx
Normal file
338
assets/js/components/BattleDialog.jsx
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
||||||
|
|
||||||
|
const darkTheme = createTheme({ palette: { mode: 'dark' } });
|
||||||
|
|
||||||
|
const DIALOG_SX = {
|
||||||
|
'& .MuiDialog-paper': {
|
||||||
|
background: '#07090d',
|
||||||
|
backgroundImage: `
|
||||||
|
linear-gradient(rgba(35, 111, 135, 0.08) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(35, 111, 135, 0.08) 1px, transparent 1px)
|
||||||
|
`,
|
||||||
|
backgroundSize: '46px 46px',
|
||||||
|
border: '1px solid rgba(35, 111, 135, 0.4)',
|
||||||
|
borderRadius: '12px',
|
||||||
|
boxShadow: '0 0 80px rgba(35, 111, 135, 0.15), 0 32px 80px rgba(0,0,0,0.9)',
|
||||||
|
width: '580px',
|
||||||
|
maxWidth: '94vw',
|
||||||
|
overflow: 'hidden',
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
'& .MuiBackdrop-root': {
|
||||||
|
background: 'rgba(2, 4, 8, 0.88)',
|
||||||
|
backdropFilter: 'blur(4px)',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const RESULT_META = {
|
||||||
|
win: {
|
||||||
|
label: 'Victory',
|
||||||
|
color: '#5ee89a',
|
||||||
|
bg: 'rgba(42,158,96,0.15)',
|
||||||
|
border: 'rgba(42,158,96,0.4)',
|
||||||
|
icon: 'fa-trophy',
|
||||||
|
},
|
||||||
|
loss: {
|
||||||
|
label: 'Defeated',
|
||||||
|
color: '#f67d52',
|
||||||
|
bg: 'rgba(173,10,5,0.15)',
|
||||||
|
border: 'rgba(173,10,5,0.4)',
|
||||||
|
icon: 'fa-flag',
|
||||||
|
},
|
||||||
|
draw: {
|
||||||
|
label: 'Draw',
|
||||||
|
color: '#95cff5',
|
||||||
|
bg: 'rgba(149,207,245,0.1)',
|
||||||
|
border: 'rgba(149,207,245,0.3)',
|
||||||
|
icon: 'fa-minus',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function Avatar({ name, color, avatarUrl, bonusPoints = 0 }) {
|
||||||
|
const isRed = 'red' === color;
|
||||||
|
const initials = (name || '?').slice(0, 2).toUpperCase();
|
||||||
|
|
||||||
|
const gradient = isRed
|
||||||
|
? 'linear-gradient(135deg, rgba(173,10,5,0.6) 0%, rgba(246,125,82,0.4) 100%)'
|
||||||
|
: 'linear-gradient(135deg, rgba(35,111,135,0.6) 0%, rgba(41,128,185,0.4) 100%)';
|
||||||
|
const glow = isRed
|
||||||
|
? '0 0 0 3px rgba(173,10,5,0.2), 0 0 28px rgba(173,10,5,0.35)'
|
||||||
|
: '0 0 0 3px rgba(35,111,135,0.2), 0 0 28px rgba(35,111,135,0.35)';
|
||||||
|
const border = isRed
|
||||||
|
? 'rgba(173,10,5,0.5)'
|
||||||
|
: 'rgba(35,111,135,0.5)';
|
||||||
|
const textColor = isRed ? '#f67d52' : '#95cff5';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 10, position: 'relative' }}>
|
||||||
|
<div style={{ position: 'relative' }}>
|
||||||
|
<div style={{
|
||||||
|
width: 72, height: 72, borderRadius: '50%',
|
||||||
|
background: avatarUrl ? 'transparent' : gradient,
|
||||||
|
border: `2px solid ${border}`,
|
||||||
|
boxShadow: glow,
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
font: '800 24px \'Rajdhani\', sans-serif',
|
||||||
|
color: textColor,
|
||||||
|
letterSpacing: 2,
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{avatarUrl ? (
|
||||||
|
<img
|
||||||
|
src={avatarUrl}
|
||||||
|
alt={name}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
objectFit: 'cover',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
initials
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{0 < bonusPoints && (
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: -6,
|
||||||
|
right: -6,
|
||||||
|
background: '#ffd700',
|
||||||
|
borderRadius: '50%',
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
boxShadow: '0 0 12px rgba(255, 215, 0, 0.6), 0 0 0 2px rgba(7, 9, 13, 1)',
|
||||||
|
border: '2px solid rgba(0,0,0,0.5)',
|
||||||
|
zIndex: 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="fa fa-star" style={{ color: '#000', fontSize: 14 }} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span style={{
|
||||||
|
font: '700 15px \'Rajdhani\', sans-serif',
|
||||||
|
color: textColor,
|
||||||
|
letterSpacing: 1,
|
||||||
|
maxWidth: 120, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
<span style={{
|
||||||
|
font: '600 10px \'Rajdhani\', sans-serif',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: 2,
|
||||||
|
color: 'rgba(255,255,255,0.3)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isRed ? 'Red' : 'Blue'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatRow({ icon, label, value, valueColor }) {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
display: 'flex', alignItems: 'center',
|
||||||
|
gap: 10, padding: '9px 0',
|
||||||
|
borderBottom: '1px solid rgba(255,255,255,0.05)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className={`fa ${icon}`} style={{ width: 16, color: 'rgba(149,207,245,0.4)', fontSize: 13 }} />
|
||||||
|
<span style={{
|
||||||
|
font: '500 13px \'Rajdhani\', sans-serif',
|
||||||
|
color: 'rgba(255,255,255,0.45)',
|
||||||
|
flex: 1,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
<span style={{
|
||||||
|
font: '700 13px \'Rajdhani\', sans-serif',
|
||||||
|
color: valueColor || 'rgba(255,255,255,0.75)',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BattleDialog({ games }) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [game, setGame] = useState(null);
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = e => {
|
||||||
|
const row = e.target.closest('[data-game-index]');
|
||||||
|
if (!row) return;
|
||||||
|
const idx = parseInt(row.dataset.gameIndex, 10);
|
||||||
|
if (!isNaN(idx) && games[idx]) {
|
||||||
|
setGame(games[idx]);
|
||||||
|
setOpen(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('click', handler);
|
||||||
|
return () => document.removeEventListener('click', handler);
|
||||||
|
}, [games]);
|
||||||
|
|
||||||
|
if (!game) {
|
||||||
|
return <ThemeProvider theme={darkTheme}><Dialog open={false} sx={DIALOG_SX} /></ThemeProvider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const meta = RESULT_META[game.result] ?? RESULT_META.draw;
|
||||||
|
const resign = game.resign;
|
||||||
|
const endReason = resign
|
||||||
|
? `${resign.charAt(0).toUpperCase() + resign.slice(1)} resigned`
|
||||||
|
: 'Points';
|
||||||
|
const shareUrl = `${window.location.origin}/battle/${game.uuid}`;
|
||||||
|
|
||||||
|
const handleShare = () => {
|
||||||
|
navigator.clipboard.writeText(shareUrl).then(() => {
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => setCopied(false), 2200);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={darkTheme}>
|
||||||
|
<Dialog open={open} onClose={() => setOpen(false)} sx={DIALOG_SX}>
|
||||||
|
<div className="bd">
|
||||||
|
<div className="bd-header">
|
||||||
|
<div className="bd-header-left">
|
||||||
|
<span className="bd-label">Battle Report</span>
|
||||||
|
<h2 className="bd-title">
|
||||||
|
<i className="fa fa-crosshairs" /> Match Details
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', gap: 8 }}>
|
||||||
|
<button
|
||||||
|
className={`bd-share${copied ? ' bd-share--copied' : ''}`}
|
||||||
|
onClick={handleShare}
|
||||||
|
aria-label="Copy share link"
|
||||||
|
title="Copy share link"
|
||||||
|
>
|
||||||
|
<i className={`fa ${copied ? 'fa-check' : 'fa-share-alt'}`} />
|
||||||
|
{copied ? 'Copied!' : 'Share'}
|
||||||
|
</button>
|
||||||
|
<button className="bd-close" onClick={() => setOpen(false)} aria-label="Close">
|
||||||
|
<i className="fa fa-times" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bd-vs-panel">
|
||||||
|
<Avatar name={game.redName} color="red" avatarUrl={game.redAvatar} bonusPoints={game.redBonusPoints > game.blueBonusPoints ? game.redBonusPoints : 0} />
|
||||||
|
<div className="bd-vs-center">
|
||||||
|
<div className="bd-vs-score">
|
||||||
|
<span className="bd-vs-score__red">{game.redPoints ?? '—'}</span>
|
||||||
|
<span className="bd-vs-score__sep">:</span>
|
||||||
|
<span className="bd-vs-score__blue">{game.bluePoints ?? '—'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="bd-vs-score" style={{ marginBottom: 8 }}>
|
||||||
|
<span style={{ font: '700 13px \'Rajdhani\', sans-serif', color: '#f67d52', display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||||
|
<i className="fa fa-star" style={{ fontSize: 11 }} /> {(game.redBonusPoints ?? 0).toFixed(1)}
|
||||||
|
</span>
|
||||||
|
<span className="bd-vs-score__sep">:</span>
|
||||||
|
<span style={{ font: '700 13px \'Rajdhani\', sans-serif', color: '#95cff5', display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||||
|
{(game.blueBonusPoints ?? 0).toFixed(1)} <i className="fa fa-star" style={{ fontSize: 11 }} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="bd-vs-label">VS</div>
|
||||||
|
<div
|
||||||
|
className="bd-result-badge"
|
||||||
|
style={{ background: meta.bg, border: `1px solid ${meta.border}`, color: meta.color }}
|
||||||
|
>
|
||||||
|
<i className={`fa ${meta.icon}`} /> {meta.label}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Avatar name={game.blueName} color="blue" avatarUrl={game.blueAvatar} bonusPoints={game.blueBonusPoints > game.redBonusPoints ? game.blueBonusPoints : 0} />
|
||||||
|
</div>
|
||||||
|
<div className="bd-stats">
|
||||||
|
<StatRow icon="fa-calendar" label="Date" value={game.date ?? '—'} />
|
||||||
|
<StatRow icon="fa-flag-checkered" label="End reason" value={endReason} />
|
||||||
|
<StatRow
|
||||||
|
icon="fa-bomb" label="Red hit a mine"
|
||||||
|
value={game.redExplodedBomb ? 'Yes' : 'No'}
|
||||||
|
valueColor={game.redExplodedBomb ? '#f67d52' : 'rgba(255,255,255,0.45)'}
|
||||||
|
/>
|
||||||
|
<StatRow
|
||||||
|
icon="fa-bomb" label="Blue hit a mine"
|
||||||
|
value={game.blueExplodedBomb ? 'Yes' : 'No'}
|
||||||
|
valueColor={game.blueExplodedBomb ? '#f67d52' : 'rgba(255,255,255,0.45)'}
|
||||||
|
/>
|
||||||
|
{game.created && game.date && game.created !== game.date && (
|
||||||
|
<StatRow icon="fa-clock-o" label="Started" value={game.created} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(0 < game.redBonusPoints
|
||||||
|
|| 0 < game.blueBonusPoints
|
||||||
|
|| game.redBonusStats?.blindHits
|
||||||
|
|| game.blueBonusStats?.blindHits
|
||||||
|
) && (
|
||||||
|
<div style={{ padding: '16px 20px 0', borderTop: '1px solid rgba(255,255,255,0.08)', marginTop: 16, marginBottom: 16 }}>
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
|
||||||
|
{/* Red Bonus */}
|
||||||
|
<div style={{
|
||||||
|
padding: 16,
|
||||||
|
border: '1px solid rgba(173,10,5,0.2)',
|
||||||
|
borderRadius: 6,
|
||||||
|
background: 'rgba(173,10,5,0.05)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ font: '700 12px \'Rajdhani\', sans-serif', textTransform: 'uppercase', letterSpacing: 2, color: '#ffd700', display: 'block', marginBottom: 12 }}>
|
||||||
|
<i className="fa fa-star" style={{ marginRight: 8 }} /> Red Bonus Statistics
|
||||||
|
</span>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
|
||||||
|
<StatRow icon="fa-star" label="Total Bonus Points" value={(game.redBonusPoints ?? 0).toFixed(1)} valueColor="#ffd700" />
|
||||||
|
{0 < game.redBonusStats?.blindHits && <StatRow icon="fa-bullseye" label="Blind hits" value={game.redBonusStats.blindHits} />}
|
||||||
|
{0 < game.redBonusStats?.chainBest && <StatRow icon="fa-link" label="Best chain" value={game.redBonusStats.chainBest} />}
|
||||||
|
{0 < game.redBonusStats?.edgeMines && <StatRow icon="fa-border" label="Edge mines" value={game.redBonusStats.edgeMines} />}
|
||||||
|
{0 < game.redBonusStats?.lastMineHits && <StatRow icon="fa-hourglass-end" label="Endgame mines" value={game.redBonusStats.lastMineHits} />}
|
||||||
|
{0 < game.redBonusStats?.biggestReveal && <StatRow icon="fa-expand" label="Biggest reveal" value={game.redBonusStats.biggestReveal} />}
|
||||||
|
{!game.redBonusStats?.blindHits && !game.redBonusStats?.chainBest && !game.redBonusStats?.edgeMines && !game.redBonusStats?.lastMineHits && !game.redBonusStats?.biggestReveal
|
||||||
|
&& <StatRow icon="fa-minus-circle" label="Status" value="No bonuses" valueColor="rgba(255,255,255,0.3)" />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Blue Bonus */}
|
||||||
|
<div style={{
|
||||||
|
padding: 16,
|
||||||
|
border: '1px solid rgba(149,207,245,0.2)',
|
||||||
|
borderRadius: 6,
|
||||||
|
background: 'rgba(149,207,245,0.05)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ font: '700 12px \'Rajdhani\', sans-serif', textTransform: 'uppercase', letterSpacing: 2, color: '#ffd700', display: 'block', marginBottom: 12 }}>
|
||||||
|
<i className="fa fa-star" style={{ marginRight: 8 }} /> Blue Bonus Statistics
|
||||||
|
</span>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
|
||||||
|
<StatRow icon="fa-star" label="Total Bonus Points" value={(game.blueBonusPoints ?? 0).toFixed(1)} valueColor="#ffd700" />
|
||||||
|
{0 < game.blueBonusStats?.blindHits && <StatRow icon="fa-bullseye" label="Blind hits" value={game.blueBonusStats.blindHits} />}
|
||||||
|
{0 < game.blueBonusStats?.chainBest && <StatRow icon="fa-link" label="Best chain" value={game.blueBonusStats.chainBest} />}
|
||||||
|
{0 < game.blueBonusStats?.edgeMines && <StatRow icon="fa-border" label="Edge mines" value={game.blueBonusStats.edgeMines} />}
|
||||||
|
{0 < game.blueBonusStats?.lastMineHits && <StatRow icon="fa-hourglass-end" label="Endgame mines" value={game.blueBonusStats.lastMineHits} />}
|
||||||
|
{0 < game.blueBonusStats?.biggestReveal && <StatRow icon="fa-expand" label="Biggest reveal" value={game.blueBonusStats.biggestReveal} />}
|
||||||
|
{!game.blueBonusStats?.blindHits && !game.blueBonusStats?.chainBest && !game.blueBonusStats?.edgeMines && !game.blueBonusStats?.lastMineHits && !game.blueBonusStats?.biggestReveal
|
||||||
|
&& <StatRow icon="fa-minus-circle" label="Status" value="No bonuses" valueColor="rgba(255,255,255,0.3)" />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
83
assets/js/components/ContactForm.jsx
Normal file
83
assets/js/components/ContactForm.jsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ContactForm Component
|
||||||
|
*
|
||||||
|
* Handles reCAPTCHA v3 integration for the contact form.
|
||||||
|
* Intercepts form submission, executes reCAPTCHA, and submits the form with the token.
|
||||||
|
*
|
||||||
|
* @param {string} siteKey - Google reCAPTCHA site key
|
||||||
|
* @param {string} recaptchaFieldId - ID of the hidden recaptcha input field
|
||||||
|
*/
|
||||||
|
const ContactForm = ({ siteKey, recaptchaFieldId }) => {
|
||||||
|
const formRef = useRef(null);
|
||||||
|
const isSubmittingRef = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const form = document.querySelector('.auth-form');
|
||||||
|
|
||||||
|
if (!form) {
|
||||||
|
console.warn('ContactForm: No .auth-form found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
formRef.current = form;
|
||||||
|
|
||||||
|
const handleSubmit = e => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (isSubmittingRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubmittingRef.current = true;
|
||||||
|
|
||||||
|
if ('undefined' !== typeof grecaptcha) {
|
||||||
|
grecaptcha.ready(() => {
|
||||||
|
grecaptcha
|
||||||
|
.execute(siteKey, { action: 'contact' })
|
||||||
|
.then(token => {
|
||||||
|
const recaptchaField = document.getElementById(recaptchaFieldId);
|
||||||
|
|
||||||
|
if (recaptchaField) {
|
||||||
|
recaptchaField.value = token;
|
||||||
|
} else {
|
||||||
|
console.error(`ContactForm: Recaptcha field with ID "${recaptchaFieldId}" not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubmittingRef.current = false;
|
||||||
|
form.submit();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('ContactForm: reCAPTCHA execution failed', error);
|
||||||
|
isSubmittingRef.current = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('ContactForm: grecaptcha is not loaded');
|
||||||
|
isSubmittingRef.current = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
form.addEventListener('submit', handleSubmit);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (formRef.current) {
|
||||||
|
formRef.current.removeEventListener('submit', handleSubmit);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [siteKey, recaptchaFieldId]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContactForm;
|
||||||
110
assets/js/components/PasskeyLogin.jsx
Normal file
110
assets/js/components/PasskeyLogin.jsx
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useState, useCallback } from 'react';
|
||||||
|
|
||||||
|
const base64ToArrayBuffer = base64 => {
|
||||||
|
const binary = atob(base64.replace(/([-_])/g, m => ('-' === m ? '+' : '/')));
|
||||||
|
const bytes = new Uint8Array(binary.length);
|
||||||
|
for (let i = 0; i < binary.length; i++) {
|
||||||
|
bytes[i] = binary.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return bytes.buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
const arrayBufferToBase64url = buffer =>
|
||||||
|
btoa(String.fromCharCode(...new Uint8Array(buffer)))
|
||||||
|
.replace(/([+\/=])/g, m => ('+' === m ? '-' : '/' === m ? '_' : ''));
|
||||||
|
|
||||||
|
const credentialToJSON = credential => ({
|
||||||
|
id: credential.id,
|
||||||
|
rawId: arrayBufferToBase64url(credential.rawId),
|
||||||
|
type: credential.type,
|
||||||
|
response: {
|
||||||
|
authenticatorData: arrayBufferToBase64url(credential.response.authenticatorData),
|
||||||
|
clientDataJSON: arrayBufferToBase64url(credential.response.clientDataJSON),
|
||||||
|
signature: arrayBufferToBase64url(credential.response.signature),
|
||||||
|
userHandle: credential.response.userHandle
|
||||||
|
? arrayBufferToBase64url(credential.response.userHandle)
|
||||||
|
: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const PasskeyLogin = ({ apiRoutes }) => {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
|
const handleLogin = useCallback(async () => {
|
||||||
|
setLoading(true);
|
||||||
|
setError('');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const beginResponse = await fetch(apiRoutes.authenticationBegin, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!beginResponse.ok) throw new Error('Failed to get authentication options');
|
||||||
|
const options = await beginResponse.json();
|
||||||
|
|
||||||
|
const publicKey = {
|
||||||
|
...options,
|
||||||
|
challenge: base64ToArrayBuffer(options.challenge),
|
||||||
|
allowCredentials: (options.allowCredentials ?? []).map(cred => ({
|
||||||
|
...cred,
|
||||||
|
id: base64ToArrayBuffer(cred.id),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
const credential = await navigator.credentials.get({ publicKey });
|
||||||
|
if (!credential) throw new Error('Authentication was cancelled');
|
||||||
|
|
||||||
|
const completeResponse = await fetch(apiRoutes.authenticationComplete, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ credential: credentialToJSON(credential) }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!completeResponse.ok) {
|
||||||
|
const body = await completeResponse.json();
|
||||||
|
throw new Error(body.error || 'Authentication failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await completeResponse.json();
|
||||||
|
if (result.success) {
|
||||||
|
window.location.href = result.redirect || '/';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setError(err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
}, [apiRoutes]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="auth-passkey-btn"
|
||||||
|
onClick={handleLogin}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
<i className={loading ? 'fa fa-spinner fa-spin' : 'fa fa-key'} />
|
||||||
|
{loading ? 'Waiting for passkey…' : 'Sign In with Passkey'}
|
||||||
|
</button>
|
||||||
|
{error && (
|
||||||
|
<p className="auth-error" style={{ marginTop: '10px' }}>
|
||||||
|
<i className="fa fa-exclamation-triangle" /> {error}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PasskeyLogin;
|
||||||
410
assets/js/components/PasskeyManager.jsx
Normal file
410
assets/js/components/PasskeyManager.jsx
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { Fragment, useCallback, useEffect, useState } from 'react';
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
import DialogActions from '@mui/material/DialogActions';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
|
||||||
|
const DIALOG_SX = {
|
||||||
|
'& .MuiDialog-paper': {
|
||||||
|
background: '#0a0e14',
|
||||||
|
color: '#e0e0e0',
|
||||||
|
},
|
||||||
|
'& .MuiTextField-root .MuiInputLabel-root': {
|
||||||
|
color: '#9e9e9e',
|
||||||
|
},
|
||||||
|
'& .MuiTextField-root .MuiOutlinedInput-root': {
|
||||||
|
'& fieldset': {
|
||||||
|
borderColor: 'rgba(35, 111, 135, 0.5)',
|
||||||
|
},
|
||||||
|
'&:hover fieldset': {
|
||||||
|
borderColor: 'rgba(35, 111, 135, 0.8)',
|
||||||
|
},
|
||||||
|
'&.Mui-focused fieldset': {
|
||||||
|
borderColor: '#236f87',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'& .MuiTextField-root .MuiOutlinedInput-input': {
|
||||||
|
color: '#e0e0e0',
|
||||||
|
},
|
||||||
|
'& .MuiFormHelperText-root': {
|
||||||
|
width: '500px',
|
||||||
|
maxWidth: '94vw',
|
||||||
|
overflow: 'hidden',
|
||||||
|
color: '#e0e0e0',
|
||||||
|
},
|
||||||
|
'& .MuiBackdrop-root': {
|
||||||
|
background: 'rgba(2, 4, 8, 0.88)',
|
||||||
|
backdropFilter: 'blur(4px)',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const base64ToArrayBuffer = base64 => {
|
||||||
|
const binary = atob(base64.replace(/([-_])/g, m => ('-' === m ? '+' : '/')));
|
||||||
|
const bytes = new Uint8Array(binary.length);
|
||||||
|
for (let i = 0; i < binary.length; i++) {
|
||||||
|
bytes[i] = binary.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return bytes.buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
const credentialToJSON = credential => {
|
||||||
|
const attestationObject = credential.response.attestationObject;
|
||||||
|
const clientDataJSON = credential.response.clientDataJSON;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: credential.id,
|
||||||
|
rawId: btoa(String.fromCharCode(...new Uint8Array(credential.rawId))).replace(/([+\/=])/g, m => ('+' === m ? '-' : '/' === m ? '_' : '')),
|
||||||
|
type: credential.type,
|
||||||
|
response: {
|
||||||
|
attestationObject: btoa(String.fromCharCode(...new Uint8Array(attestationObject))).replace(/([+\/=])/g, m => ('+' === m ? '-' : '/' === m ? '_' : '')),
|
||||||
|
clientDataJSON: btoa(String.fromCharCode(...new Uint8Array(clientDataJSON))).replace(/([+\/=])/g, m => ('+' === m ? '-' : '/' === m ? '_' : '')),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const PasskeyManager = ({ credentials, apiRoutes }) => {
|
||||||
|
const [addModalOpen, setAddModalOpen] = useState(false);
|
||||||
|
const [renameModalOpen, setRenameModalOpen] = useState(false);
|
||||||
|
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
||||||
|
const [selectedCredential, setSelectedCredential] = useState(null);
|
||||||
|
const [passkeyName, setPasskeyName] = useState('');
|
||||||
|
const [renameName, setRenameName] = useState('');
|
||||||
|
const [status, setStatus] = useState({ type: '', message: '' });
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [credentialsList, setCredentialsList] = useState(credentials);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCredentialsList(credentials);
|
||||||
|
}, [credentials]);
|
||||||
|
|
||||||
|
const showStatus = useCallback((message, type) => {
|
||||||
|
setStatus({ message, type });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const closeAddModal = useCallback(() => {
|
||||||
|
setAddModalOpen(false);
|
||||||
|
setPasskeyName('');
|
||||||
|
setStatus({ type: '', message: '' });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const openAddModal = useCallback(() => {
|
||||||
|
setPasskeyName('');
|
||||||
|
setStatus({ type: '', message: '' });
|
||||||
|
setAddModalOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleAddPasskey = useCallback(async () => {
|
||||||
|
if (!passkeyName.trim()) {
|
||||||
|
showStatus('Please enter a passkey name', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
showStatus('Starting registration...', 'loading');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const optionsResponse = await fetch(apiRoutes.registrationBegin, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ credentialName: passkeyName.trim() }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!optionsResponse.ok) throw new Error('Failed to get registration options');
|
||||||
|
const options = await optionsResponse.json();
|
||||||
|
|
||||||
|
showStatus('Please touch your security key or use biometric authentication...', 'loading');
|
||||||
|
|
||||||
|
const publicKey = {
|
||||||
|
...options,
|
||||||
|
challenge: base64ToArrayBuffer(options.challenge),
|
||||||
|
user: {
|
||||||
|
...options.user,
|
||||||
|
id: base64ToArrayBuffer(options.user.id),
|
||||||
|
},
|
||||||
|
attestation: 'direct',
|
||||||
|
};
|
||||||
|
|
||||||
|
const credential = await navigator.credentials.create({ publicKey });
|
||||||
|
|
||||||
|
if (!credential) {
|
||||||
|
showStatus('Registration was cancelled', 'error');
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showStatus('Verifying credential...', 'loading');
|
||||||
|
|
||||||
|
const completeResponse = await fetch(apiRoutes.registrationComplete, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
credential: credentialToJSON(credential),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!completeResponse.ok) throw new Error('Failed to complete registration');
|
||||||
|
|
||||||
|
showStatus('Passkey registered successfully!', 'success');
|
||||||
|
setTimeout(() => window.location.reload(), 1500);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Registration error:', error);
|
||||||
|
showStatus('Error: ' + error.message, 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
}, [passkeyName, apiRoutes, showStatus]);
|
||||||
|
|
||||||
|
const openRenameModal = useCallback(credential => {
|
||||||
|
setSelectedCredential(credential);
|
||||||
|
setRenameName(credential.credentialName);
|
||||||
|
setStatus({ type: '', message: '' });
|
||||||
|
setRenameModalOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const closeRenameModal = useCallback(() => {
|
||||||
|
setRenameModalOpen(false);
|
||||||
|
setSelectedCredential(null);
|
||||||
|
setRenameName('');
|
||||||
|
setStatus({ type: '', message: '' });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleRename = useCallback(async () => {
|
||||||
|
if (!renameName.trim() || !selectedCredential) {
|
||||||
|
showStatus('Please enter a new name', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${apiRoutes.credentials}/${selectedCredential.id}/rename`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ name: renameName.trim() }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error('Failed to rename passkey');
|
||||||
|
|
||||||
|
window.location.reload();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Rename error:', error);
|
||||||
|
showStatus('Error: ' + error.message, 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
}, [renameName, selectedCredential, apiRoutes, showStatus]);
|
||||||
|
|
||||||
|
const openDeleteModal = useCallback(credential => {
|
||||||
|
setSelectedCredential(credential);
|
||||||
|
setDeleteModalOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const closeDeleteModal = useCallback(() => {
|
||||||
|
setDeleteModalOpen(false);
|
||||||
|
setSelectedCredential(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDelete = useCallback(async () => {
|
||||||
|
if (!selectedCredential) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${apiRoutes.credentials}/${selectedCredential.id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
showStatus('Failed to delete passkey', 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Delete error:', error);
|
||||||
|
showStatus('Error: ' + error.message, 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
setDeleteModalOpen(false);
|
||||||
|
}, [selectedCredential, apiRoutes, showStatus]);
|
||||||
|
|
||||||
|
const statusClass = 'error' === status.type
|
||||||
|
? 'registration-status--error'
|
||||||
|
: 'success' === status.type
|
||||||
|
? 'registration-status--success'
|
||||||
|
: 'loading' === status.type
|
||||||
|
? 'registration-status--loading'
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="passkey-manager">
|
||||||
|
<div className="passkey-manager__actions">
|
||||||
|
<button className="btn btn--primary" onClick={openAddModal} type="button">
|
||||||
|
<i className="fa fa-plus" /> Add Passkey
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="passkey-manager__list">
|
||||||
|
{0 < credentialsList.length ? (
|
||||||
|
credentialsList.map(credential => (
|
||||||
|
<div key={credential.id} className="passkey-item" data-credential-id={credential.id}>
|
||||||
|
<div className="passkey-item__header">
|
||||||
|
<div className="passkey-item__info">
|
||||||
|
<h3 className="passkey-item__name">{credential.credentialName}</h3>
|
||||||
|
<p className="passkey-item__meta">
|
||||||
|
Created: {credential.createdAt}
|
||||||
|
{credential.lastUsedAt && (
|
||||||
|
<>
|
||||||
|
<br />Last used: {credential.lastUsedAt}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="passkey-item__badges">
|
||||||
|
{credential.isBackupEligible && (
|
||||||
|
<span className="badge badge--info" title="This passkey can be backed up">
|
||||||
|
<i className="fa fa-cloud" /> Backup Eligible
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{credential.isBackupAuthenticated && (
|
||||||
|
<span className="badge badge--success" title="This passkey is backed up">
|
||||||
|
<i className="fa fa-check" /> Backed Up
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="passkey-item__actions">
|
||||||
|
<button
|
||||||
|
className="btn btn--sm btn--warning"
|
||||||
|
onClick={() => openRenameModal(credential)}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i className="fa fa-edit" /> Rename
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn--sm btn--danger"
|
||||||
|
onClick={() => openDeleteModal(credential)}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i className="fa fa-trash" /> Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="empty-state">
|
||||||
|
<i className="fa fa-key empty-state__icon" />
|
||||||
|
<p className="empty-state__text">No passkeys registered yet.</p>
|
||||||
|
<p className="empty-state__subtext">
|
||||||
|
Add your first passkey to get started with secure, passwordless authentication.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Dialog open={addModalOpen} onClose={closeAddModal} sx={DIALOG_SX}>
|
||||||
|
<DialogTitle>Add New Passkey</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<TextField
|
||||||
|
autoFocus
|
||||||
|
fullWidth
|
||||||
|
id="passkeyName"
|
||||||
|
label="Passkey Name"
|
||||||
|
placeholder="e.g., My Laptop, iPhone"
|
||||||
|
value={passkeyName}
|
||||||
|
onChange={e => setPasskeyName(e.target.value)}
|
||||||
|
onKeyUp={e => 'Enter' === e.key && handleAddPasskey()}
|
||||||
|
margin="dense"
|
||||||
|
variant="outlined"
|
||||||
|
helperText="Give this passkey a descriptive name to help you remember it."
|
||||||
|
/>
|
||||||
|
{status.message && (
|
||||||
|
<div className={`registration-status ${statusClass}`}>
|
||||||
|
{status.message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={closeAddModal} disabled={loading}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleAddPasskey} variant="contained" disabled={loading}>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog open={renameModalOpen} onClose={closeRenameModal} sx={DIALOG_SX}>
|
||||||
|
<DialogTitle>Rename Passkey</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<TextField
|
||||||
|
autoFocus
|
||||||
|
fullWidth
|
||||||
|
id="renamePasskeyName"
|
||||||
|
label="New Name"
|
||||||
|
value={renameName}
|
||||||
|
onChange={e => setRenameName(e.target.value)}
|
||||||
|
onKeyUp={e => 'Enter' === e.key && handleRename()}
|
||||||
|
margin="dense"
|
||||||
|
variant="outlined"
|
||||||
|
/>
|
||||||
|
{status.message && (
|
||||||
|
<div className={`registration-status ${statusClass}`}>
|
||||||
|
{status.message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={closeRenameModal} disabled={loading}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleRename} variant="contained" disabled={loading}>
|
||||||
|
Rename
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog open={deleteModalOpen} onClose={closeDeleteModal} sx={DIALOG_SX}>
|
||||||
|
<DialogTitle>Delete Passkey</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<p>
|
||||||
|
Are you sure you want to delete the passkey
|
||||||
|
{selectedCredential && (
|
||||||
|
<Fragment>
|
||||||
|
<strong>{selectedCredential.credentialName}</strong>?
|
||||||
|
This action cannot be undone.
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
{status.message && (
|
||||||
|
<div className={`registration-status ${statusClass}`}>
|
||||||
|
{status.message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={closeDeleteModal} disabled={loading}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleDelete} variant="contained" color="error" disabled={loading}>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PasskeyManager;
|
||||||
103
assets/js/components/ProfileCharts.jsx
Normal file
103
assets/js/components/ProfileCharts.jsx
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { BarChart } from '@mui/x-charts/BarChart';
|
||||||
|
import { PieChart } from '@mui/x-charts/PieChart';
|
||||||
|
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
||||||
|
|
||||||
|
const darkTheme = createTheme({
|
||||||
|
palette: {
|
||||||
|
mode: 'dark',
|
||||||
|
background: { paper: 'transparent' },
|
||||||
|
},
|
||||||
|
typography: {
|
||||||
|
fontFamily: '\'Rajdhani\', sans-serif',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const WIN_COLOR = '#5ee89a';
|
||||||
|
const LOSS_COLOR = '#f67d52';
|
||||||
|
const DRAW_COLOR = '#95cff5';
|
||||||
|
|
||||||
|
const axisStyle = {
|
||||||
|
tickLabelStyle: { fill: 'rgba(255,255,255,0.4)', fontSize: 11, fontFamily: '\'Rajdhani\', sans-serif' },
|
||||||
|
labelStyle: { fill: 'rgba(255,255,255,0.4)', fontSize: 11, fontFamily: '\'Rajdhani\', sans-serif' },
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ProfileCharts({ chartData }) {
|
||||||
|
const { months, wins, losses, draws, pieWins, pieLosses, pieDraws } = chartData;
|
||||||
|
const total = pieWins + pieLosses + pieDraws;
|
||||||
|
|
||||||
|
const hasBars = wins.some(v => 0 < v) || losses.some(v => 0 < v) || draws.some(v => 0 < v);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={darkTheme}>
|
||||||
|
<div className="profile-charts">
|
||||||
|
{0 < total && (
|
||||||
|
<div className="profile-chart-block">
|
||||||
|
<h2 className="profile-section__title">
|
||||||
|
<i className="fa fa-pie-chart" /> Result Breakdown
|
||||||
|
</h2>
|
||||||
|
<div className="profile-chart-inner">
|
||||||
|
<PieChart
|
||||||
|
series={[{
|
||||||
|
data: [
|
||||||
|
{ id: 0, value: pieWins, label: `Wins ${pieWins}`, color: WIN_COLOR },
|
||||||
|
{ id: 1, value: pieLosses, label: `Losses ${pieLosses}`, color: LOSS_COLOR },
|
||||||
|
{ id: 2, value: pieDraws, label: `Draws ${pieDraws}`, color: DRAW_COLOR },
|
||||||
|
],
|
||||||
|
innerRadius: 52,
|
||||||
|
paddingAngle: 3,
|
||||||
|
cornerRadius: 4,
|
||||||
|
highlightScope: { fade: 'global', highlight: 'item' },
|
||||||
|
}]}
|
||||||
|
slotProps={{
|
||||||
|
legend: {
|
||||||
|
labelStyle: {
|
||||||
|
fill: 'rgba(255,255,255,0.55)',
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: '\'Rajdhani\', sans-serif',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
width={320}
|
||||||
|
height={200}
|
||||||
|
margin={{ top: 10, bottom: 10, left: 10, right: 120 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasBars && (
|
||||||
|
<div className="profile-chart-block">
|
||||||
|
<h2 className="profile-section__title">
|
||||||
|
<i className="fa fa-bar-chart" /> Activity (6 months)
|
||||||
|
</h2>
|
||||||
|
<div className="profile-chart-inner">
|
||||||
|
<BarChart
|
||||||
|
xAxis={[{ scaleType: 'band', data: months, ...axisStyle }]}
|
||||||
|
yAxis={[{ ...axisStyle }]}
|
||||||
|
series={[
|
||||||
|
{ data: wins, label: 'Wins', color: WIN_COLOR, stack: 'total' },
|
||||||
|
{ data: losses, label: 'Losses', color: LOSS_COLOR, stack: 'total' },
|
||||||
|
{ data: draws, label: 'Draws', color: DRAW_COLOR, stack: 'total' },
|
||||||
|
]}
|
||||||
|
slotProps={{
|
||||||
|
legend: {
|
||||||
|
labelStyle: {
|
||||||
|
fill: 'rgba(255,255,255,0.55)',
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: '\'Rajdhani\', sans-serif',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
borderRadius={3}
|
||||||
|
width={460}
|
||||||
|
height={200}
|
||||||
|
margin={{ top: 10, bottom: 30, left: 30, right: 120 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
31
assets/js/contact.jsx
Normal file
31
assets/js/contact.jsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import ContactForm from './components/ContactForm';
|
||||||
|
|
||||||
|
const wrapper = document.getElementById('contact-form-wrapper');
|
||||||
|
|
||||||
|
if (wrapper) {
|
||||||
|
const siteKey = wrapper.dataset.siteKey;
|
||||||
|
const recaptchaFieldId = wrapper.dataset.recaptchaFieldId;
|
||||||
|
|
||||||
|
if (siteKey && recaptchaFieldId) {
|
||||||
|
createRoot(wrapper).render(
|
||||||
|
<ContactForm
|
||||||
|
siteKey={siteKey}
|
||||||
|
recaptchaFieldId={recaptchaFieldId}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.error('ContactForm: Missing siteKey or recaptchaFieldId in data attributes');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import MineSeeker from './mine-seeker/app';
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<MineSeeker env={document.getElementById('mine-wrapper').dataset.env}
|
|
||||||
gameId={document.getElementById('mine-wrapper').dataset.gameId}
|
|
||||||
ssl={document.getElementById('mine-wrapper').dataset.ssl}/>,
|
|
||||||
document.getElementById('mine-wrapper')
|
|
||||||
);
|
|
||||||
35
assets/js/mine-seeker/MineSeeker.jsx
Normal file
35
assets/js/mine-seeker/MineSeeker.jsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useRef } from 'react';
|
||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
import { GameProvider } from '@mine-contexts';
|
||||||
|
import { GameBoard } from '@mine-components';
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
const MineSeeker = ({ env, gameId }) => {
|
||||||
|
const isEnvDev = 'dev' === env;
|
||||||
|
const gameAssoc = useRef('' !== gameId ? gameId : crypto.randomUUID()).current;
|
||||||
|
const gameInherited = '' !== gameId;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<GameProvider>
|
||||||
|
<GameBoard
|
||||||
|
gameAssoc={gameAssoc}
|
||||||
|
gameInherited={gameInherited}
|
||||||
|
isEnvDev={isEnvDev}
|
||||||
|
/>
|
||||||
|
</GameProvider>
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MineSeeker;
|
||||||
@@ -1,456 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import Grid from './grid/grid';
|
|
||||||
import GridControl from './grid/grid-control';
|
|
||||||
|
|
||||||
class MineSeeker extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
let gameAssoc = props.gameId !== '' ? props.gameId : this.makeGameAssoc(50);
|
|
||||||
let channel = "mineseeker/channel/" + gameAssoc;
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
env: props.env,
|
|
||||||
ssl: props.ssl,
|
|
||||||
gameInherited: props.gameId !== '',
|
|
||||||
gameAssoc: gameAssoc,
|
|
||||||
channel: channel,
|
|
||||||
session: null,
|
|
||||||
createGrid: false,
|
|
||||||
stepCache: [],
|
|
||||||
connectionLost: false,
|
|
||||||
end: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
makeGameAssoc(len) {
|
|
||||||
let text = "";
|
|
||||||
let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
currectGridSize() {
|
|
||||||
let $field = $('#mine-wrapper .grid');
|
|
||||||
$field.height($field.width());
|
|
||||||
|
|
||||||
$field = $('#mine-wrapper .grid .field-wrapper');
|
|
||||||
$field.height($field.width());
|
|
||||||
|
|
||||||
$('#mine-wrapper .grid .field-wrapper .field')
|
|
||||||
.height($field.width())
|
|
||||||
.css('line-height', ($field.width() - 2) + 'px');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* STEP
|
|
||||||
*
|
|
||||||
* @param coords
|
|
||||||
* @returns {{red: *, blue: *}}
|
|
||||||
*/
|
|
||||||
makePointsCalcAndStep(coords) {
|
|
||||||
let users = this.refs.gridControl.refs.userControl,
|
|
||||||
activePlayer = users.state.activePlayer ? 'blue' : 'red',
|
|
||||||
inactivePlayer = users.state.activePlayer ? 'red' : 'blue',
|
|
||||||
redPoints = activePlayer === 'red'
|
|
||||||
? users.refs[activePlayer].state.mines
|
|
||||||
: users.refs[inactivePlayer].state.mines,
|
|
||||||
bluePoints = activePlayer === 'blue'
|
|
||||||
? users.refs[activePlayer].state.mines
|
|
||||||
: users.refs[inactivePlayer].state.mines;
|
|
||||||
|
|
||||||
this.refs.gridControl.stepEvent(coords);
|
|
||||||
|
|
||||||
let mineCache = this.refs.gridControl.state.foundUserMineCache;
|
|
||||||
redPoints += activePlayer === 'red' ? mineCache : 0;
|
|
||||||
bluePoints += activePlayer === 'blue' ? mineCache : 0;
|
|
||||||
|
|
||||||
return {red: redPoints, blue: bluePoints};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* START
|
|
||||||
*
|
|
||||||
* @param payload
|
|
||||||
*/
|
|
||||||
makeGameStart(payload) {
|
|
||||||
/** every time the blue starts */
|
|
||||||
this.refs.gridControl.refs.userControl.setState({activePlayer: 1});
|
|
||||||
|
|
||||||
/** Set up player names w/ server data */
|
|
||||||
this.refs.gridControl.refs.userControl.refs.red.setState({
|
|
||||||
name: payload.users.red !== '' ? payload.users.red : payload.users.redAnon,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.refs.gridControl.refs.userControl.refs.blue.setState({
|
|
||||||
name: payload.users.blue !== '' ? payload.users.blue : payload.users.blueAnon,
|
|
||||||
desc: this.refs.gridControl.state.webPlayer === 'blue'
|
|
||||||
? this.refs.gridControl.state.desc.you
|
|
||||||
: this.refs.gridControl.state.desc.buddy,
|
|
||||||
active: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.refs.gridControl.setState({overlay: false});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* THE END
|
|
||||||
*
|
|
||||||
* @param bluePoints
|
|
||||||
* @param redPoints
|
|
||||||
* @param resign
|
|
||||||
*/
|
|
||||||
makeGameEndIfItEnds(bluePoints, redPoints, resign = false) {
|
|
||||||
let redWins = redPoints > 25,
|
|
||||||
blueWins = bluePoints > 25;
|
|
||||||
|
|
||||||
if (redWins || blueWins || resign) {
|
|
||||||
this.refs.gridControl.state.sound.won.play();
|
|
||||||
|
|
||||||
if (false === resign) {
|
|
||||||
this.refs.gridControl.setState({
|
|
||||||
overlay: true,
|
|
||||||
overlayTitle: (redWins ? 'Red' : 'Blue') + " wins the game!",
|
|
||||||
overlaySubTitle: "Play again!"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.refs.gridControl.showLeftMines();
|
|
||||||
|
|
||||||
this.refs.gridControl.refs.userControl.setState({activePlayer: false});
|
|
||||||
this.refs.gridControl.refs.userControl.refs.red.setState({desc: ""});
|
|
||||||
this.refs.gridControl.refs.userControl.refs.blue.setState({desc: ""});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resignProcess(color) {
|
|
||||||
this.refs.gridControl.setState({
|
|
||||||
overlay: true,
|
|
||||||
overlayTitle: color === this.refs.gridControl.state.webPlayer
|
|
||||||
? "You have been give up"
|
|
||||||
: "Your opponent has been resigned",
|
|
||||||
overlaySubTitle: color === this.refs.gridControl.state.webPlayer
|
|
||||||
? "You LOSE!"
|
|
||||||
: "You WIN!"
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setState({end: true});
|
|
||||||
|
|
||||||
this.makeGameEndIfItEnds(0, 0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
clickResign() {
|
|
||||||
/** PUBLISH */
|
|
||||||
this.state.session.publish(this.state.channel, {
|
|
||||||
'resign': this.refs.gridControl.refs.userControl.state.activePlayer ? 'blue' : 'red'
|
|
||||||
});
|
|
||||||
this.resignProcess(this.refs.gridControl.state.webPlayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
clickResignCancel() {
|
|
||||||
this.refs.gridControl.setState({
|
|
||||||
overlay: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** RESIGN */
|
|
||||||
resign() {
|
|
||||||
let users = this.refs.gridControl.refs.userControl,
|
|
||||||
activePlayer = users.state.activePlayer ? 'blue' : 'red';
|
|
||||||
|
|
||||||
if (this.refs.gridControl.state.webPlayer === activePlayer) {
|
|
||||||
this.refs.gridControl.setState({
|
|
||||||
overlay: true,
|
|
||||||
overlayTitle: "Are u sure u want to resign?!",
|
|
||||||
overlaySubTitle: <div className="resign">
|
|
||||||
<a onClick={this.clickResign.bind(this)}>Yes</a>
|
|
||||||
<a onClick={this.clickResignCancel.bind(this)}>No!</a>
|
|
||||||
</div>
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus
|
|
||||||
* @see https://developers.facebook.com/docs/sharing/reference/send-dialog
|
|
||||||
* @see https://developers.facebook.com/docs/plugins/share-button/
|
|
||||||
*/
|
|
||||||
clickFBShare() {
|
|
||||||
let display = 'popup';
|
|
||||||
|
|
||||||
FB.getLoginStatus(function (response) {
|
|
||||||
display = response.status === 'connected'
|
|
||||||
? 'dialog'
|
|
||||||
: 'popup';
|
|
||||||
});
|
|
||||||
|
|
||||||
FB.ui({
|
|
||||||
method: 'send',
|
|
||||||
display: display,
|
|
||||||
link: window.location.href + '/' + this.state.gameAssoc,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
wInit(session, gridServer, gridClient) {
|
|
||||||
this.setState({session: session});
|
|
||||||
|
|
||||||
/** save session to GridControl */
|
|
||||||
/** render grid fields - @see #12 */
|
|
||||||
this.refs.gridControl.setState({
|
|
||||||
grid: this.state.gameInherited ? gridServer : gridClient,
|
|
||||||
channel: this.state.channel,
|
|
||||||
desc: {
|
|
||||||
buddy: <div>
|
|
||||||
Your buddy is <br/>
|
|
||||||
making a <br/>
|
|
||||||
move.
|
|
||||||
</div>,
|
|
||||||
you: <div>
|
|
||||||
It is your turn! <br/>
|
|
||||||
Make a move.
|
|
||||||
</div>
|
|
||||||
},
|
|
||||||
overlay: true,
|
|
||||||
overlayTitle: "We are waiting for your opponent...",
|
|
||||||
overlaySubTitle: this.state.gameAssoc
|
|
||||||
?
|
|
||||||
<div>
|
|
||||||
<h3>Share this unique link w/ your opponent</h3>
|
|
||||||
<div className="clippy">
|
|
||||||
<input id="foo"
|
|
||||||
defaultValue={window.location.href + '/' + this.state.gameAssoc}/>
|
|
||||||
</div>
|
|
||||||
{this.state.env !== 'dev' &&
|
|
||||||
<a onClick={this.clickFBShare.bind(this)}>Share in Facebook message</a>
|
|
||||||
}
|
|
||||||
{this.state.env === 'dev' &&
|
|
||||||
<a href={"/play/" + this.state.gameAssoc} target="_blank">Play w/ me!</a>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
: '',
|
|
||||||
renderGridFields: this.state.gameAssoc
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
wSubscribe(payload, rpcUsers = null) {
|
|
||||||
this.state.env === 'dev' && console.info(
|
|
||||||
(typeof payload.user !== 'undefined' ? payload.user : 'user') + " has been subscribed to the channel!"
|
|
||||||
);
|
|
||||||
|
|
||||||
let firstUser = !rpcUsers;
|
|
||||||
|
|
||||||
this.refs.gridControl.state.webPlayer === null && this.refs.gridControl.setState({
|
|
||||||
webPlayer: payload.user === payload.users.blue ||
|
|
||||||
(
|
|
||||||
firstUser && payload.users.blueAnon !== '' ||
|
|
||||||
!firstUser && (rpcUsers.blueAnon === '' && rpcUsers.blue === '')
|
|
||||||
)
|
|
||||||
? 'blue' : 'red'
|
|
||||||
});
|
|
||||||
|
|
||||||
/** rwd */
|
|
||||||
(900 > $(document).width()) && this.currectGridSize();
|
|
||||||
|
|
||||||
/** every user has been came */
|
|
||||||
if (
|
|
||||||
payload.userCnt === 2 &&
|
|
||||||
(
|
|
||||||
!this.state.connectionLost ||
|
|
||||||
this.state.connectionLost && false === this.refs.gridControl.refs.userControl.state.activePlayer && !this.state.end
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this.makeGameStart(payload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wUnsubscribe(payload) {
|
|
||||||
this.state.env === 'dev' && console.info(payload.msg);
|
|
||||||
|
|
||||||
this.refs.gridControl.setState({
|
|
||||||
overlay: true,
|
|
||||||
overlayTitle: "The connection has been lost w/ your friend...",
|
|
||||||
overlaySubTitle: "Please, restart the game!"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
wTopic(payload) {
|
|
||||||
/** Auto-Step if this player is not the current user */
|
|
||||||
if (this.refs.gridControl.state.webPlayer !== payload.data.player) {
|
|
||||||
if (null === payload.data.resign) {
|
|
||||||
this.state.env === 'dev' && console.warn(payload.user + " has been stepped to coords: " + payload.data.coords[0] + ', ' + payload.data.coords[1]);
|
|
||||||
this.state.env === 'dev' && console.warn('Opponent stepped: Auto-Step process');
|
|
||||||
|
|
||||||
this.refs.gridControl.refs.userControl.setState({bombSelected: payload.data.bomb});
|
|
||||||
|
|
||||||
/** STEP */
|
|
||||||
let points = this.makePointsCalcAndStep(payload.data.coords);
|
|
||||||
|
|
||||||
/** THE END */
|
|
||||||
this.makeGameEndIfItEnds(points.blue, points.red);
|
|
||||||
} else {
|
|
||||||
/** RESIGN */
|
|
||||||
/** THE END */
|
|
||||||
this.resignProcess(payload.data.resign);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Connect - Subscribe */
|
|
||||||
subscribe(rpcUsers = null) {
|
|
||||||
this.state.session.subscribe(
|
|
||||||
this.state.channel,
|
|
||||||
(uri, payload, log) => {
|
|
||||||
let isTopicEvent = typeof payload.data !== 'undefined',
|
|
||||||
isNotUnsubscribe = typeof payload.msg === 'undefined';
|
|
||||||
|
|
||||||
/** CONNECTION */
|
|
||||||
if (isTopicEvent) {
|
|
||||||
this.wTopic(payload);
|
|
||||||
} else {
|
|
||||||
if (isNotUnsubscribe) {
|
|
||||||
this.wSubscribe(payload, rpcUsers);
|
|
||||||
} else {
|
|
||||||
this.wUnsubscribe(payload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** RECONNECTION */
|
|
||||||
if (payload.userCnt === 2 && this.state.connectionLost) {
|
|
||||||
this.state.env === 'dev' && console.info('Reconnection process');
|
|
||||||
|
|
||||||
/** PUBLISH */
|
|
||||||
let cache = this.state.stepCache;
|
|
||||||
cache.forEach((item) => this.state.session.publish(this.state.channel, item));
|
|
||||||
this.setState({connectionLost: false, stepCache: []});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
connectWithWebsocket() {
|
|
||||||
/** Create Websocket w/ Bahnhof.js */
|
|
||||||
let websocket = WS.connect(
|
|
||||||
(this.state.ssl === 'true' ? "wss" : "ws") + "://" + window.location.hostname + "/ws/"
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect
|
|
||||||
* Session is an Autobahn JS WAMP session.
|
|
||||||
*/
|
|
||||||
websocket.on("socket/connect", (session) => {
|
|
||||||
this.state.env === 'dev' && console.info("Successfully connected to the Server!");
|
|
||||||
|
|
||||||
if (!this.state.connectionLost) {
|
|
||||||
let gridClient = this.state.gameInherited || new Grid().state.grid;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect - RPC
|
|
||||||
* Send grid information to the server
|
|
||||||
*/
|
|
||||||
session
|
|
||||||
.call(
|
|
||||||
this.state.gameInherited ? "mineseeker-rpc/connectGame" : "mineseeker-rpc/startGame",
|
|
||||||
this.state.gameInherited ? this.state.gameAssoc : [window.btoa(JSON.stringify(gridClient)), this.state.gameAssoc]
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
(data) => {
|
|
||||||
this.state.env === 'dev' && console.info('RPC has been called');
|
|
||||||
|
|
||||||
let serverData = data[0] !== true
|
|
||||||
? JSON.parse(window.atob(data))
|
|
||||||
: data;
|
|
||||||
|
|
||||||
/** Check the grid if the user is inherited @see #30 */
|
|
||||||
if ((this.state.gameInherited && typeof serverData.grid !== 'undefined') || !this.state.gameInherited) {
|
|
||||||
this.wInit(session, serverData.grid, gridClient);
|
|
||||||
this.subscribe(this.state.gameInherited && serverData.users);
|
|
||||||
} else {
|
|
||||||
this.refs.gridControl.setState({
|
|
||||||
overlay: true,
|
|
||||||
overlayTitle: "This channel does not exists!",
|
|
||||||
overlaySubTitle: <a href={"/play"} target="_self">Restart game!</a>
|
|
||||||
});
|
|
||||||
|
|
||||||
console.error("This channel does not exists!");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(error, desc) => this.state.env === 'dev' && console.error(["RPC Error", error, desc])
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.setState({session: session});
|
|
||||||
this.subscribe();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DisConnect
|
|
||||||
* Error provides us with some insight into the disconnection: error.reason and error.code
|
|
||||||
*/
|
|
||||||
websocket.on("socket/disconnect", (error) => {
|
|
||||||
this.state.env === 'dev' && console.error("Disconnected for " + error.reason + " with code " + error.code);
|
|
||||||
|
|
||||||
error.code === 6 && this.setState({connectionLost: true});
|
|
||||||
error.code === 3 && setTimeout(this.componentDidMount().bind(this), 500);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** After rendering */
|
|
||||||
componentDidMount() {
|
|
||||||
this.connectWithWebsocket();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cache the steps unless reconnection
|
|
||||||
*
|
|
||||||
* @param dataPack
|
|
||||||
*/
|
|
||||||
cachePublish(dataPack) {
|
|
||||||
let cache = this.state.stepCache;
|
|
||||||
cache.push(dataPack);
|
|
||||||
this.setState({stepCache: cache});
|
|
||||||
}
|
|
||||||
|
|
||||||
onClick(coords) {
|
|
||||||
let activePlayer = this.refs.gridControl.refs.userControl.state.activePlayer ? 'blue' : 'red';
|
|
||||||
|
|
||||||
/** if the clicked field is NEVER CLICKED */
|
|
||||||
if (this.refs.gridControl.checkFieldHasBeenNeverClicked(coords[0], coords[1])) {
|
|
||||||
/** Player step and it is the current player */
|
|
||||||
if (activePlayer === this.refs.gridControl.state.webPlayer) {
|
|
||||||
/** STEP */
|
|
||||||
let points = this.makePointsCalcAndStep(coords);
|
|
||||||
|
|
||||||
/** THE END */
|
|
||||||
this.makeGameEndIfItEnds(points.blue, points.red);
|
|
||||||
|
|
||||||
let dataPack = {
|
|
||||||
'coords': coords,
|
|
||||||
'player': activePlayer,
|
|
||||||
'bomb': this.refs.gridControl.refs.userControl.state.bombSelected,
|
|
||||||
'redPoints': points.red,
|
|
||||||
'bluePoints': points.blue,
|
|
||||||
'resign': null,
|
|
||||||
'redExplodedBomb': activePlayer === 'red' && this.refs.gridControl.refs.userControl.state.bombSelected,
|
|
||||||
'blueExplodedBomb': activePlayer === 'blue' && this.refs.gridControl.refs.userControl.state.bombSelected
|
|
||||||
};
|
|
||||||
|
|
||||||
/** PUBLISH */
|
|
||||||
!this.state.connectionLost
|
|
||||||
? this.state.session.publish(this.state.channel, dataPack)
|
|
||||||
: this.cachePublish(dataPack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<GridControl ref="gridControl"
|
|
||||||
env={this.props.env === 'dev'}
|
|
||||||
resign={this.resign.bind(this)}
|
|
||||||
onClick={this.onClick.bind(this)}/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MineSeeker;
|
|
||||||
25
assets/js/mine-seeker/components/BonusBox.jsx
Normal file
25
assets/js/mine-seeker/components/BonusBox.jsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const BonusBox = ({ color, points, onClick, title }) => (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`bonus-box ${color}-bonus`}
|
||||||
|
onClick={onClick}
|
||||||
|
title={title || 'View bonus statistics'}
|
||||||
|
aria-label={`${color} bonus points: ${points}`}
|
||||||
|
>
|
||||||
|
<i className="fa fa-star bonus-box__icon" />
|
||||||
|
<span className="bonus-box__value">{points}</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default BonusBox;
|
||||||
97
assets/js/mine-seeker/components/BonusStatsDialog.jsx
Normal file
97
assets/js/mine-seeker/components/BonusStatsDialog.jsx
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import { BONUS_LABELS } from '@mine-utils';
|
||||||
|
|
||||||
|
const DIALOG_SX = {
|
||||||
|
'& .MuiDialog-paper': {
|
||||||
|
background: '#07090d',
|
||||||
|
backgroundImage: `
|
||||||
|
linear-gradient(rgba(35, 111, 135, 0.08) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(35, 111, 135, 0.08) 1px, transparent 1px)
|
||||||
|
`,
|
||||||
|
backgroundSize: '46px 46px',
|
||||||
|
border: '1px solid rgba(35, 111, 135, 0.4)',
|
||||||
|
borderRadius: '12px',
|
||||||
|
boxShadow: '0 0 80px rgba(35, 111, 135, 0.15), 0 32px 80px rgba(0, 0, 0, 0.9)',
|
||||||
|
width: '560px',
|
||||||
|
maxWidth: '94vw',
|
||||||
|
overflow: 'hidden',
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
'& .MuiBackdrop-root': {
|
||||||
|
background: 'rgba(2, 4, 8, 0.88)',
|
||||||
|
backdropFilter: 'blur(4px)',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatPlayerName = name => {
|
||||||
|
if (name && name.startsWith('anon_')) {
|
||||||
|
return 'Anonymous';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name && 10 < name.length) {
|
||||||
|
return name.substring(0, 7) + '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
return name || 'Unknown';
|
||||||
|
};
|
||||||
|
|
||||||
|
const PlayerColumn = ({ color, player }) => (
|
||||||
|
<div className={`bsd-column bsd-column--${color}`}>
|
||||||
|
<div className="bsd-column-header">
|
||||||
|
<span className="bsd-column-name">{formatPlayerName(player.name)}</span>
|
||||||
|
<span className="bsd-column-total">
|
||||||
|
<i className="fa fa-star" />
|
||||||
|
{player.bonusPoints}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<ul className="bsd-stats">
|
||||||
|
{Object.entries(BONUS_LABELS).map(([key, { label, desc }]) => (
|
||||||
|
<li key={key} className="bsd-stat">
|
||||||
|
<div className="bsd-stat-text">
|
||||||
|
<span className="bsd-stat-label">{label}</span>
|
||||||
|
<span className="bsd-stat-desc">{desc}</span>
|
||||||
|
</div>
|
||||||
|
<span className="bsd-stat-value">{player.bonusStats?.[key] ?? 0}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const BonusStatsDialog = ({ open, onClose, red, blue }) => (
|
||||||
|
<Dialog open={open} onClose={onClose} sx={DIALOG_SX}>
|
||||||
|
<div className="bsd">
|
||||||
|
<div className="bsd-header">
|
||||||
|
<div className="bsd-header-text">
|
||||||
|
<span className="bsd-label">Scoring</span>
|
||||||
|
<h2 className="bsd-title">
|
||||||
|
<i className="fa fa-star" />
|
||||||
|
Bonus Statistics
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<button className="bsd-close" onClick={onClose} aria-label="Close">
|
||||||
|
<i className="fa fa-times" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="bsd-body">
|
||||||
|
<PlayerColumn color="red" player={red} />
|
||||||
|
<PlayerColumn color="blue" player={blue} />
|
||||||
|
</div>
|
||||||
|
<p className="bsd-note">
|
||||||
|
Bonus points are awarded alongside the main score for skillful play.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default BonusStatsDialog;
|
||||||
169
assets/js/mine-seeker/components/CaptchaOverlay.jsx
Normal file
169
assets/js/mine-seeker/components/CaptchaOverlay.jsx
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
const CAPTCHA_STORAGE_KEY = 'mineseeker_captcha_verified';
|
||||||
|
const CAPTCHA_TOKEN_KEY = 'mineseeker_captcha_token';
|
||||||
|
const RECAPTCHA_ACTION = 'mineseeker_play';
|
||||||
|
|
||||||
|
const CaptchaOverlay = ({ siteKey, onVerified, children }) => {
|
||||||
|
const [verified, setVerified] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const storedToken = sessionStorage.getItem(CAPTCHA_TOKEN_KEY);
|
||||||
|
const storedTime = sessionStorage.getItem(CAPTCHA_STORAGE_KEY);
|
||||||
|
|
||||||
|
if (storedToken && storedTime) {
|
||||||
|
const elapsed = (Date.now() - parseInt(storedTime)) / 1000;
|
||||||
|
if (110 > elapsed) {
|
||||||
|
const wrapper = document.getElementById('mine-wrapper');
|
||||||
|
if (wrapper) {
|
||||||
|
wrapper.dataset.captchaToken = storedToken;
|
||||||
|
}
|
||||||
|
setVerified(true);
|
||||||
|
onVerified?.();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.grecaptcha) {
|
||||||
|
window.grecaptcha.ready(() => {
|
||||||
|
window.grecaptcha
|
||||||
|
.execute(siteKey, { action: RECAPTCHA_ACTION })
|
||||||
|
.then(token => {
|
||||||
|
handleToken(token);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setError(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [siteKey, onVerified]);
|
||||||
|
|
||||||
|
const handleToken = token => {
|
||||||
|
const wrapper = document.getElementById('mine-wrapper');
|
||||||
|
if (wrapper) {
|
||||||
|
wrapper.dataset.captchaToken = token;
|
||||||
|
}
|
||||||
|
sessionStorage.setItem(CAPTCHA_TOKEN_KEY, token);
|
||||||
|
sessionStorage.setItem(CAPTCHA_STORAGE_KEY, Date.now().toString());
|
||||||
|
setVerified(true);
|
||||||
|
onVerified?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
setLoading(true);
|
||||||
|
setError(false);
|
||||||
|
|
||||||
|
window.grecaptcha.ready(() => {
|
||||||
|
window.grecaptcha
|
||||||
|
.execute(siteKey, { action: RECAPTCHA_ACTION })
|
||||||
|
.then(token => {
|
||||||
|
handleToken(token);
|
||||||
|
setLoading(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setLoading(false);
|
||||||
|
setError(true);
|
||||||
|
setTimeout(() => setError(false), 2000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (verified) {
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const overlayStyles = {
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
background: 'rgba(7, 9, 13, 0.95)',
|
||||||
|
backdropFilter: 'blur(8px)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
zIndex: 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
const contentStyles = {
|
||||||
|
textAlign: 'center',
|
||||||
|
color: '#fff',
|
||||||
|
maxWidth: '400px',
|
||||||
|
padding: '40px',
|
||||||
|
};
|
||||||
|
|
||||||
|
const iconStyles = {
|
||||||
|
fontSize: '64px',
|
||||||
|
color: '#236f87',
|
||||||
|
marginBottom: '24px',
|
||||||
|
};
|
||||||
|
|
||||||
|
const h1Styles = {
|
||||||
|
font: '800 32px Rajdhani, sans-serif',
|
||||||
|
margin: '0 0 16px',
|
||||||
|
letterSpacing: '1px',
|
||||||
|
};
|
||||||
|
|
||||||
|
const pStyles = {
|
||||||
|
color: 'rgba(149, 207, 245, 0.7)',
|
||||||
|
font: '400 16px Rajdhani, sans-serif',
|
||||||
|
margin: '0 0 32px',
|
||||||
|
letterSpacing: '0.5px',
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttonStyles = {
|
||||||
|
background: error
|
||||||
|
? 'linear-gradient(#8a2323 0%, #681a1a 100%)'
|
||||||
|
: loading
|
||||||
|
? 'linear-gradient(#236f87 0%, #1a5068 100%)'
|
||||||
|
: 'linear-gradient(#236f87 0%, #1a5068 100%)',
|
||||||
|
border: `2px solid ${error ? '#9a2e2e' : loading ? '#2e7a9a' : '#2e7a9a'}`,
|
||||||
|
borderRadius: '8px',
|
||||||
|
color: '#e0f4ff',
|
||||||
|
cursor: loading ? 'wait' : 'pointer',
|
||||||
|
font: '800 18px Rajdhani, sans-serif',
|
||||||
|
letterSpacing: '2px',
|
||||||
|
padding: '16px 40px',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
transition: 'all 0.3s ease',
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '12px',
|
||||||
|
opacity: loading ? 0.7 : 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={overlayStyles}>
|
||||||
|
<div style={contentStyles}>
|
||||||
|
<div style={iconStyles}>
|
||||||
|
<i className="fa fa-shield-halved" />
|
||||||
|
</div>
|
||||||
|
<h1 style={h1Styles}>Ready to Play?</h1>
|
||||||
|
<p style={pStyles}>
|
||||||
|
Click below to verify you're human and start playing.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
style={buttonStyles}
|
||||||
|
onClick={handleClick}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
<i className={`fa ${loading ? 'fa-spinner fa-spin' : error ? 'fa-exclamation-circle' : 'fa-play'}`} />
|
||||||
|
{loading ? 'Verifying...' : error ? 'Try Again' : 'Start Playing'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CaptchaOverlay;
|
||||||
41
assets/js/mine-seeker/components/ChallengeCountdown.jsx
Normal file
41
assets/js/mine-seeker/components/ChallengeCountdown.jsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
import { Fragment, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
const ChallengeCountdown = ({ onAccept, onDecline, seconds = 30 }) => {
|
||||||
|
const [countdown, setCountdown] = useState(seconds);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setCountdown(prev => {
|
||||||
|
if (1 >= prev) {
|
||||||
|
clearInterval(interval);
|
||||||
|
onDecline();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return prev - 1;
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [onDecline]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<p style={{ textAlign: 'center', marginBottom: 20, color: '#95cff5' }}>
|
||||||
|
You have {countdown} second{1 === countdown ? '' : 's'} to answer to the challenge!
|
||||||
|
</p>
|
||||||
|
<div className="resign">
|
||||||
|
<a onClick={onAccept}>Accept</a>
|
||||||
|
<a onClick={onDecline}>Decline</a>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChallengeCountdown;
|
||||||
34
assets/js/mine-seeker/components/GameBoard.jsx
Normal file
34
assets/js/mine-seeker/components/GameBoard.jsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { useGame } from '@mine-contexts';
|
||||||
|
import { useServerCommunication } from '@mine-hooks';
|
||||||
|
import GridControl from './grid/GridControl';
|
||||||
|
|
||||||
|
export const GameBoard = ({ gameAssoc, gameInherited, isEnvDev }) => {
|
||||||
|
const { gridReady } = useGame();
|
||||||
|
const { onClick, resign } = useServerCommunication(gameAssoc, gameInherited, isEnvDev);
|
||||||
|
|
||||||
|
if (!gridReady) {
|
||||||
|
return (
|
||||||
|
<div className="game-overlay">
|
||||||
|
<div className="game-overlay-window"><h1>Loading…</h1></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GridControl
|
||||||
|
gameAssoc={gameAssoc}
|
||||||
|
onClick={onClick}
|
||||||
|
resign={resign}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
188
assets/js/mine-seeker/components/GameTimer.jsx
Normal file
188
assets/js/mine-seeker/components/GameTimer.jsx
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useGame } from '@mine-contexts';
|
||||||
|
import BonusBox from './BonusBox';
|
||||||
|
import BonusStatsDialog from './BonusStatsDialog';
|
||||||
|
|
||||||
|
const renderAvatar = player => {
|
||||||
|
if (!player.registered) return null;
|
||||||
|
return (
|
||||||
|
<div className="timer-avatar">
|
||||||
|
{player.avatar
|
||||||
|
? <img src={player.avatar} alt={player.name} className="timer-avatar__img" />
|
||||||
|
: <span className="timer-avatar__initials">{player.name.slice(0, 2).toUpperCase()}</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const GameTimer = () => {
|
||||||
|
const { overlay, connectionLost, endRef, activePlayer, red, blue } = useGame();
|
||||||
|
const [redTime, setRedTime] = useState(0);
|
||||||
|
const [blueTime, setBlueTime] = useState(0);
|
||||||
|
const [isRunning, setIsRunning] = useState(false);
|
||||||
|
const [bonusDialogOpen, setBonusDialogOpen] = useState(false);
|
||||||
|
const timerIntervalRef = useRef(null);
|
||||||
|
const gameStartedRef = useRef(false);
|
||||||
|
|
||||||
|
// Use timestamps instead of counters for more reliable background tracking
|
||||||
|
const redStartTimeRef = useRef(null);
|
||||||
|
const blueStartTimeRef = useRef(null);
|
||||||
|
const lastActivePlayerRef = useRef(null);
|
||||||
|
const pausedRedTimeRef = useRef(0);
|
||||||
|
const pausedBlueTimeRef = useRef(0);
|
||||||
|
|
||||||
|
// Start timer when overlay is hidden (both players connected and game started)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!overlay && !gameStartedRef.current) {
|
||||||
|
gameStartedRef.current = true;
|
||||||
|
setIsRunning(true);
|
||||||
|
setRedTime(0);
|
||||||
|
setBlueTime(0);
|
||||||
|
redStartTimeRef.current = Date.now();
|
||||||
|
blueStartTimeRef.current = Date.now();
|
||||||
|
pausedRedTimeRef.current = 0;
|
||||||
|
pausedBlueTimeRef.current = 0;
|
||||||
|
lastActivePlayerRef.current = activePlayer;
|
||||||
|
}
|
||||||
|
}, [overlay]);
|
||||||
|
|
||||||
|
// Stop timer on game end (resign/win)
|
||||||
|
useEffect(() => {
|
||||||
|
if (endRef.current) {
|
||||||
|
setIsRunning(false);
|
||||||
|
}
|
||||||
|
}, [endRef.current]);
|
||||||
|
|
||||||
|
// Stop timer on connection loss
|
||||||
|
useEffect(() => {
|
||||||
|
if (connectionLost) {
|
||||||
|
setIsRunning(false);
|
||||||
|
}
|
||||||
|
}, [connectionLost]);
|
||||||
|
|
||||||
|
// Handle player switch - pause one timer, resume the other
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isRunning) return;
|
||||||
|
|
||||||
|
if (lastActivePlayerRef.current !== activePlayer) {
|
||||||
|
// Player switched, save current accumulated time for whoever was active
|
||||||
|
const startRef = lastActivePlayerRef.current ? blueStartTimeRef.current : redStartTimeRef.current;
|
||||||
|
if (startRef) {
|
||||||
|
const elapsed = Math.floor((Date.now() - startRef) / 1000);
|
||||||
|
if (lastActivePlayerRef.current) {
|
||||||
|
pausedBlueTimeRef.current += elapsed;
|
||||||
|
} else {
|
||||||
|
pausedRedTimeRef.current += elapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the new active player's timer
|
||||||
|
if (activePlayer) {
|
||||||
|
blueStartTimeRef.current = Date.now();
|
||||||
|
} else {
|
||||||
|
redStartTimeRef.current = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastActivePlayerRef.current = activePlayer;
|
||||||
|
}
|
||||||
|
}, [activePlayer, isRunning]);
|
||||||
|
|
||||||
|
// Main timer effect - update display every 100ms
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isRunning) {
|
||||||
|
if (timerIntervalRef.current) {
|
||||||
|
clearInterval(timerIntervalRef.current);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timerIntervalRef.current = setInterval(() => {
|
||||||
|
let currentRedTime = pausedRedTimeRef.current;
|
||||||
|
let currentBlueTime = pausedBlueTimeRef.current;
|
||||||
|
|
||||||
|
// Add elapsed time for the active player
|
||||||
|
if (!activePlayer && redStartTimeRef.current) {
|
||||||
|
currentRedTime += Math.floor((Date.now() - redStartTimeRef.current) / 1000);
|
||||||
|
} else if (activePlayer && blueStartTimeRef.current) {
|
||||||
|
currentBlueTime += Math.floor((Date.now() - blueStartTimeRef.current) / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
setRedTime(currentRedTime);
|
||||||
|
setBlueTime(currentBlueTime);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (timerIntervalRef.current) {
|
||||||
|
clearInterval(timerIntervalRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [isRunning, activePlayer]);
|
||||||
|
|
||||||
|
// Handle focus/blur to synchronize timer when tab regains focus
|
||||||
|
useEffect(() => {
|
||||||
|
const handleFocus = () => {
|
||||||
|
// Force update when tab regains focus to sync any background drift
|
||||||
|
if (isRunning) {
|
||||||
|
let currentRedTime = pausedRedTimeRef.current;
|
||||||
|
let currentBlueTime = pausedBlueTimeRef.current;
|
||||||
|
|
||||||
|
if (!activePlayer && redStartTimeRef.current) {
|
||||||
|
currentRedTime += Math.floor((Date.now() - redStartTimeRef.current) / 1000);
|
||||||
|
} else if (activePlayer && blueStartTimeRef.current) {
|
||||||
|
currentBlueTime += Math.floor((Date.now() - blueStartTimeRef.current) / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
setRedTime(currentRedTime);
|
||||||
|
setBlueTime(currentBlueTime);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('focus', handleFocus);
|
||||||
|
return () => window.removeEventListener('focus', handleFocus);
|
||||||
|
}, [isRunning, activePlayer]);
|
||||||
|
|
||||||
|
// Cleanup on unmount
|
||||||
|
useEffect(() => () => {
|
||||||
|
if (timerIntervalRef.current) {
|
||||||
|
clearInterval(timerIntervalRef.current);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const formatTime = seconds => {
|
||||||
|
const mins = Math.floor(seconds / 60);
|
||||||
|
const secs = seconds % 60;
|
||||||
|
return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const openBonusDialog = () => setBonusDialogOpen(true);
|
||||||
|
const closeBonusDialog = () => setBonusDialogOpen(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="game-timer-container">
|
||||||
|
<BonusBox color="red" points={red.bonusPoints ?? 0} onClick={openBonusDialog} />
|
||||||
|
<div className={`game-timer red-timer ${!activePlayer ? 'active' : ''}`}>
|
||||||
|
{renderAvatar(red)}
|
||||||
|
<i className={`fa ${!activePlayer ? 'fa-hourglass-half' : 'fa-hourglass-start'} timer-icon`} />
|
||||||
|
<span className="timer-display">{formatTime(redTime)}</span>
|
||||||
|
</div>
|
||||||
|
<div className={`game-timer blue-timer ${activePlayer ? 'active' : ''}`}>
|
||||||
|
{renderAvatar(blue)}
|
||||||
|
<i className={`fa ${activePlayer ? 'fa-hourglass-half' : 'fa-hourglass-start'} timer-icon`} />
|
||||||
|
<span className="timer-display">{formatTime(blueTime)}</span>
|
||||||
|
</div>
|
||||||
|
<BonusBox color="blue" points={blue.bonusPoints ?? 0} onClick={openBonusDialog} />
|
||||||
|
<BonusStatsDialog open={bonusDialogOpen} onClose={closeBonusDialog} red={red} blue={blue} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GameTimer;
|
||||||
286
assets/js/mine-seeker/components/OnlinePlayersDialog.jsx
Normal file
286
assets/js/mine-seeker/components/OnlinePlayersDialog.jsx
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
|
||||||
|
const DIALOG_SX = {
|
||||||
|
'& .MuiDialog-paper': {
|
||||||
|
background: '#07090d',
|
||||||
|
backgroundImage: `
|
||||||
|
linear-gradient(rgba(35, 111, 135, 0.08) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(35, 111, 135, 0.08) 1px, transparent 1px)
|
||||||
|
`,
|
||||||
|
backgroundSize: '46px 46px',
|
||||||
|
border: '1px solid rgba(35, 111, 135, 0.4)',
|
||||||
|
borderRadius: '12px',
|
||||||
|
boxShadow: '0 0 80px rgba(35, 111, 135, 0.15), 0 32px 80px rgba(0, 0, 0, 0.9)',
|
||||||
|
width: '500px',
|
||||||
|
maxWidth: '94vw',
|
||||||
|
overflow: 'hidden',
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
'& .MuiBackdrop-root': {
|
||||||
|
background: 'rgba(2, 4, 8, 0.88)',
|
||||||
|
backdropFilter: 'blur(4px)',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatSince = isoStr => {
|
||||||
|
const diff = Math.floor((Date.now() - new Date(isoStr)) / 60000);
|
||||||
|
if (1 > diff) return 'just now';
|
||||||
|
if (1 === diff) return '1 min ago';
|
||||||
|
return `${diff} min ago`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc }) => {
|
||||||
|
const [players, setPlayers] = useState([]);
|
||||||
|
const [search, setSearch] = useState('');
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [refreshKey, setRefreshKey] = useState(0);
|
||||||
|
const [snapshotLoaded, setSnapshotLoaded] = useState(false);
|
||||||
|
const [challengingGameAssoc, setChallengingGameAssoc] = useState(null);
|
||||||
|
const [declinedMsg, setDeclinedMsg] = useState('');
|
||||||
|
const [waitingCountdown, setWaitingCountdown] = useState(0);
|
||||||
|
const declinedTimerRef = useRef(null);
|
||||||
|
|
||||||
|
const addPlayer = useCallback(entry => {
|
||||||
|
setPlayers(prev =>
|
||||||
|
prev.some(p => p.gameAssoc === entry.gameAssoc)
|
||||||
|
? prev
|
||||||
|
: [...prev, entry],
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const removePlayer = useCallback(gameAssoc => {
|
||||||
|
setPlayers(prev => prev.filter(p => p.gameAssoc !== gameAssoc));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) return;
|
||||||
|
setLoading(true);
|
||||||
|
setSnapshotLoaded(false);
|
||||||
|
fetch('/api/game/waiting')
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
// Filter out current user's game from the snapshot
|
||||||
|
const filtered = data.filter(p => p.gameAssoc !== currentGameAssoc);
|
||||||
|
setPlayers(filtered);
|
||||||
|
setSnapshotLoaded(true);
|
||||||
|
setLoading(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setPlayers([]);
|
||||||
|
setSnapshotLoaded(true);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}, [open, refreshKey, currentGameAssoc]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open || !snapshotLoaded) return;
|
||||||
|
setSearch('');
|
||||||
|
|
||||||
|
const wrapper = document.getElementById('mine-wrapper');
|
||||||
|
const hubUrl = wrapper?.dataset?.mercureHubUrl;
|
||||||
|
const jwt = wrapper?.dataset?.mercureSubscriberJwt;
|
||||||
|
if (!hubUrl) return;
|
||||||
|
|
||||||
|
const url = new URL(hubUrl, window.location.origin);
|
||||||
|
url.searchParams.append('topic', 'mineseeker/lobby');
|
||||||
|
if (jwt) url.searchParams.append('authorization', jwt);
|
||||||
|
|
||||||
|
const es = new EventSource(url.toString());
|
||||||
|
es.onmessage = e => {
|
||||||
|
const { action, gameAssoc, name, since } = JSON.parse(e.data);
|
||||||
|
// Don't add the current user's game to the list
|
||||||
|
if (gameAssoc === currentGameAssoc) return;
|
||||||
|
if ('join' === action) addPlayer({ gameAssoc, name, since });
|
||||||
|
if ('leave' === action) removePlayer(gameAssoc);
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => es.close();
|
||||||
|
}, [open, snapshotLoaded, addPlayer, removePlayer, currentGameAssoc]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = () => {
|
||||||
|
setChallengingGameAssoc(null);
|
||||||
|
clearTimeout(declinedTimerRef.current);
|
||||||
|
setDeclinedMsg('Challenge was not accepted.');
|
||||||
|
setWaitingCountdown(0);
|
||||||
|
declinedTimerRef.current = setTimeout(() => setDeclinedMsg(''), 3500);
|
||||||
|
};
|
||||||
|
window.addEventListener('challenge-declined', handler);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('challenge-declined', handler);
|
||||||
|
clearTimeout(declinedTimerRef.current);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!waitingCountdown) return;
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setWaitingCountdown(prev => {
|
||||||
|
if (1 >= prev) return 0;
|
||||||
|
return prev - 1;
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [waitingCountdown]);
|
||||||
|
|
||||||
|
const handleChallenge = player => {
|
||||||
|
if (challengingGameAssoc) return;
|
||||||
|
setChallengingGameAssoc(player.gameAssoc);
|
||||||
|
setDeclinedMsg('');
|
||||||
|
setWaitingCountdown(30);
|
||||||
|
fetch('/api/game/challenge/' + player.gameAssoc, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ challengerGameAssoc: currentGameAssoc }),
|
||||||
|
}).catch(() => {
|
||||||
|
setChallengingGameAssoc(null);
|
||||||
|
setWaitingCountdown(0);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const visible = players
|
||||||
|
.filter(p => p.gameAssoc !== currentGameAssoc)
|
||||||
|
.filter(p => p.name.toLowerCase().includes(search.toLowerCase()));
|
||||||
|
|
||||||
|
const shown = visible.slice(0, 5);
|
||||||
|
const hasMore = 5 < visible.length;
|
||||||
|
|
||||||
|
// Debug: log if currentGameAssoc is undefined or if current user appears
|
||||||
|
if ('development' === process.env.NODE_ENV && 0 < players.length) {
|
||||||
|
const userInList = players.find(p => p.gameAssoc === currentGameAssoc);
|
||||||
|
if (userInList) {
|
||||||
|
console.warn('[OnlinePlayersDialog] Current user appears in players list:', { currentGameAssoc, userInList });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={0 < waitingCountdown ? undefined : onClose}
|
||||||
|
disableEscapeKeyDown={0 < waitingCountdown}
|
||||||
|
sx={DIALOG_SX}
|
||||||
|
>
|
||||||
|
<div className="opd">
|
||||||
|
<div className="opd-header">
|
||||||
|
<div className="opd-header-text">
|
||||||
|
<span className="opd-label">Multiplayer</span>
|
||||||
|
<h2 className="opd-title">
|
||||||
|
<i className="fa fa-users" />
|
||||||
|
Online Players
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div className="opd-header-actions">
|
||||||
|
<button
|
||||||
|
className={`opd-refresh${loading ? ' opd-refresh--spin' : ''}`}
|
||||||
|
onClick={() => { if (0 === waitingCountdown) setRefreshKey(k => k + 1); }}
|
||||||
|
disabled={loading || 0 < waitingCountdown}
|
||||||
|
aria-label="Refresh"
|
||||||
|
title="Refresh list"
|
||||||
|
>
|
||||||
|
<i className="fa fa-refresh" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="opd-close"
|
||||||
|
onClick={() => { if (0 === waitingCountdown) onClose(); }}
|
||||||
|
disabled={0 < waitingCountdown}
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
<i className="fa fa-times" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{0 < waitingCountdown ? (
|
||||||
|
<div className="opd-waiting">
|
||||||
|
<i className="fa fa-hourglass-start" />
|
||||||
|
<p>Waiting {waitingCountdown} second{1 === waitingCountdown ? '' : 's'} for opponent's answer...</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="opd-search-wrap">
|
||||||
|
<i className="fa fa-search opd-search-icon" />
|
||||||
|
<input
|
||||||
|
className="opd-search"
|
||||||
|
placeholder="Search by username…"
|
||||||
|
value={search}
|
||||||
|
onChange={e => setSearch(e.target.value)}
|
||||||
|
/>
|
||||||
|
{search && (
|
||||||
|
<button className="opd-search-clear" onClick={() => setSearch('')}>
|
||||||
|
<i className="fa fa-times" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="opd-list">
|
||||||
|
{loading && (
|
||||||
|
<div className="opd-empty">
|
||||||
|
<i className="fa fa-spinner fa-spin" />
|
||||||
|
<p>Loading players…</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!loading && 0 === shown.length && (
|
||||||
|
<div className="opd-empty">
|
||||||
|
<i className="fa fa-hourglass-half" />
|
||||||
|
<p>
|
||||||
|
{search
|
||||||
|
? 'No players match your search.'
|
||||||
|
: 'No other players are waiting right now.'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{declinedMsg && (
|
||||||
|
<div className="opd-declined">
|
||||||
|
<i className="fa fa-times-circle" />
|
||||||
|
{' '}{declinedMsg}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!loading && shown.map(player => {
|
||||||
|
const isWaiting = challengingGameAssoc === player.gameAssoc;
|
||||||
|
return (
|
||||||
|
<div key={player.gameAssoc} className="opd-row">
|
||||||
|
<div className="opd-avatar">
|
||||||
|
{player.name.slice(0, 2).toUpperCase()}
|
||||||
|
</div>
|
||||||
|
<div className="opd-info">
|
||||||
|
<span className="opd-name">{player.name}</span>
|
||||||
|
<span className="opd-since">
|
||||||
|
<i className="fa fa-clock-o" />
|
||||||
|
{' '}Waiting {formatSince(player.since)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className={`opd-join${isWaiting ? ' opd-join--waiting' : ''}`}
|
||||||
|
onClick={() => handleChallenge(player)}
|
||||||
|
disabled={!!challengingGameAssoc}
|
||||||
|
>
|
||||||
|
<i className={`fa ${isWaiting ? 'fa-spinner fa-spin' : 'fa-play'}`} />
|
||||||
|
{isWaiting ? 'Waiting...' : 'Join'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!loading && hasMore && (
|
||||||
|
<p className="opd-note">
|
||||||
|
Showing 5 of {visible.length} waiting players
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OnlinePlayersDialog;
|
||||||
85
assets/js/mine-seeker/components/WaitingOverlayContent.jsx
Normal file
85
assets/js/mine-seeker/components/WaitingOverlayContent.jsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
import { Fragment, useState } from 'react';
|
||||||
|
import { OnlinePlayersDialog } from '@mine-components';
|
||||||
|
|
||||||
|
const WaitingOverlayContent = ({ shareUrl, currentGameAssoc }) => {
|
||||||
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<div className="waiting-options">
|
||||||
|
<div className="waiting-option">
|
||||||
|
<div className="waiting-option-header">
|
||||||
|
<i className="fa fa-link" />
|
||||||
|
<span>Invite a Friend</span>
|
||||||
|
</div>
|
||||||
|
<p className="waiting-option-desc">Share this link with your opponent</p>
|
||||||
|
<ShareLinkBox
|
||||||
|
url={shareUrl}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="waiting-divider">
|
||||||
|
<span>OR</span>
|
||||||
|
</div>
|
||||||
|
<div className="waiting-option">
|
||||||
|
<div className="waiting-option-header">
|
||||||
|
<i className="fa fa-users" />
|
||||||
|
<span>Challenge a Player</span>
|
||||||
|
</div>
|
||||||
|
<p className="waiting-option-desc">Browse online players and challenge them</p>
|
||||||
|
<button
|
||||||
|
className="browse-players-btn"
|
||||||
|
onClick={() => setDialogOpen(true)}
|
||||||
|
>
|
||||||
|
<i className="fa fa-search" />
|
||||||
|
Browse Players
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<OnlinePlayersDialog
|
||||||
|
open={dialogOpen}
|
||||||
|
onClose={() => setDialogOpen(false)}
|
||||||
|
currentGameAssoc={currentGameAssoc}
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ShareLinkBox = ({ url }) => {
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
|
const handleCopy = () => {
|
||||||
|
navigator.clipboard.writeText(url).then(() => {
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => setCopied(false), 2500);
|
||||||
|
}).catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="share-invite">
|
||||||
|
<div className="share-url-box" onClick={e => e.currentTarget.querySelector('input').select()}>
|
||||||
|
<i className="fa fa-link share-url-icon" />
|
||||||
|
<input
|
||||||
|
className="share-url-input"
|
||||||
|
readOnly
|
||||||
|
value={url}
|
||||||
|
onClick={e => e.target.select()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button className={`share-copy-btn${copied ? ' copied' : ''}`} onClick={handleCopy}>
|
||||||
|
<i className={`fa ${copied ? 'fa-check' : 'fa-clipboard'}`} />
|
||||||
|
{copied ? 'Copied!' : 'Copy Link'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WaitingOverlayContent;
|
||||||
101
assets/js/mine-seeker/components/grid/GridControl.jsx
Normal file
101
assets/js/mine-seeker/components/grid/GridControl.jsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { Fragment, useState } from 'react';
|
||||||
|
import { useGame } from '@mine-contexts';
|
||||||
|
import GridField from './GridField';
|
||||||
|
import UserControl from '../user/UserControl';
|
||||||
|
import GameTimer from '../GameTimer';
|
||||||
|
import { BOMB_SYMBOLS, bombRadius } from '@mine-utils';
|
||||||
|
|
||||||
|
const GridControl = ({ gameAssoc, onClick, resign }) => {
|
||||||
|
const {
|
||||||
|
overlay, overlayTitle, overlaySubTitle,
|
||||||
|
webPlayer, activePlayer, bombSelected,
|
||||||
|
cells, setCells, endRef,
|
||||||
|
} = useGame();
|
||||||
|
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
const shareUrl = gameAssoc ? `${window.location.origin}/battle/${gameAssoc}` : null;
|
||||||
|
|
||||||
|
const handleShare = () => {
|
||||||
|
if (!shareUrl) return;
|
||||||
|
navigator.clipboard.writeText(shareUrl).then(() => {
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => setCopied(false), 2200);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleHover = (row, col) => {
|
||||||
|
if (!bombSelected) return;
|
||||||
|
const activeColor = activePlayer ? 'blue' : 'red';
|
||||||
|
if (activeColor !== webPlayer) return;
|
||||||
|
|
||||||
|
setCells(prev => {
|
||||||
|
const next = prev.map(r => r.map(c =>
|
||||||
|
null !== c.bombTargetArea ? { ...c, bombTargetArea: null } : c,
|
||||||
|
));
|
||||||
|
bombRadius(row, col, prev.length, prev[0]?.length ?? 0).forEach(([r, c], i) => {
|
||||||
|
if (!next[r]?.[c]) return;
|
||||||
|
|
||||||
|
next[r] = [...next[r]];
|
||||||
|
next[r][c] = { ...next[r][c], bombTargetArea: BOMB_SYMBOLS[i] };
|
||||||
|
});
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<GameTimer />
|
||||||
|
<div className="game-wrapper">
|
||||||
|
<div className={`game-overlay${overlay ? '' : ' hide'}`}>
|
||||||
|
<div className="game-overlay-window">
|
||||||
|
<h1>{overlayTitle}</h1>
|
||||||
|
{'string' === typeof overlaySubTitle ? (
|
||||||
|
<h2>{overlaySubTitle}</h2>
|
||||||
|
) : (
|
||||||
|
overlaySubTitle
|
||||||
|
)}
|
||||||
|
{gameAssoc && endRef.current && (
|
||||||
|
<button
|
||||||
|
className={`game-overlay-share${copied ? ' copied' : ''}`}
|
||||||
|
onClick={handleShare}
|
||||||
|
title="Copy share link"
|
||||||
|
aria-label="Copy share link"
|
||||||
|
>
|
||||||
|
<i className={`fa ${copied ? 'fa-check' : 'fa-share-alt'}`} />
|
||||||
|
{copied ? 'Copied!' : 'Share Battle'}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<UserControl
|
||||||
|
resign={resign}
|
||||||
|
/>
|
||||||
|
<div className="grid-container">
|
||||||
|
<div className="grid">
|
||||||
|
{cells.flatMap((row, r) =>
|
||||||
|
row.map((cell, c) => (
|
||||||
|
<GridField
|
||||||
|
key={`${r}_${c}`}
|
||||||
|
cell={cell}
|
||||||
|
onClick={() => onClick([r, c])}
|
||||||
|
onMouseEnter={() => handleHover(r, c)}
|
||||||
|
/>
|
||||||
|
)),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GridControl;
|
||||||
77
assets/js/mine-seeker/components/grid/GridField.jsx
Normal file
77
assets/js/mine-seeker/components/grid/GridField.jsx
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo, useMemo } from 'react';
|
||||||
|
import { IMAGES } from '@mine-utils';
|
||||||
|
|
||||||
|
const bombSrc = area => {
|
||||||
|
if (null === area) return null;
|
||||||
|
const vert = ['left', 'center', 'right'][area[0]] ?? null;
|
||||||
|
const hor = ['top', 'middle', 'bottom'][area[1]] ?? null;
|
||||||
|
if (null === vert || null === hor) return IMAGES.bombEmpty;
|
||||||
|
return IMAGES.bombPos(hor, vert);
|
||||||
|
};
|
||||||
|
|
||||||
|
const GridField = memo(function GridField({ cell, onClick, onMouseEnter }) {
|
||||||
|
const { currentImage, currentObj, active, lastClickedRed, lastClickedBlue, bombTargetArea } = cell;
|
||||||
|
|
||||||
|
const fieldClass = 'field'
|
||||||
|
+ (active ? ' active' : '')
|
||||||
|
+ (active && 'm' === currentObj ? ' mine' : '')
|
||||||
|
+ ' color-' + currentObj;
|
||||||
|
const bombSourceString = useMemo(() => bombSrc(bombTargetArea), [bombTargetArea]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="field-wrapper"
|
||||||
|
onClick={onClick}
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="field-target"
|
||||||
|
src={IMAGES.target}
|
||||||
|
alt="Field of target"
|
||||||
|
/>
|
||||||
|
{bombSourceString && (
|
||||||
|
<img
|
||||||
|
className="field-bomb-target"
|
||||||
|
src={bombSourceString}
|
||||||
|
alt="Field of bomb target"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{(lastClickedRed || lastClickedBlue) && (
|
||||||
|
<img
|
||||||
|
className={`field-${lastClickedRed ? 'red' : 'blue'}-last last-clicked`}
|
||||||
|
src={IMAGES.last(lastClickedRed ? 'red' : 'blue')}
|
||||||
|
alt="Last clicked area"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className={fieldClass}>
|
||||||
|
<div
|
||||||
|
style={{ background: "url('/images/bg-corner-outbg.png') no-repeat top left / 100% 100%" }}
|
||||||
|
className="field-corner"
|
||||||
|
>
|
||||||
|
{isNaN(currentImage) && (
|
||||||
|
<div className="flag-mine">
|
||||||
|
<img src={currentImage} alt="" />
|
||||||
|
<div className="flag-mine-base" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!isNaN(currentImage) && 0 !== currentImage && (
|
||||||
|
<div className="flag-number">
|
||||||
|
{currentImage}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default GridField;
|
||||||
18
assets/js/mine-seeker/components/index.js
Normal file
18
assets/js/mine-seeker/components/index.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { GameBoard } from './GameBoard';
|
||||||
|
export { default as OnlinePlayersDialog } from './OnlinePlayersDialog';
|
||||||
|
export { default as WaitingOverlayContent } from './WaitingOverlayContent';
|
||||||
|
export { default as ChallengeCountdown } from './ChallengeCountdown';
|
||||||
|
export { default as GameTimer } from './GameTimer';
|
||||||
|
export { default as GridControl } from './grid/GridControl';
|
||||||
|
export { default as GridField } from './grid/GridField';
|
||||||
|
export { default as User } from './user/User';
|
||||||
|
export { default as UserControl } from './user/UserControl';
|
||||||
54
assets/js/mine-seeker/components/user/User.jsx
Normal file
54
assets/js/mine-seeker/components/user/User.jsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import { IMAGES } from '@mine-utils';
|
||||||
|
|
||||||
|
const User = memo(function User(
|
||||||
|
{
|
||||||
|
color,
|
||||||
|
webPlayer,
|
||||||
|
name,
|
||||||
|
desc,
|
||||||
|
active,
|
||||||
|
mines,
|
||||||
|
haveBomb,
|
||||||
|
enabledBomb,
|
||||||
|
onClickBombSelector,
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const buzzClass = 'bomb-container'
|
||||||
|
+ (active && color === webPlayer && haveBomb && enabledBomb ? ' buzz' : '');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`user-container user-${color}`}>
|
||||||
|
<div className="user-header">
|
||||||
|
<div className="user-color">{color}</div>
|
||||||
|
{active && <img src={IMAGES.cursor(color)} alt="" className="user-cursor" />}
|
||||||
|
<img src={IMAGES.figure(color)} alt="" />
|
||||||
|
</div>
|
||||||
|
<div className="user-name"> {name} </div>
|
||||||
|
<div className="user-caret"><i className="fa fa-caret-down" /></div>
|
||||||
|
<div className="user-desc"> {desc} </div>
|
||||||
|
<div className="user-control">
|
||||||
|
<img src={IMAGES.flag(color)} alt="" />
|
||||||
|
<div className="user-control-mines">{mines}</div>
|
||||||
|
<div className={buzzClass} onClick={onClickBombSelector}>
|
||||||
|
<div className="bomb">
|
||||||
|
{haveBomb && <img src={enabledBomb && active ? IMAGES.bomb : IMAGES.bombDisabled} alt="" />}
|
||||||
|
{!haveBomb && <img src={IMAGES.bombExploded} alt="" />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="clear" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default User;
|
||||||
70
assets/js/mine-seeker/components/user/UserControl.jsx
Normal file
70
assets/js/mine-seeker/components/user/UserControl.jsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { Fragment, useState } from 'react';
|
||||||
|
import { useGame } from '@mine-contexts';
|
||||||
|
import User from './User';
|
||||||
|
import BonusStatsDialog from '../BonusStatsDialog';
|
||||||
|
|
||||||
|
const UserControl = ({ resign }) => {
|
||||||
|
const { webPlayer, activePlayer, mines, foundMines, red, blue, onBombToggle } = useGame();
|
||||||
|
const [bonusDialogOpen, setBonusDialogOpen] = useState(false);
|
||||||
|
const activeColor = activePlayer ? 'blue' : 'red';
|
||||||
|
const resignClass = 'resign' + (activeColor !== webPlayer ? ' disabled' : '');
|
||||||
|
const minesClass = 'active-mines' + (foundMines ? ' found-mine' : '');
|
||||||
|
|
||||||
|
const handleBombClick = (color, player) => {
|
||||||
|
const p = 'red' === color ? red : blue;
|
||||||
|
if (p.haveBomb && p.enabledBomb && activePlayer === player) {
|
||||||
|
onBombToggle();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBonusClick = () => {
|
||||||
|
setBonusDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<div className="users">
|
||||||
|
<User
|
||||||
|
color="blue" webPlayer={webPlayer} {...blue}
|
||||||
|
onClickBombSelector={() => handleBombClick('blue', 1)}
|
||||||
|
onBonusClick={handleBonusClick}
|
||||||
|
/>
|
||||||
|
<div className="active-mines-container">
|
||||||
|
<i className="fa fa-star" />
|
||||||
|
<div className={minesClass}>
|
||||||
|
<div className="active-mines-nbr">{mines}</div>
|
||||||
|
<div className="active-mines-shine" />
|
||||||
|
</div>
|
||||||
|
<i className="fa fa-star" />
|
||||||
|
</div>
|
||||||
|
<div className="clear" />
|
||||||
|
<User
|
||||||
|
color="red" webPlayer={webPlayer} {...red}
|
||||||
|
onClickBombSelector={() => handleBombClick('red', 0)}
|
||||||
|
onBonusClick={handleBonusClick}
|
||||||
|
/>
|
||||||
|
<button className={resignClass} onClick={resign}>
|
||||||
|
<div className="resign-shine" />
|
||||||
|
Resign
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<BonusStatsDialog
|
||||||
|
open={bonusDialogOpen}
|
||||||
|
onClose={() => setBonusDialogOpen(false)}
|
||||||
|
red={red}
|
||||||
|
blue={blue}
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserControl;
|
||||||
16
assets/js/mine-seeker/contexts/GameContext.jsx
Normal file
16
assets/js/mine-seeker/contexts/GameContext.jsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
|
||||||
|
const GameContext = createContext(null);
|
||||||
|
|
||||||
|
export const useGame = () => useContext(GameContext);
|
||||||
|
|
||||||
|
export default GameContext;
|
||||||
274
assets/js/mine-seeker/contexts/GameProvider.jsx
Normal file
274
assets/js/mine-seeker/contexts/GameProvider.jsx
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useRef } from 'react';
|
||||||
|
import { Howl } from 'howler';
|
||||||
|
import GameContext from './GameContext';
|
||||||
|
import { useGameRefs, useGameState } from '@mine-hooks';
|
||||||
|
import { DESC, IMAGES, patchCells } from '@mine-utils';
|
||||||
|
|
||||||
|
export const GameProvider = ({ children }) => {
|
||||||
|
const {
|
||||||
|
webPlayerRef,
|
||||||
|
activePlayerRef,
|
||||||
|
bombSelectedRef,
|
||||||
|
connectionLostRef,
|
||||||
|
redRef,
|
||||||
|
blueRef,
|
||||||
|
lastClickedRef,
|
||||||
|
endRef,
|
||||||
|
} = useGameRefs();
|
||||||
|
|
||||||
|
const {
|
||||||
|
webPlayer, setWebPlayer,
|
||||||
|
activePlayer, setActivePlayer,
|
||||||
|
overlay, setOverlay,
|
||||||
|
overlayTitle, setOverlayTitle,
|
||||||
|
overlaySubTitle, setOverlaySubTitle,
|
||||||
|
mines, setMines,
|
||||||
|
bombSelected, setBombSelected,
|
||||||
|
foundMines, setFoundMines,
|
||||||
|
red, setRed,
|
||||||
|
blue, setBlue,
|
||||||
|
cells, setCells,
|
||||||
|
gridReady, setGridReady,
|
||||||
|
connectionLost, setConnectionLost,
|
||||||
|
} = useGameState();
|
||||||
|
|
||||||
|
const [gameUuid, setGameUuid] = React.useState(null);
|
||||||
|
|
||||||
|
const sounds = useRef({
|
||||||
|
click: new Howl({ src: ['/sound/click.mp3'] }),
|
||||||
|
bomb: new Howl({ src: ['/sound/bomb.mp3'] }),
|
||||||
|
mine: new Howl({ src: ['/sound/mine.mp3'] }),
|
||||||
|
warning: new Howl({ src: ['/sound/warning.mp3'] }),
|
||||||
|
won: new Howl({ src: ['/sound/won.mp3'] }),
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Sync helpers (keep ref + state in lockstep) */
|
||||||
|
|
||||||
|
const syncWebPlayer = v => {
|
||||||
|
webPlayerRef.current = v;
|
||||||
|
setWebPlayer(v);
|
||||||
|
};
|
||||||
|
const syncActivePlayer = v => {
|
||||||
|
activePlayerRef.current = v;
|
||||||
|
setActivePlayer(v);
|
||||||
|
};
|
||||||
|
const syncBombSelected = v => {
|
||||||
|
bombSelectedRef.current = v;
|
||||||
|
setBombSelected(v);
|
||||||
|
};
|
||||||
|
const syncConnLost = v => {
|
||||||
|
connectionLostRef.current = v;
|
||||||
|
setConnectionLost(v);
|
||||||
|
};
|
||||||
|
const syncRed = fn => {
|
||||||
|
const n = fn(redRef.current);
|
||||||
|
redRef.current = n;
|
||||||
|
setRed(n);
|
||||||
|
};
|
||||||
|
const syncBlue = fn => {
|
||||||
|
const n = fn(blueRef.current);
|
||||||
|
blueRef.current = n;
|
||||||
|
setBlue(n);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Overlay */
|
||||||
|
|
||||||
|
const showOverlay = (title, sub) => {
|
||||||
|
setOverlay(true);
|
||||||
|
setOverlayTitle(title);
|
||||||
|
setOverlaySubTitle(sub);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideOverlay = () => setOverlay(false);
|
||||||
|
|
||||||
|
/** Cell helpers */
|
||||||
|
|
||||||
|
const applyRevealedCell = (cell, player, isMainCell = false) => {
|
||||||
|
const { row, col, value } = cell;
|
||||||
|
setCells(prev => {
|
||||||
|
if (prev[row][col].active) return prev;
|
||||||
|
const patch = 'm' === value
|
||||||
|
? { currentImage: IMAGES.flag(player), currentObj: 'm', active: true }
|
||||||
|
: { currentImage: value, currentObj: value, active: true };
|
||||||
|
if (isMainCell) {
|
||||||
|
patch.lastClickedRed = 'red' === player;
|
||||||
|
patch.lastClickedBlue = 'blue' === player;
|
||||||
|
}
|
||||||
|
return patchCells(prev, [{ row, col, ...patch }]);
|
||||||
|
});
|
||||||
|
if (isMainCell) lastClickedRef.current = { ...lastClickedRef.current, [player]: [row, col] };
|
||||||
|
};
|
||||||
|
|
||||||
|
const showLeftMines = (leftMines = []) => {
|
||||||
|
if (!leftMines.length) return;
|
||||||
|
setCells(prev => {
|
||||||
|
const patches = leftMines
|
||||||
|
.filter(({ row, col }) => !prev[row][col].active)
|
||||||
|
.map(({ row, col }) => ({ row, col, currentImage: IMAGES.leftMine }));
|
||||||
|
return patches.length ? patchCells(prev, patches) : prev;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Game logic */
|
||||||
|
|
||||||
|
const changePlayer = () => {
|
||||||
|
const wasColor = activePlayerRef.current ? 'blue' : 'red';
|
||||||
|
const nextColor = activePlayerRef.current ? 'red' : 'blue';
|
||||||
|
const nextVal = activePlayerRef.current ? 0 : 1;
|
||||||
|
const desc = wasColor === webPlayerRef.current ? DESC.buddy : DESC.you;
|
||||||
|
|
||||||
|
syncActivePlayer(nextVal);
|
||||||
|
syncRed(p => ({ ...p, active: 'red' === nextColor, desc: 'red' === nextColor ? desc : '' }));
|
||||||
|
syncBlue(p => ({ ...p, active: 'blue' === nextColor, desc: 'blue' === nextColor ? desc : '' }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyStep = stepData => {
|
||||||
|
const {
|
||||||
|
player,
|
||||||
|
bomb: isBomb,
|
||||||
|
minesFound = 0,
|
||||||
|
revealedCells = [],
|
||||||
|
redPoints: rp,
|
||||||
|
bluePoints: bp,
|
||||||
|
redBonusPoints = 0,
|
||||||
|
blueBonusPoints = 0,
|
||||||
|
redBonusStats = {},
|
||||||
|
blueBonusStats = {},
|
||||||
|
} = stepData;
|
||||||
|
|
||||||
|
if (isBomb) {
|
||||||
|
sounds.current.bomb.play();
|
||||||
|
} else if (0 < minesFound) {
|
||||||
|
const cur = ('red' === player ? redRef : blueRef).current.mines;
|
||||||
|
sounds.current[20 < cur + minesFound ? 'warning' : 'mine'].play();
|
||||||
|
} else {
|
||||||
|
sounds.current.click.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
const lc = lastClickedRef.current[player];
|
||||||
|
setCells(prev => {
|
||||||
|
let next = prev;
|
||||||
|
if (lc) next = patchCells(next, [{ row: lc[0], col: lc[1], lastClickedRed: false, lastClickedBlue: false }]);
|
||||||
|
|
||||||
|
revealedCells.forEach(({ row, col, value }, idx) => {
|
||||||
|
if (next[row][col].active) return;
|
||||||
|
const patch = 'm' === value
|
||||||
|
? { currentImage: IMAGES.flag(player), currentObj: 'm', active: true }
|
||||||
|
: { currentImage: value, currentObj: value, active: true };
|
||||||
|
if (0 === idx) {
|
||||||
|
patch.lastClickedRed = 'red' === player;
|
||||||
|
patch.lastClickedBlue = 'blue' === player;
|
||||||
|
}
|
||||||
|
next = patchCells(next, [{ row, col, ...patch }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isBomb) next = next.map(r => r.map(c => null !== c.bombTargetArea ? { ...c, bombTargetArea: null } : c));
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (0 < revealedCells.length) {
|
||||||
|
lastClickedRef.current = { ...lastClickedRef.current, [player]: [revealedCells[0].row, revealedCells[0].col] };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 < minesFound) {
|
||||||
|
setMines(51 - rp - bp);
|
||||||
|
setFoundMines(true);
|
||||||
|
setTimeout(() => setFoundMines(false), 500);
|
||||||
|
syncRed(p => ({ ...p, mines: 'red' === player ? rp : p.mines }));
|
||||||
|
syncBlue(p => ({ ...p, mines: 'blue' === player ? bp : p.mines }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update bonus points and stats
|
||||||
|
syncRed(p => ({
|
||||||
|
...p,
|
||||||
|
bonusPoints: 'red' === player ? redBonusPoints : p.bonusPoints,
|
||||||
|
bonusStats: 'red' === player ? redBonusStats : p.bonusStats,
|
||||||
|
}));
|
||||||
|
syncBlue(p => ({
|
||||||
|
...p,
|
||||||
|
bonusPoints: 'blue' === player ? blueBonusPoints : p.bonusPoints,
|
||||||
|
bonusStats: 'blue' === player ? blueBonusStats : p.bonusStats,
|
||||||
|
}));
|
||||||
|
|
||||||
|
syncRed(p => ({ ...p, enabledBomb: rp <= bp }));
|
||||||
|
syncBlue(p => ({ ...p, enabledBomb: bp <= rp }));
|
||||||
|
|
||||||
|
if (isBomb || 0 === minesFound) changePlayer();
|
||||||
|
|
||||||
|
if (isBomb) {
|
||||||
|
syncBombSelected(false);
|
||||||
|
syncRed(p => 'red' === player ? { ...p, haveBomb: false } : p);
|
||||||
|
syncBlue(p => 'blue' === player ? { ...p, haveBomb: false } : p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeGameEndIfItEnds = (bluePoints, redPoints, resign = false, leftMines = []) => {
|
||||||
|
const redWins = 25 < redPoints;
|
||||||
|
const blueWins = 25 < bluePoints;
|
||||||
|
|
||||||
|
if (redWins || blueWins || resign) {
|
||||||
|
sounds.current.won.play();
|
||||||
|
|
||||||
|
if (!resign) showOverlay((redWins ? 'Red' : 'Blue') + ' wins the game!', 'Play again!');
|
||||||
|
|
||||||
|
showLeftMines(leftMines);
|
||||||
|
syncActivePlayer(false);
|
||||||
|
syncRed(p => ({ ...p, desc: '' }));
|
||||||
|
syncBlue(p => ({ ...p, desc: '' }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resignProcess = (color, uuid = null) => {
|
||||||
|
const wp = webPlayerRef.current;
|
||||||
|
if (uuid) {
|
||||||
|
setGameUuid(uuid);
|
||||||
|
}
|
||||||
|
showOverlay(
|
||||||
|
color === wp ? 'You have been give up' : 'Your opponent has been resigned',
|
||||||
|
color === wp ? 'You LOSE!' : 'You WIN!',
|
||||||
|
);
|
||||||
|
endRef.current = true;
|
||||||
|
makeGameEndIfItEnds(0, 0, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBombToggle = () => {
|
||||||
|
const next = !bombSelectedRef.current;
|
||||||
|
syncBombSelected(next);
|
||||||
|
if (!next) {
|
||||||
|
setCells(prev => prev.map(r => r.map(c => null !== c.bombTargetArea ? { ...c, bombTargetArea: null } : c)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GameContext.Provider
|
||||||
|
value={{
|
||||||
|
// State (for rendering)
|
||||||
|
webPlayer, activePlayer, overlay, overlayTitle, overlaySubTitle,
|
||||||
|
mines, bombSelected, foundMines, red, blue, cells, gridReady, connectionLost, gameUuid,
|
||||||
|
// Setters needed by useServerComm
|
||||||
|
setCells, setGridReady, setGameUuid,
|
||||||
|
// Refs (needed by useServerComm for async-safe reads)
|
||||||
|
webPlayerRef, activePlayerRef, bombSelectedRef, connectionLostRef, endRef,
|
||||||
|
// Sync helpers
|
||||||
|
syncWebPlayer, syncActivePlayer, syncBombSelected, syncConnLost, syncRed, syncBlue,
|
||||||
|
// Game logic called by useServerComm
|
||||||
|
showOverlay, hideOverlay,
|
||||||
|
applyRevealedCell, applyStep,
|
||||||
|
makeGameEndIfItEnds, resignProcess,
|
||||||
|
// UI action
|
||||||
|
onBombToggle,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</GameContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
13
assets/js/mine-seeker/contexts/index.js
Normal file
13
assets/js/mine-seeker/contexts/index.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { useGame } from './GameContext';
|
||||||
|
export { GameProvider } from './GameProvider';
|
||||||
|
export { default as GameContext } from './GameContext';
|
||||||
|
|
||||||
@@ -1,449 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import GridField from './grid-field';
|
|
||||||
import UserControl from '../user/user-control';
|
|
||||||
import {Howl, Howler} from 'howler';
|
|
||||||
|
|
||||||
class GridControl extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
let click = new Howl({src: ['/sound/click.mp3']}),
|
|
||||||
bomb = new Howl({src: ['/sound/bomb.mp3']}),
|
|
||||||
mine = new Howl({src: ['/sound/mine.mp3']}),
|
|
||||||
warning = new Howl({src: ['/sound/warning.mp3']}),
|
|
||||||
won = new Howl({src: ['/sound/won.mp3']});
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
env: props.env,
|
|
||||||
webPlayer: null,
|
|
||||||
grid: null,
|
|
||||||
desc: null,
|
|
||||||
renderGridFields: false,
|
|
||||||
gridFields: [],
|
|
||||||
updatedFieldCache: [],
|
|
||||||
bombFieldCache: [],
|
|
||||||
foundUserMineCache: 0,
|
|
||||||
playBomb: false,
|
|
||||||
overlay: false,
|
|
||||||
overlayTitle: "",
|
|
||||||
overlaySubTitle: "",
|
|
||||||
sound: {
|
|
||||||
click: click,
|
|
||||||
bomb: bomb,
|
|
||||||
mine: mine,
|
|
||||||
warning: warning,
|
|
||||||
won: won
|
|
||||||
},
|
|
||||||
lastClicked: {
|
|
||||||
red: null,
|
|
||||||
blue: null
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
refString(row, col) {
|
|
||||||
return 'gridField_' + row + '_' + col;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkMine(row, col) {
|
|
||||||
return typeof this.state.grid[row] !== 'undefined' && typeof this.state.grid[row][col] !== 'undefined' && this.state.grid[row][col] !== 'm';
|
|
||||||
}
|
|
||||||
|
|
||||||
checkFieldHasBeenNeverClicked(row, col) {
|
|
||||||
return this.state.updatedFieldCache.indexOf(this.refString(row, col)) < 0 && !this.refs[this.refString(row, col)].state.active;
|
|
||||||
}
|
|
||||||
|
|
||||||
getBombRadius(row, col) {
|
|
||||||
let isBombTargetCenter = row > 1 && row < this.state.grid.length - 2 && col > 1 && col < this.state.grid[row].length - 2;
|
|
||||||
|
|
||||||
/** if the (5x5) target not fits the grid */
|
|
||||||
if (!isBombTargetCenter) {
|
|
||||||
col = col < 2 ? 2 : col;
|
|
||||||
row = row < 2 ? 2 : row;
|
|
||||||
row = row > this.state.grid.length - 3 ? this.state.grid.length - 3 : row;
|
|
||||||
col = col > this.state.grid[0].length - 3 ? this.state.grid[0].length - 3 : col;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
[row, col], [row - 2, col - 2], [row - 2, col], [row - 2, col + 2], [row, col - 2], [row, col + 2],
|
|
||||||
[row + 2, col - 2], [row + 2, col], [row + 2, col + 2], [row - 2, col + 1], [row - 2, col - 1],
|
|
||||||
[row - 1, col - 2], [row - 1, col - 1], [row - 1, col], [row - 1, col + 1], [row - 1, col + 2],
|
|
||||||
[row, col - 1], [row, col + 1], [row + 1, col - 2], [row + 1, col - 1], [row + 1, col],
|
|
||||||
[row + 1, col + 1], [row + 1, col + 2], [row + 2, col - 1], [row + 2, col + 1]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
getBombFieldRadius() {
|
|
||||||
return [
|
|
||||||
[null, null], [0, 0], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2], [2, 2],
|
|
||||||
[null, null], [null, null], [null, null], [null, null], [null, null], [null, null], [null, null],
|
|
||||||
[null, null], [null, null], [null, null], [null, null], [null, null], [null, null], [null, null],
|
|
||||||
[null, null], [null, null]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
getNeighbourRadius(row, col) {
|
|
||||||
return [
|
|
||||||
[row - 1, col], [row - 1, col - 1], [row - 1, col + 1], [row, col - 1], [row, col + 1], [row + 1, col],
|
|
||||||
[row + 1, col + 1], [row + 1, col - 1]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
checkNeighbourItem(row, col) {
|
|
||||||
if (this.checkMine(row, col)) {
|
|
||||||
var currentField = this.refs[this.refString(row, col)];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* It must be cached because the GridField.state not updated until
|
|
||||||
* all showAppropriateFields() method runned out!!
|
|
||||||
*/
|
|
||||||
if (this.checkFieldHasBeenNeverClicked(row, col)) {
|
|
||||||
this.state.updatedFieldCache.push(this.refString(row, col));
|
|
||||||
|
|
||||||
currentField.setState({
|
|
||||||
currentImage: this.state.grid[row][col],
|
|
||||||
currentObj: this.state.grid[row][col],
|
|
||||||
active: true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.state.grid[row][col] === 0) {
|
|
||||||
return {
|
|
||||||
row: row,
|
|
||||||
col: col
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkNeighbours(row, col) {
|
|
||||||
let anotherFields = [],
|
|
||||||
neighbours = this.getNeighbourRadius(row, col);
|
|
||||||
|
|
||||||
for (let i = 0, j = neighbours.length; i < j; i++) {
|
|
||||||
anotherFields.push(this.checkNeighbourItem(neighbours[i][0], neighbours[i][1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return anotherFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
bombClear() {
|
|
||||||
if (this.state.bombFieldCache.length) {
|
|
||||||
for (var i = 0, j = this.state.bombFieldCache.length; i < j; i++) {
|
|
||||||
var cacheItem = this.state.bombFieldCache[i];
|
|
||||||
this.refs[this.refString(cacheItem[0], cacheItem[1])]
|
|
||||||
.setState({bombTargetArea: null});
|
|
||||||
}
|
|
||||||
this.state.bombFieldCache = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bombCreate(row, col) {
|
|
||||||
var bombFieldSymbols = this.getBombFieldRadius(),
|
|
||||||
bombFields = this.getBombRadius(row, col);
|
|
||||||
|
|
||||||
for (var i = 0, j = bombFields.length; i < j; i++) {
|
|
||||||
this.state.bombFieldCache.push(bombFields[i]);
|
|
||||||
|
|
||||||
this.refs[this.refString(bombFields[i][0], bombFields[i][1])]
|
|
||||||
.setState({bombTargetArea: bombFieldSymbols[i]});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showLeftMines() {
|
|
||||||
for (let i = 0, j = this.state.grid.length; i < j; i++) {
|
|
||||||
for (let k = 0, l = this.state.grid[i].length; k < l; k++) {
|
|
||||||
let currentField = this.refs[this.refString(i, k)];
|
|
||||||
|
|
||||||
if (this.state.grid[i][k] === 'm' && this.checkFieldHasBeenNeverClicked(i, k)) {
|
|
||||||
currentField.setState({
|
|
||||||
currentImage: currentField.state.icons.root + currentField.state.icons.left
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** set __ACTIVE__ player in the UserControl !!!! */
|
|
||||||
changePlayer(idx, max, currentObject) {
|
|
||||||
var userControl = this.refs.userControl,
|
|
||||||
activePlayer = userControl.state.activePlayer ? 'blue' : 'red',
|
|
||||||
inactivePlayer = userControl.state.activePlayer ? 'red' : 'blue';
|
|
||||||
|
|
||||||
if (
|
|
||||||
userControl.state.bombSelected && idx === (max - 1) ||
|
|
||||||
!idx && !userControl.state.bombSelected && currentObject !== 'm'
|
|
||||||
) {
|
|
||||||
userControl.setState({
|
|
||||||
activePlayer: userControl.state.activePlayer ? 0 : 1
|
|
||||||
});
|
|
||||||
|
|
||||||
/** the desc is inversely because the user.active is not changed yet !!! */
|
|
||||||
userControl.refs[activePlayer].setState({
|
|
||||||
active: false,
|
|
||||||
desc: ""
|
|
||||||
});
|
|
||||||
|
|
||||||
userControl.refs[inactivePlayer].setState({
|
|
||||||
active: true,
|
|
||||||
desc: activePlayer === this.state.webPlayer
|
|
||||||
? this.state.desc.buddy
|
|
||||||
: this.state.desc.you
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show all fields that needed after click
|
|
||||||
*
|
|
||||||
* @param currentField
|
|
||||||
* @param row
|
|
||||||
* @param col
|
|
||||||
*/
|
|
||||||
showAppropriateFields(currentField, row, col) {
|
|
||||||
currentField.setState({
|
|
||||||
currentObj: this.state.grid[row][col],
|
|
||||||
active: true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.checkFieldHasBeenNeverClicked(row, col)) {
|
|
||||||
this.state.updatedFieldCache.push(this.refString(row, col));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.grid[row][col] === 0) {
|
|
||||||
var neighbours = this.checkNeighbours(row, col);
|
|
||||||
|
|
||||||
neighbours
|
|
||||||
.filter((i) => {
|
|
||||||
return i !== false;
|
|
||||||
})
|
|
||||||
.forEach((element, index, array) => {
|
|
||||||
var currentField = this.refs[this.refString(element.row, element.col)];
|
|
||||||
this.showAppropriateFields(currentField, element.row, element.col);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Player control method
|
|
||||||
*
|
|
||||||
* @param currentObject {int|string} Current object from Grid class
|
|
||||||
* @param row {int}
|
|
||||||
* @param col {int}
|
|
||||||
* @param justOnFirstIteration {int} When bomb is being used check the whole explosion area
|
|
||||||
*/
|
|
||||||
handleGridField(currentObject, row, col, justOnFirstIteration = 0) {
|
|
||||||
var userControl = this.refs.userControl,
|
|
||||||
gridFieldControl = this.refs[this.refString(row, col)],
|
|
||||||
activePlayer = userControl.state.activePlayer ? 'blue' : 'red',
|
|
||||||
inactivePlayer = userControl.state.activePlayer ? 'red' : 'blue';
|
|
||||||
|
|
||||||
/** if the clicked field is NEVER CLICKED */
|
|
||||||
if (this.checkFieldHasBeenNeverClicked(row, col)) {
|
|
||||||
/** update LAST CLICKED grid field */
|
|
||||||
if (!justOnFirstIteration) {
|
|
||||||
if (this.state.lastClicked[activePlayer] !== null) {
|
|
||||||
this.refs[this.refString(this.state.lastClicked[activePlayer][0], this.state.lastClicked[activePlayer][1])].setState({
|
|
||||||
lastClickedRed: false,
|
|
||||||
lastClickedBlue: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.state.lastClicked[activePlayer] = [row, col];
|
|
||||||
|
|
||||||
/** if you found mine */
|
|
||||||
if (currentObject === 'm') {
|
|
||||||
this.state.foundUserMineCache++;
|
|
||||||
|
|
||||||
if (!justOnFirstIteration) {
|
|
||||||
/** set last clicked field w/ color */
|
|
||||||
this.state.lastClicked[activePlayer] = [row, col];
|
|
||||||
|
|
||||||
this.state.sound[
|
|
||||||
(userControl.refs[activePlayer].state.mines + this.state.foundUserMineCache) > 20
|
|
||||||
? 'warning'
|
|
||||||
: 'mine'
|
|
||||||
].play();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** set current image in field */
|
|
||||||
gridFieldControl.setState({
|
|
||||||
currentImage: gridFieldControl.state.icons.root + gridFieldControl.state.icons.flag[activePlayer]
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.state.sound.click.play();
|
|
||||||
|
|
||||||
/** set current image in field - WHEN it is a number */
|
|
||||||
if (!isNaN(currentObject)) {
|
|
||||||
gridFieldControl.setState({
|
|
||||||
currentImage: currentObject
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set bombs status - we must add one mine (currentObject === 'm' ? 1 : 0) to current mine
|
|
||||||
* when it found NOW because the status is not refreshed unless the handleGridField() ends
|
|
||||||
*/
|
|
||||||
userControl.refs[activePlayer].setState({
|
|
||||||
enabledBomb: userControl.refs[activePlayer].state.mines + (currentObject === 'm' ? 1 : 0) <= userControl.refs[inactivePlayer].state.mines
|
|
||||||
});
|
|
||||||
|
|
||||||
userControl.refs[inactivePlayer].setState({
|
|
||||||
enabledBomb: userControl.refs[activePlayer].state.mines + (currentObject === 'm' ? 1 : 0) >= userControl.refs[inactivePlayer].state.mines
|
|
||||||
});
|
|
||||||
|
|
||||||
/** set-up last clicked */
|
|
||||||
if (!justOnFirstIteration) {
|
|
||||||
gridFieldControl.setState({
|
|
||||||
lastClickedRed: activePlayer === 'red',
|
|
||||||
lastClickedBlue: activePlayer === 'blue'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show elems w/ conditions
|
|
||||||
*
|
|
||||||
* @param row
|
|
||||||
* @param col
|
|
||||||
* @param idx
|
|
||||||
* @param max
|
|
||||||
*/
|
|
||||||
show(row, col, idx = 0, max = 0) {
|
|
||||||
this.handleGridField(this.state.grid[row][col], row, col, idx);
|
|
||||||
this.showAppropriateFields(this.refs[this.refString(row, col)], row, col);
|
|
||||||
this.changePlayer(idx, max, this.state.grid[row][col]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* STEP one
|
|
||||||
*
|
|
||||||
* @param coords
|
|
||||||
*/
|
|
||||||
stepEvent(coords) {
|
|
||||||
/** if the clicked field is NEVER CLICKED */
|
|
||||||
if (this.checkFieldHasBeenNeverClicked(coords[0], coords[1])) {
|
|
||||||
var activePlayer = this.refs.userControl.state.activePlayer ? 'blue' : 'red';
|
|
||||||
|
|
||||||
this.state.foundUserMineCache = 0;
|
|
||||||
this.state.playBomb = true;
|
|
||||||
|
|
||||||
/** Show elements */
|
|
||||||
if (this.refs.userControl.state.bombSelected) {
|
|
||||||
this.state.sound.bomb.play();
|
|
||||||
|
|
||||||
var bombRadius = this.getBombRadius(coords[0], coords[1]);
|
|
||||||
for (var i = 0, j = bombRadius.length; i < j; i++) {
|
|
||||||
this.show(bombRadius[i][0], bombRadius[i][1], i, j);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** remove BOMB from activePlayer */
|
|
||||||
this.refs.userControl.refs[activePlayer].setState({
|
|
||||||
haveBomb: false
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.show(coords[0], coords[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Mine score handling */
|
|
||||||
if (this.state.foundUserMineCache) {
|
|
||||||
this.refs.userControl.setState({
|
|
||||||
mines: this.refs.userControl.state.mines - this.state.foundUserMineCache,
|
|
||||||
foundMines: true
|
|
||||||
}, () => {
|
|
||||||
/** because of CSS animation in .found-mine */
|
|
||||||
setTimeout(() => this.refs.userControl.setState({foundMines: false}), 500);
|
|
||||||
|
|
||||||
/** add the found mines to the active Player */
|
|
||||||
this.refs.userControl.refs[activePlayer].setState({
|
|
||||||
mines: this.refs.userControl.refs[activePlayer].state.mines + this.state.foundUserMineCache
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reset BOMB status */
|
|
||||||
if (this.refs.userControl.state.bombSelected) {
|
|
||||||
/** reset bomb selected status */
|
|
||||||
this.refs.userControl.setState({bombSelected: false});
|
|
||||||
|
|
||||||
/** clear cache, reset symbols */
|
|
||||||
this.bombClear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On Hover when you want to drop BOMB
|
|
||||||
* Target grid field
|
|
||||||
* @param coords
|
|
||||||
*/
|
|
||||||
onHoverGridField(coords) {
|
|
||||||
if (this.refs.userControl.state.bombSelected) {
|
|
||||||
var activePlayer = this.refs.userControl.state.activePlayer ? 'blue' : 'red';
|
|
||||||
|
|
||||||
if (activePlayer === this.state.webPlayer) {
|
|
||||||
/** clear cache, reset symbols */
|
|
||||||
this.bombClear();
|
|
||||||
|
|
||||||
/** new cache && field activate */
|
|
||||||
this.bombCreate(coords[0], coords[1]);
|
|
||||||
} else {
|
|
||||||
this.refs.userControl.setState({bombSelected: false});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
overlayClass() {
|
|
||||||
return 'game-overlay' + (this.state.overlay ? '' : ' hide');
|
|
||||||
}
|
|
||||||
|
|
||||||
renderGridFields() {
|
|
||||||
for (let i = 0, j = this.state.grid.length; i < j; i++) {
|
|
||||||
for (let k = 0, l = this.state.grid[i].length; k < l; k++) {
|
|
||||||
this.state.gridFields.push(
|
|
||||||
<GridField row={i}
|
|
||||||
col={k}
|
|
||||||
ref={this.refString(i, k)}
|
|
||||||
key={window.btoa((Math.random() * 0.5).toString())}
|
|
||||||
handleHoverOn={this.onHoverGridField.bind(this, [i, k])}
|
|
||||||
onClick={this.props.onClick.bind(null, [i, k])}/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
/** Render the grid fields just one time in one party #12 */
|
|
||||||
this.state.renderGridFields && this.renderGridFields();
|
|
||||||
this.state.renderGridFields = false;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="game-wrapper">
|
|
||||||
<div className={this.overlayClass()}>
|
|
||||||
<div className="game-overlay-window">
|
|
||||||
<h1>{this.state.overlayTitle}</h1>
|
|
||||||
<h2>{this.state.overlaySubTitle}</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<UserControl ref="userControl"
|
|
||||||
resign={this.props.resign}
|
|
||||||
webPlayer={this.state.webPlayer}
|
|
||||||
bombClear={this.bombClear.bind(this)}/>
|
|
||||||
<div className="grid-container">
|
|
||||||
<div className="grid">
|
|
||||||
<>
|
|
||||||
{this.state.gridFields}
|
|
||||||
</>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default GridControl;
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
class GridField extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
currentObj: 'w',
|
|
||||||
currentImage: null,
|
|
||||||
active: false,
|
|
||||||
lastClickedRed: false,
|
|
||||||
lastClickedBlue: false,
|
|
||||||
bombTargetArea: null,
|
|
||||||
icons: {
|
|
||||||
root: '/images/',
|
|
||||||
water: {
|
|
||||||
1: 'bg-wave-1-outbg.png',
|
|
||||||
2: 'bg-wave-1-outbg.png',
|
|
||||||
3: 'bg-wave-2-outbg.png'
|
|
||||||
},
|
|
||||||
flag: {
|
|
||||||
red: 'bg-flag-red-outbg.png',
|
|
||||||
blue: 'bg-flag-blue-outbg.png'
|
|
||||||
},
|
|
||||||
target: {
|
|
||||||
lastBlue: 'bg-last-blue-outbg.png',
|
|
||||||
lastRed: 'bg-last-red-outbg.png',
|
|
||||||
crosshair: 'bg-target-outbg.png',
|
|
||||||
crosshairBomb: 'bg-target-bomb-outbg.png'
|
|
||||||
},
|
|
||||||
left: 'bg-left-mine-outbg.png'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
var wave = Math.floor(Math.random() * 3) + 1;
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
currentImage: this.state.icons.root + this.state.icons.water[wave]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
classNameWhenActive() {
|
|
||||||
return 'field'
|
|
||||||
+ (this.state.active === true ? ' active' : '')
|
|
||||||
+ (this.state.active === true && this.state.currentObj === 'm' ? ' mine' : '')
|
|
||||||
+ ' color-' + this.state.currentObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentImage() {
|
|
||||||
return isNaN(this.state.currentImage)
|
|
||||||
?
|
|
||||||
<div className="flag-mine">
|
|
||||||
<img src={this.state.currentImage}/>
|
|
||||||
<div className="flag-mine-base"></div>
|
|
||||||
</div>
|
|
||||||
: this.state.currentImage ? <div className="flag-number">{this.state.currentImage}</div> : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
lastClickedClass() {
|
|
||||||
return 'field-'
|
|
||||||
+ (this.state.lastClickedRed ? 'red' : '')
|
|
||||||
+ (this.state.lastClickedBlue ? 'blue' : '') + '-last last-clicked';
|
|
||||||
}
|
|
||||||
|
|
||||||
lastClickedSrc() {
|
|
||||||
return this.state.lastClickedRed
|
|
||||||
? "/images/bg-last-red-outbg.png"
|
|
||||||
: "/images/bg-last-blue-outbg.png";
|
|
||||||
}
|
|
||||||
|
|
||||||
currentLastClicked() {
|
|
||||||
return this.state.lastClickedRed || this.state.lastClickedBlue
|
|
||||||
? <img className={this.lastClickedClass()}
|
|
||||||
src={this.lastClickedSrc()}
|
|
||||||
alt="blue last"/>
|
|
||||||
: '';
|
|
||||||
}
|
|
||||||
|
|
||||||
createBombTarget() {
|
|
||||||
if (this.state.bombTargetArea !== null) {
|
|
||||||
var vert = '', hor = '';
|
|
||||||
|
|
||||||
switch (this.state.bombTargetArea[0]) {
|
|
||||||
case 0:
|
|
||||||
vert = 'left';
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
vert = 'center';
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
vert = 'right';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
vert = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this.state.bombTargetArea[1]) {
|
|
||||||
case 0:
|
|
||||||
hor = 'top';
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
hor = 'middle';
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
hor = 'bottom';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
vert = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var src = vert === null
|
|
||||||
? '/images/bg-bomb-empty-outbg.png'
|
|
||||||
: '/images/bg-bomb-' + hor + '-' + vert + '-outbg.png';
|
|
||||||
|
|
||||||
return <img className="field-bomb-target"
|
|
||||||
src={src}
|
|
||||||
alt="bomb target"/>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="field-wrapper"
|
|
||||||
onClick={this.props.onClick}
|
|
||||||
onMouseEnter={this.props.handleHoverOn}>
|
|
||||||
<img className="field-target"
|
|
||||||
src="/images/bg-target-outbg.png"
|
|
||||||
alt="target"/>
|
|
||||||
{this.createBombTarget()}
|
|
||||||
{this.currentLastClicked()}
|
|
||||||
<div className={this.classNameWhenActive()}>
|
|
||||||
<div className="field-corner">
|
|
||||||
{this.currentImage()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default GridField;
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
class Grid extends React.Component {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
row: 16,
|
|
||||||
col: 16,
|
|
||||||
mines: 51,
|
|
||||||
set: []
|
|
||||||
};
|
|
||||||
|
|
||||||
this.state.grid = this.numberingGrid(
|
|
||||||
this.createGrid(
|
|
||||||
this.shuffleSet(
|
|
||||||
this.createSet(
|
|
||||||
this.state.set
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
createSet(obj) {
|
|
||||||
for (var i = 0, j = this.state.row * this.state.col; i < j; i++) {
|
|
||||||
obj.push(
|
|
||||||
this.state.mines > 0
|
|
||||||
? "m"
|
|
||||||
: "w"
|
|
||||||
);
|
|
||||||
this.state.mines--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
shuffleSet(obj) {
|
|
||||||
return obj.sort(function () {
|
|
||||||
return Math.round(Math.random()) - .5;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createGrid(obj) {
|
|
||||||
var grid = [[]],
|
|
||||||
row = 0,
|
|
||||||
col = 0;
|
|
||||||
|
|
||||||
for (var i = 0, j = obj.length; i < j; i++) {
|
|
||||||
grid[row][col] = obj[i];
|
|
||||||
|
|
||||||
if (col === 15 && row !== 15) {
|
|
||||||
row++;
|
|
||||||
col = 0;
|
|
||||||
grid.push([]);
|
|
||||||
} else {
|
|
||||||
col++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkMine(field, i, j) {
|
|
||||||
return typeof field[i] !== 'undefined' && typeof field[i][j] !== 'undefined' && field[i][j] === 'm';
|
|
||||||
}
|
|
||||||
|
|
||||||
isThereMine(obj, row, col) {
|
|
||||||
if (this.checkMine(obj, row, col)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
numberingGrid(obj) {
|
|
||||||
var nbr = 0;
|
|
||||||
|
|
||||||
for (var i = 0; i < this.state.row; i++) {
|
|
||||||
for (var j = 0; j < this.state.col; j++) {
|
|
||||||
if (obj[i][j] === 'w') {
|
|
||||||
nbr = 0;
|
|
||||||
|
|
||||||
nbr += this.isThereMine(obj, i - 1, j);
|
|
||||||
nbr += this.isThereMine(obj, i - 1, j - 1);
|
|
||||||
nbr += this.isThereMine(obj, i - 1, j + 1);
|
|
||||||
nbr += this.isThereMine(obj, i, j - 1);
|
|
||||||
nbr += this.isThereMine(obj, i, j + 1);
|
|
||||||
nbr += this.isThereMine(obj, i + 1, j);
|
|
||||||
nbr += this.isThereMine(obj, i + 1, j + 1);
|
|
||||||
nbr += this.isThereMine(obj, i + 1, j - 1);
|
|
||||||
|
|
||||||
obj[i][j] = nbr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Grid;
|
|
||||||
14
assets/js/mine-seeker/hooks/index.js
Normal file
14
assets/js/mine-seeker/hooks/index.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { default as useGameRefs } from './useGameRefs';
|
||||||
|
export { default as useGameState } from './useGameState';
|
||||||
|
export { default as useServerCommunication } from './useServerCommunication';
|
||||||
|
export { default as useStepTimer } from './useStepTimer';
|
||||||
|
|
||||||
35
assets/js/mine-seeker/hooks/useGameRefs.jsx
Normal file
35
assets/js/mine-seeker/hooks/useGameRefs.jsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useRef } from 'react';
|
||||||
|
import { PLAYER_DEF } from '@mine-utils';
|
||||||
|
|
||||||
|
const useGameRefs = () => {
|
||||||
|
const webPlayerRef = useRef(null);
|
||||||
|
const activePlayerRef = useRef(false);
|
||||||
|
const bombSelectedRef = useRef(false);
|
||||||
|
const connectionLostRef = useRef(false);
|
||||||
|
const redRef = useRef({ ...PLAYER_DEF });
|
||||||
|
const blueRef = useRef({ ...PLAYER_DEF });
|
||||||
|
const lastClickedRef = useRef({ red: null, blue: null });
|
||||||
|
const endRef = useRef(false);
|
||||||
|
|
||||||
|
return {
|
||||||
|
webPlayerRef,
|
||||||
|
activePlayerRef,
|
||||||
|
bombSelectedRef,
|
||||||
|
connectionLostRef,
|
||||||
|
redRef,
|
||||||
|
blueRef,
|
||||||
|
lastClickedRef,
|
||||||
|
endRef,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useGameRefs;
|
||||||
45
assets/js/mine-seeker/hooks/useGameState.jsx
Normal file
45
assets/js/mine-seeker/hooks/useGameState.jsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { initCells, PLAYER_DEF } from '@mine-utils';
|
||||||
|
|
||||||
|
const useGameState = () => {
|
||||||
|
const [webPlayer, setWebPlayer] = useState(null);
|
||||||
|
const [activePlayer, setActivePlayer] = useState(false);
|
||||||
|
const [overlay, setOverlay] = useState(true);
|
||||||
|
const [overlayTitle, setOverlayTitle] = useState('');
|
||||||
|
const [overlaySubTitle, setOverlaySubTitle] = useState('');
|
||||||
|
const [mines, setMines] = useState(51);
|
||||||
|
const [bombSelected, setBombSelected] = useState(false);
|
||||||
|
const [foundMines, setFoundMines] = useState(false);
|
||||||
|
const [red, setRed] = useState({ ...PLAYER_DEF });
|
||||||
|
const [blue, setBlue] = useState({ ...PLAYER_DEF });
|
||||||
|
const [cells, setCells] = useState(initCells);
|
||||||
|
const [gridReady, setGridReady] = useState(false);
|
||||||
|
const [connectionLost, setConnectionLost] = useState(false);
|
||||||
|
|
||||||
|
return {
|
||||||
|
webPlayer, setWebPlayer,
|
||||||
|
activePlayer, setActivePlayer,
|
||||||
|
overlay, setOverlay,
|
||||||
|
overlayTitle, setOverlayTitle,
|
||||||
|
overlaySubTitle, setOverlaySubTitle,
|
||||||
|
mines, setMines,
|
||||||
|
bombSelected, setBombSelected,
|
||||||
|
foundMines, setFoundMines,
|
||||||
|
red, setRed,
|
||||||
|
blue, setBlue,
|
||||||
|
cells, setCells,
|
||||||
|
gridReady, setGridReady,
|
||||||
|
connectionLost, setConnectionLost,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useGameState;
|
||||||
359
assets/js/mine-seeker/hooks/useServerCommunication.jsx
Normal file
359
assets/js/mine-seeker/hooks/useServerCommunication.jsx
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
|
import { useGame } from '@mine-contexts';
|
||||||
|
import { DESC } from '@mine-utils';
|
||||||
|
import useStepTimer from './useStepTimer';
|
||||||
|
import { WaitingOverlayContent } from '@mine-components';
|
||||||
|
|
||||||
|
import { ChallengeCountdown } from '@mine-components';
|
||||||
|
|
||||||
|
const useServerCommunication = (gameAssoc, gameInherited, isEnvDev) => {
|
||||||
|
const {
|
||||||
|
/** Async-safe refs */
|
||||||
|
webPlayerRef, activePlayerRef, bombSelectedRef, connectionLostRef, endRef,
|
||||||
|
/** State setters */
|
||||||
|
setGridReady, setGameUuid,
|
||||||
|
/** Sync helpers */
|
||||||
|
syncWebPlayer, syncActivePlayer, syncBombSelected, syncConnLost, syncRed, syncBlue,
|
||||||
|
/** Game logic */
|
||||||
|
showOverlay, hideOverlay,
|
||||||
|
applyRevealedCell, applyStep,
|
||||||
|
makeGameEndIfItEnds, resignProcess,
|
||||||
|
/** Current cells snapshot (for active-check in onClick) */
|
||||||
|
cells,
|
||||||
|
} = useGame();
|
||||||
|
|
||||||
|
const eventSourceRef = useRef(null);
|
||||||
|
const rpcUsersRef = useRef(null);
|
||||||
|
const stepCacheRef = useRef([]);
|
||||||
|
const { getStepElapsed, resetStepTimer, startNewTurn } = useStepTimer();
|
||||||
|
const isGameRunningRef = useRef(false);
|
||||||
|
const lastActivePlayerRef = useRef(null);
|
||||||
|
|
||||||
|
/** REST mutations / queries */
|
||||||
|
|
||||||
|
const connectQuery = useQuery({
|
||||||
|
queryKey: ['game-connect', gameAssoc],
|
||||||
|
queryFn: () => fetch('/api/game/connect/' + gameAssoc)
|
||||||
|
.then(r => r.text())
|
||||||
|
.then(b64 => JSON.parse(window.atob(b64))),
|
||||||
|
enabled: false,
|
||||||
|
retry: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const startMutation = useMutation({
|
||||||
|
mutationFn: () => fetch('/api/game/start', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ gameAssoc }),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const joinMutation = useMutation({
|
||||||
|
mutationFn: () => fetch('/api/game/join/' + gameAssoc, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}).catch(e => isEnvDev && console.error('Join error', e)),
|
||||||
|
});
|
||||||
|
|
||||||
|
const stepMutation = useMutation({
|
||||||
|
mutationFn: dataPack => fetch('/api/game/step/' + gameAssoc, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(dataPack),
|
||||||
|
}).then(r => r.json()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Game-start helpers (triggered by server events) */
|
||||||
|
|
||||||
|
const wInit = (revealedCells = []) => {
|
||||||
|
setGridReady(true);
|
||||||
|
showOverlay('Choose an opponent!', gameAssoc ? (
|
||||||
|
<WaitingOverlayContent
|
||||||
|
shareUrl={`${window.location.href}/${gameAssoc}`}
|
||||||
|
currentGameAssoc={gameAssoc}
|
||||||
|
/>
|
||||||
|
) : '');
|
||||||
|
setTimeout(() => revealedCells.forEach(cell => applyRevealedCell(cell, cell.player)), 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeGameStart = payload => {
|
||||||
|
syncActivePlayer(1);
|
||||||
|
syncRed(p => ({
|
||||||
|
...p,
|
||||||
|
name: payload.users.red || payload.users.redAnon || p.name,
|
||||||
|
registered: !!payload.users.red,
|
||||||
|
avatar: payload.users.redAvatar ?? null,
|
||||||
|
}));
|
||||||
|
syncBlue(p => ({
|
||||||
|
...p,
|
||||||
|
name: payload.users.blue || payload.users.blueAnon || p.name,
|
||||||
|
registered: !!payload.users.blue,
|
||||||
|
avatar: payload.users.blueAvatar ?? null,
|
||||||
|
desc: 'blue' === webPlayerRef.current ? DESC.you : DESC.buddy,
|
||||||
|
active: true,
|
||||||
|
}));
|
||||||
|
isGameRunningRef.current = true;
|
||||||
|
lastActivePlayerRef.current = 1; // Blue starts
|
||||||
|
startNewTurn();
|
||||||
|
resetStepTimer();
|
||||||
|
hideOverlay();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Mercure / SSE message handlers */
|
||||||
|
|
||||||
|
const wSubscribe = (payload, rpcUsers = null) => {
|
||||||
|
isEnvDev && console.info((payload.user ?? 'user') + ' subscribed');
|
||||||
|
const firstUser = !rpcUsers;
|
||||||
|
|
||||||
|
if (null === webPlayerRef.current) {
|
||||||
|
const isBlue = payload.user === payload.users.blue
|
||||||
|
|| (firstUser ? '' !== payload.users.blueAnon : '' === rpcUsers.blueAnon && '' === rpcUsers.blue);
|
||||||
|
syncWebPlayer(isBlue ? 'blue' : 'red');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
2 === payload.userCnt
|
||||||
|
&& (!connectionLostRef.current
|
||||||
|
|| (connectionLostRef.current && false === activePlayerRef.current && !endRef.current))
|
||||||
|
) {
|
||||||
|
makeGameStart(payload);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const wUnsubscribe = payload => {
|
||||||
|
isEnvDev && console.info(payload.msg);
|
||||||
|
showOverlay('The connection has been lost w/ your friend...', 'Please, restart the game!');
|
||||||
|
};
|
||||||
|
|
||||||
|
const wChallenge = payload => {
|
||||||
|
const { challengerName, challengerGameAssoc } = payload;
|
||||||
|
let declineTimeout = null;
|
||||||
|
|
||||||
|
const handleAccept = () => {
|
||||||
|
clearTimeout(declineTimeout);
|
||||||
|
fetch('/api/game/challenge/respond/' + challengerGameAssoc, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ accepted: true, targetGameAssoc: gameAssoc }),
|
||||||
|
}).then(() => {
|
||||||
|
showOverlay('Challenge accepted!', 'Waiting for the challenger to join...');
|
||||||
|
}).catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDecline = () => {
|
||||||
|
clearTimeout(declineTimeout);
|
||||||
|
fetch('/api/game/challenge/respond/' + challengerGameAssoc, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ accepted: false, targetGameAssoc: gameAssoc }),
|
||||||
|
}).then(() => {
|
||||||
|
showOverlay('We are waiting for your opponent...', gameAssoc ? (
|
||||||
|
<WaitingOverlayContent
|
||||||
|
shareUrl={window.location.origin + '/play/' + gameAssoc}
|
||||||
|
currentGameAssoc={gameAssoc}
|
||||||
|
/>
|
||||||
|
) : '');
|
||||||
|
}).catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
declineTimeout = setTimeout(handleDecline, 30000);
|
||||||
|
|
||||||
|
showOverlay(
|
||||||
|
challengerName + ' wants to challenge you!',
|
||||||
|
<ChallengeCountdown onAccept={handleAccept} onDecline={handleDecline} />,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const wChallengeResponse = payload => {
|
||||||
|
if (payload.accepted) {
|
||||||
|
window.location.href = '/play/' + payload.targetGameAssoc;
|
||||||
|
} else {
|
||||||
|
window.dispatchEvent(new CustomEvent('challenge-declined'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const wTopic = payload => {
|
||||||
|
if (webPlayerRef.current !== payload.data.player) {
|
||||||
|
if (null === payload.data.resign) {
|
||||||
|
isEnvDev && console.warn(payload.user + ' stepped to ' + payload.data.coords.join(','));
|
||||||
|
syncBombSelected(payload.data.bomb);
|
||||||
|
|
||||||
|
// Detect if turn switched (other player made a move)
|
||||||
|
// After their move, it's now our turn (or the opposite player's turn)
|
||||||
|
if (lastActivePlayerRef.current !== activePlayerRef.current) {
|
||||||
|
startNewTurn();
|
||||||
|
lastActivePlayerRef.current = activePlayerRef.current;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyStep(payload.data);
|
||||||
|
if (payload.data.uuid && !endRef.current) {
|
||||||
|
setGameUuid(payload.data.uuid);
|
||||||
|
}
|
||||||
|
makeGameEndIfItEnds(payload.data.bluePoints, payload.data.redPoints, false, payload.data.leftMines);
|
||||||
|
} else {
|
||||||
|
resignProcess(payload.data.resign, payload.data.uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMercureMessage = payload => {
|
||||||
|
if (undefined !== payload.type) {
|
||||||
|
if ('challenge' === payload.type) wChallenge(payload);
|
||||||
|
else if ('challenge-response' === payload.type) wChallengeResponse(payload);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (undefined !== payload.data) {
|
||||||
|
wTopic(payload);
|
||||||
|
} else if (undefined === payload.msg) {
|
||||||
|
wSubscribe(payload, rpcUsersRef.current);
|
||||||
|
} else {
|
||||||
|
wUnsubscribe(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (2 === payload.userCnt && connectionLostRef.current) {
|
||||||
|
isEnvDev && console.info('Reconnection');
|
||||||
|
stepCacheRef.current.forEach(item => stepMutation.mutate(item));
|
||||||
|
stepCacheRef.current = [];
|
||||||
|
syncConnLost(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openEventSource = () => {
|
||||||
|
const wrapper = document.getElementById('mine-wrapper');
|
||||||
|
const hubUrl = wrapper.dataset.mercureHubUrl;
|
||||||
|
const subscriberJwt = wrapper.dataset.mercureSubscriberJwt;
|
||||||
|
const url = new URL(hubUrl, window.location.origin);
|
||||||
|
|
||||||
|
url.searchParams.append('topic', 'mineseeker/channel/' + gameAssoc);
|
||||||
|
if (subscriberJwt) url.searchParams.append('authorization', subscriberJwt);
|
||||||
|
|
||||||
|
if (eventSourceRef.current) eventSourceRef.current.close();
|
||||||
|
|
||||||
|
const es = new EventSource(url.toString());
|
||||||
|
es.onmessage = e => handleMercureMessage(JSON.parse(e.data));
|
||||||
|
es.onopen = () => {
|
||||||
|
isEnvDev && console.info('SSE opened');
|
||||||
|
if (connectionLostRef.current) {
|
||||||
|
isEnvDev && console.info('SSE reconnected');
|
||||||
|
joinMutation.mutate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
es.onerror = () => {
|
||||||
|
isEnvDev && console.error('SSE error');
|
||||||
|
syncConnLost(true);
|
||||||
|
};
|
||||||
|
eventSourceRef.current = es;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Initialization */
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
if (connectionLostRef.current) {
|
||||||
|
openEventSource();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (gameInherited) {
|
||||||
|
const serverData = await connectQuery.refetch().then(r => {
|
||||||
|
if (r.error) throw r.error;
|
||||||
|
return r.data;
|
||||||
|
});
|
||||||
|
|
||||||
|
if ('undefined' === typeof serverData.users || null === serverData.users) {
|
||||||
|
showOverlay('This channel does not exists!', <a href="/play" target="_self">Restart game!</a>);
|
||||||
|
console.error('This channel does not exists!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcUsersRef.current = serverData.users;
|
||||||
|
openEventSource();
|
||||||
|
wInit(serverData.revealedCells || []);
|
||||||
|
} else {
|
||||||
|
await startMutation.mutateAsync();
|
||||||
|
openEventSource();
|
||||||
|
wInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
isEnvDev && console.info('Connection initialised — joining channel');
|
||||||
|
await joinMutation.mutateAsync();
|
||||||
|
} catch (e) {
|
||||||
|
isEnvDev && console.error('Connection error', e);
|
||||||
|
setTimeout(() => window.location.reload(), 500);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
window.addEventListener('pagehide', () => navigator.sendBeacon('/api/game/leave/' + gameAssoc));
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/** UI-facing callbacks */
|
||||||
|
|
||||||
|
const onClick = async coords => {
|
||||||
|
const activeColor = activePlayerRef.current ? 'blue' : 'red';
|
||||||
|
if (activeColor !== webPlayerRef.current) return;
|
||||||
|
|
||||||
|
const [r, c] = coords;
|
||||||
|
if (cells[r]?.[c]?.active) return;
|
||||||
|
|
||||||
|
const stepElapsed = getStepElapsed(activePlayerRef.current, isGameRunningRef.current);
|
||||||
|
const dataPack = { coords, player: activeColor, bomb: bombSelectedRef.current, resign: null, stepElapsed };
|
||||||
|
|
||||||
|
if (connectionLostRef.current) {
|
||||||
|
stepCacheRef.current.push(dataPack);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await stepMutation.mutateAsync(dataPack);
|
||||||
|
applyStep(result);
|
||||||
|
if (result.uuid && !endRef.current) {
|
||||||
|
setGameUuid(result.uuid);
|
||||||
|
}
|
||||||
|
makeGameEndIfItEnds(result.bluePoints, result.redPoints, false, result.leftMines);
|
||||||
|
} catch (e) {
|
||||||
|
isEnvDev && console.error('Step error', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickResign = () => {
|
||||||
|
const color = activePlayerRef.current ? 'blue' : 'red';
|
||||||
|
const stepElapsed = getStepElapsed(activePlayerRef.current, isGameRunningRef.current);
|
||||||
|
stepMutation.mutate(
|
||||||
|
{ resign: color, stepElapsed },
|
||||||
|
{
|
||||||
|
onSuccess: result => {
|
||||||
|
if (result?.uuid && !endRef.current) {
|
||||||
|
resignProcess(webPlayerRef.current, result.uuid);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resign = () => {
|
||||||
|
const activeColor = activePlayerRef.current ? 'blue' : 'red';
|
||||||
|
if (webPlayerRef.current !== activeColor) return;
|
||||||
|
showOverlay('Are u sure u want to resign?!', (
|
||||||
|
<div className="resign">
|
||||||
|
<a onClick={clickResign}>Yes</a>
|
||||||
|
<a onClick={hideOverlay}>No!</a>
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
return { onClick, resign };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useServerCommunication;
|
||||||
53
assets/js/mine-seeker/hooks/useStepTimer.jsx
Normal file
53
assets/js/mine-seeker/hooks/useStepTimer.jsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useRef } from 'react';
|
||||||
|
|
||||||
|
const useStepTimer = () => {
|
||||||
|
// Record when the current turn started (timestamp)
|
||||||
|
const turnStartTimeRef = useRef(null);
|
||||||
|
// Flag to track if we've already recorded a turn start
|
||||||
|
const turnStartedRef = useRef(false);
|
||||||
|
|
||||||
|
const getStepElapsed = (currentActivePlayer, isGameRunning) => {
|
||||||
|
// If game not running, return 0
|
||||||
|
if (!isGameRunning) return 0;
|
||||||
|
|
||||||
|
// Only initialize the turn timer ONCE per call to getStepElapsed
|
||||||
|
// This prevents resetting on multiple calls
|
||||||
|
if (!turnStartedRef.current) {
|
||||||
|
turnStartTimeRef.current = Date.now();
|
||||||
|
turnStartedRef.current = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After initialization, just calculate elapsed time
|
||||||
|
if (turnStartTimeRef.current) {
|
||||||
|
return Math.floor((Date.now() - turnStartTimeRef.current) / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetStepTimer = () => {
|
||||||
|
turnStartTimeRef.current = null;
|
||||||
|
turnStartedRef.current = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call this when we know a turn has actually changed (from server response)
|
||||||
|
const startNewTurn = () => {
|
||||||
|
turnStartTimeRef.current = Date.now();
|
||||||
|
turnStartedRef.current = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { getStepElapsed, resetStepTimer, startNewTurn };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useStepTimer;
|
||||||
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import User from './user';
|
|
||||||
|
|
||||||
class UserControl extends React.Component {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* activePlayer - red: 0, blue: 1
|
|
||||||
* @type {{activePlayer: boolean, mines: number, bombSelected: boolean, foundMines: boolean}}
|
|
||||||
*/
|
|
||||||
this.state = {
|
|
||||||
activePlayer: false,
|
|
||||||
mines: 51,
|
|
||||||
bombSelected: false,
|
|
||||||
foundMines: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
youCanSelectBomb(activePlayer, clickedPlayer) {
|
|
||||||
return this.refs[activePlayer].state.haveBomb &&
|
|
||||||
this.refs[activePlayer].state.enabledBomb &&
|
|
||||||
this.state.activePlayer === clickedPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
onClickBombSelector(clickedPlayer) {
|
|
||||||
let activePlayer = this.state.activePlayer ? 'blue' : 'red';
|
|
||||||
|
|
||||||
if (this.youCanSelectBomb(activePlayer, clickedPlayer)) {
|
|
||||||
this.state.bombSelected = !this.state.bombSelected;
|
|
||||||
|
|
||||||
if (!this.state.bombSelected) {
|
|
||||||
this.props.bombClear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getResignClass(webPlayer) {
|
|
||||||
let activePlayer = this.state.activePlayer === 1 ? 'blue' : 'red';
|
|
||||||
return "resign" + (webPlayer !== activePlayer ? ' disabled' : '');
|
|
||||||
}
|
|
||||||
|
|
||||||
activeMines() {
|
|
||||||
return "active-mines" + (this.state.foundMines ? ' found-mine' : '');
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="users">
|
|
||||||
<User ref="blue"
|
|
||||||
color="blue"
|
|
||||||
webPlayer={this.props.webPlayer}
|
|
||||||
active={this.state.activePlayer === 1}
|
|
||||||
onClickBombSelector={this.onClickBombSelector.bind(this, 1)}/>
|
|
||||||
<div className="active-mines-container">
|
|
||||||
<i className="fa fa-star"></i>
|
|
||||||
<div className={this.activeMines()}>
|
|
||||||
<div className="active-mines-nbr">{this.state.mines}</div>
|
|
||||||
<div className="active-mines-shine"></div>
|
|
||||||
</div>
|
|
||||||
<i className="fa fa-star"></i>
|
|
||||||
</div>
|
|
||||||
<div className="clear"></div>
|
|
||||||
<User ref="red"
|
|
||||||
color="red"
|
|
||||||
webPlayer={this.props.webPlayer}
|
|
||||||
active={this.state.activePlayer === 0}
|
|
||||||
onClickBombSelector={this.onClickBombSelector.bind(this, 0)}/>
|
|
||||||
<button className={this.getResignClass(this.props.webPlayer)} onClick={this.props.resign}>
|
|
||||||
<div className="resign-shine"></div>
|
|
||||||
Resign
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default UserControl;
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
class User extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
name: "...",
|
|
||||||
desc: "",
|
|
||||||
active: props.active,
|
|
||||||
color: props.color === 'blue' ? 1 : 0,
|
|
||||||
mines: 0,
|
|
||||||
srcRoot: '/images/',
|
|
||||||
haveBomb: true,
|
|
||||||
enabledBomb: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
setColor(color) {
|
|
||||||
return 'user-container user-' + color;
|
|
||||||
}
|
|
||||||
|
|
||||||
getSrc(color) {
|
|
||||||
return this.state.srcRoot + 'bg-flag-' + color + '-outbg.png';
|
|
||||||
}
|
|
||||||
|
|
||||||
getBombBuzzClass(webPlayer) {
|
|
||||||
let activePlayer = this.state.color === 1 ? 'blue' : 'red';
|
|
||||||
|
|
||||||
return "bomb-container" +
|
|
||||||
(
|
|
||||||
this.state.active && (activePlayer === webPlayer) && this.state.haveBomb && this.state.enabledBomb
|
|
||||||
? ' buzz'
|
|
||||||
: ''
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getBomb() {
|
|
||||||
let src = this.state.srcRoot;
|
|
||||||
|
|
||||||
if (this.state.haveBomb) {
|
|
||||||
src += this.state.enabledBomb && this.state.active
|
|
||||||
? 'bg-bomb-outbg.png'
|
|
||||||
: 'bg-bomb-disabled-outbg.png';
|
|
||||||
} else {
|
|
||||||
src += 'bg-bomb-exploded-outbg.png';
|
|
||||||
}
|
|
||||||
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
|
|
||||||
getFigure(color) {
|
|
||||||
return this.state.srcRoot + 'bg-figure-' + color + '-outbg.png';
|
|
||||||
}
|
|
||||||
|
|
||||||
getCursor(state, color) {
|
|
||||||
return state
|
|
||||||
? <img src={this.state.srcRoot + 'bg-cursor-' + color + '-outbg.png'} alt="cursor" className="user-cursor"/>
|
|
||||||
: '';
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className={this.setColor(this.props.color)}>
|
|
||||||
<div className="user-header">
|
|
||||||
<div className="user-color">{this.props.color}</div>
|
|
||||||
{this.getCursor(this.props.active, this.props.color)}
|
|
||||||
<img src={this.getFigure(this.props.color)} alt="figure"/>
|
|
||||||
</div>
|
|
||||||
<div className="user-name"> {this.state.name} </div>
|
|
||||||
<div className="user-caret"><i className="fa fa-caret-down"></i></div>
|
|
||||||
<div className="user-desc"> {this.state.desc} </div>
|
|
||||||
<div className="user-control">
|
|
||||||
<img src={this.getSrc(this.props.color)} alt="flag"/>
|
|
||||||
<div className="user-control-mines">
|
|
||||||
{this.state.mines}
|
|
||||||
</div>
|
|
||||||
<div className={this.getBombBuzzClass(this.props.webPlayer)} onClick={this.props.onClickBombSelector}>
|
|
||||||
<div className="bomb">
|
|
||||||
<img src={this.getBomb()} alt="bomb"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="clear"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default User;
|
|
||||||
102
assets/js/mine-seeker/utils/constants.jsx
Normal file
102
assets/js/mine-seeker/utils/constants.jsx
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const ROWS = 16;
|
||||||
|
export const COLS = 16;
|
||||||
|
export const IMG = '/images/';
|
||||||
|
|
||||||
|
export const WAVES = {
|
||||||
|
1: 'bg-wave-1-outbg.png',
|
||||||
|
2: 'bg-wave-1-outbg.png',
|
||||||
|
3: 'bg-wave-2-outbg.png',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IMAGES = {
|
||||||
|
target: `${IMG}bg-target-outbg.png`,
|
||||||
|
bomb: `${IMG}bg-bomb-outbg.png`,
|
||||||
|
bombDisabled: `${IMG}bg-bomb-disabled-outbg.png`,
|
||||||
|
bombExploded: `${IMG}bg-bomb-exploded-outbg.png`,
|
||||||
|
bombEmpty: `${IMG}bg-bomb-empty-outbg.png`,
|
||||||
|
leftMine: `${IMG}bg-left-mine-outbg.png`,
|
||||||
|
cursor: color => `${IMG}bg-cursor-${color}-outbg.png`,
|
||||||
|
figure: color => `${IMG}bg-figure-${color}-outbg.png`,
|
||||||
|
flag: player => `${IMG}bg-flag-${player}-outbg.png`,
|
||||||
|
last: color => `${IMG}bg-last-${color}-outbg.png`,
|
||||||
|
wave: n => `${IMG}${WAVES[n]}`,
|
||||||
|
bombPos: (hor, vert) => `${IMG}bg-bomb-${hor}-${vert}-outbg.png`,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BONUS_STATS_DEF = {
|
||||||
|
blindHits: 0, chainBest: 0, chainCurrent: 0, lastMineHits: 0, edgeMines: 0, biggestReveal: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BONUS_LABELS = {
|
||||||
|
blindHits: { label: 'Blind hits', desc: 'Mines clicked with no revealed number nearby' },
|
||||||
|
chainBest: { label: 'Best chain', desc: 'Longest streak of consecutive mine-clicks' },
|
||||||
|
chainCurrent: { label: 'Current chain', desc: 'Active consecutive mine-click streak' },
|
||||||
|
lastMineHits: { label: 'Endgame mines', desc: 'Mines clicked while few remain on the board' },
|
||||||
|
edgeMines: { label: 'Edge mines', desc: 'Mines clicked on the board boundary' },
|
||||||
|
biggestReveal: { label: 'Biggest reveal', desc: 'Largest number of safe cells revealed in one click' },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PLAYER_DEF = {
|
||||||
|
name: '...', desc: '', active: false, mines: 0, haveBomb: true, enabledBomb: true,
|
||||||
|
registered: false, avatar: null,
|
||||||
|
bonusPoints: 0, bonusStats: { ...BONUS_STATS_DEF },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DESC = {
|
||||||
|
buddy: <div>Your buddy is <br />making a <br />move.</div>,
|
||||||
|
you: <div>It is your turn! <br />Make a move.</div>,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BOMB_SYMBOLS = [
|
||||||
|
[null, null], [0, 0], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2], [2, 2],
|
||||||
|
[null, null], [null, null], [null, null], [null, null], [null, null], [null, null], [null, null],
|
||||||
|
[null, null], [null, null], [null, null], [null, null], [null, null], [null, null], [null, null],
|
||||||
|
[null, null], [null, null],
|
||||||
|
];
|
||||||
|
|
||||||
|
export const bombRadius = (row, col, rows, cols) => {
|
||||||
|
const centre = 1 < row && row < rows - 2 && 1 < col && col < cols - 2;
|
||||||
|
if (!centre) {
|
||||||
|
col = Math.max(2, Math.min(col, cols - 3));
|
||||||
|
row = Math.max(2, Math.min(row, rows - 3));
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
[row, col], [row - 2, col - 2], [row - 2, col], [row - 2, col + 2],
|
||||||
|
[row, col - 2], [row, col + 2], [row + 2, col - 2], [row + 2, col],
|
||||||
|
[row + 2, col + 2], [row - 2, col + 1], [row - 2, col - 1],
|
||||||
|
[row - 1, col - 2], [row - 1, col - 1], [row - 1, col], [row - 1, col + 1], [row - 1, col + 2],
|
||||||
|
[row, col - 1], [row, col + 1], [row + 1, col - 2], [row + 1, col - 1], [row + 1, col],
|
||||||
|
[row + 1, col + 1], [row + 1, col + 2], [row + 2, col - 1], [row + 2, col + 1],
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const patchCells = (prev, patches) => {
|
||||||
|
const next = prev.map(r => [...r]);
|
||||||
|
for (const { row, col, ...rest } of patches) {
|
||||||
|
next[row][col] = { ...next[row][col], ...rest };
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initCells = () =>
|
||||||
|
Array.from({ length: ROWS }, () =>
|
||||||
|
Array.from({ length: COLS }, () => ({
|
||||||
|
currentImage: IMAGES.wave(Math.floor(Math.random() * 3) + 1),
|
||||||
|
currentObj: 'w',
|
||||||
|
active: false,
|
||||||
|
lastClickedRed: false,
|
||||||
|
lastClickedBlue: false,
|
||||||
|
bombTargetArea: null,
|
||||||
|
})),
|
||||||
|
);
|
||||||
10
assets/js/mine-seeker/utils/index.js
Normal file
10
assets/js/mine-seeker/utils/index.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { DESC, IMAGES, BOMB_SYMBOLS, PLAYER_DEF, BONUS_STATS_DEF, BONUS_LABELS, bombRadius, initCells, patchCells } from './constants';
|
||||||
37
assets/js/passkey.jsx
Normal file
37
assets/js/passkey.jsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import PasskeyManager from './components/PasskeyManager';
|
||||||
|
import PasskeyLogin from './components/PasskeyLogin';
|
||||||
|
|
||||||
|
const passkeyManagerRoot = document.getElementById('passkey-manager-root');
|
||||||
|
|
||||||
|
if (passkeyManagerRoot) {
|
||||||
|
const credentials = JSON.parse(passkeyManagerRoot.dataset.credentials || '[]');
|
||||||
|
const apiRoutes = JSON.parse(passkeyManagerRoot.dataset.apiRoutes || '{}');
|
||||||
|
|
||||||
|
createRoot(passkeyManagerRoot).render(
|
||||||
|
<PasskeyManager
|
||||||
|
credentials={credentials}
|
||||||
|
apiRoutes={apiRoutes}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const passkeyLoginRoot = document.getElementById('passkey-login-root');
|
||||||
|
|
||||||
|
if (passkeyLoginRoot) {
|
||||||
|
const apiRoutes = JSON.parse(passkeyLoginRoot.dataset.apiRoutes || '{}');
|
||||||
|
|
||||||
|
createRoot(passkeyLoginRoot).render(
|
||||||
|
<PasskeyLogin apiRoutes={apiRoutes} />,
|
||||||
|
);
|
||||||
|
}
|
||||||
31
assets/js/profile.jsx
Normal file
31
assets/js/profile.jsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import ProfileCharts from './components/ProfileCharts';
|
||||||
|
import BattleDialog from './components/BattleDialog';
|
||||||
|
import AvatarUpload from './components/AvatarUpload';
|
||||||
|
|
||||||
|
const avatarRoot = document.getElementById('profile-avatar-root');
|
||||||
|
if (avatarRoot) {
|
||||||
|
const { uploadUrl, thumbUrl, initials } = avatarRoot.dataset;
|
||||||
|
createRoot(avatarRoot).render(
|
||||||
|
<AvatarUpload
|
||||||
|
uploadUrl={uploadUrl}
|
||||||
|
initialThumbUrl={thumbUrl || null}
|
||||||
|
initials={initials}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const chartsRoot = document.getElementById('profile-charts-root');
|
||||||
|
if (chartsRoot) {
|
||||||
|
createRoot(chartsRoot).render(
|
||||||
|
<ProfileCharts chartData={JSON.parse(chartsRoot.dataset.chartData)} />,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const battleRoot = document.getElementById('profile-battle-root');
|
||||||
|
if (battleRoot) {
|
||||||
|
createRoot(battleRoot).render(
|
||||||
|
<BattleDialog games={JSON.parse(battleRoot.dataset.games)} />,
|
||||||
|
);
|
||||||
|
}
|
||||||
42
assets/js/src/build/react-dom-server.js
vendored
42
assets/js/src/build/react-dom-server.js
vendored
@@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* ReactDOMServer v15.3.2
|
|
||||||
*
|
|
||||||
* Copyright 2013-present, Facebook, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the BSD-style license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree. An additional grant
|
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
// Based off https://github.com/ForbesLindesay/umd/blob/master/template.js
|
|
||||||
;(function(f) {
|
|
||||||
// CommonJS
|
|
||||||
if (typeof exports === "object" && typeof module !== "undefined") {
|
|
||||||
module.exports = f(require('react'));
|
|
||||||
|
|
||||||
// RequireJS
|
|
||||||
} else if (typeof define === "function" && define.amd) {
|
|
||||||
define(['react'], f);
|
|
||||||
|
|
||||||
// <script>
|
|
||||||
} else {
|
|
||||||
var g;
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
g = window;
|
|
||||||
} else if (typeof global !== "undefined") {
|
|
||||||
g = global;
|
|
||||||
} else if (typeof self !== "undefined") {
|
|
||||||
g = self;
|
|
||||||
} else {
|
|
||||||
// works providing we're not in "use strict";
|
|
||||||
// needed for Java 8 Nashorn
|
|
||||||
// see https://github.com/facebook/react/issues/3037
|
|
||||||
g = this;
|
|
||||||
}
|
|
||||||
g.ReactDOMServer = f(g.React);
|
|
||||||
}
|
|
||||||
|
|
||||||
})(function(React) {
|
|
||||||
return React.__SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
||||||
});
|
|
||||||
12
assets/js/src/build/react-dom-server.min.js
vendored
12
assets/js/src/build/react-dom-server.min.js
vendored
@@ -1,12 +0,0 @@
|
|||||||
/**
|
|
||||||
* ReactDOMServer v15.3.2
|
|
||||||
*
|
|
||||||
* Copyright 2013-present, Facebook, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the BSD-style license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree. An additional grant
|
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e(require("react"));else if("function"==typeof define&&define.amd)define(["react"],e);else{var f;f="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,f.ReactDOMServer=e(f.React)}}(function(e){return e.__SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED});
|
|
||||||
42
assets/js/src/build/react-dom.js
vendored
42
assets/js/src/build/react-dom.js
vendored
@@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* ReactDOM v15.3.2
|
|
||||||
*
|
|
||||||
* Copyright 2013-present, Facebook, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the BSD-style license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree. An additional grant
|
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
// Based off https://github.com/ForbesLindesay/umd/blob/master/template.js
|
|
||||||
;(function(f) {
|
|
||||||
// CommonJS
|
|
||||||
if (typeof exports === "object" && typeof module !== "undefined") {
|
|
||||||
module.exports = f(require('react'));
|
|
||||||
|
|
||||||
// RequireJS
|
|
||||||
} else if (typeof define === "function" && define.amd) {
|
|
||||||
define(['react'], f);
|
|
||||||
|
|
||||||
// <script>
|
|
||||||
} else {
|
|
||||||
var g;
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
g = window;
|
|
||||||
} else if (typeof global !== "undefined") {
|
|
||||||
g = global;
|
|
||||||
} else if (typeof self !== "undefined") {
|
|
||||||
g = self;
|
|
||||||
} else {
|
|
||||||
// works providing we're not in "use strict";
|
|
||||||
// needed for Java 8 Nashorn
|
|
||||||
// see https://github.com/facebook/react/issues/3037
|
|
||||||
g = this;
|
|
||||||
}
|
|
||||||
g.ReactDOM = f(g.React);
|
|
||||||
}
|
|
||||||
|
|
||||||
})(function(React) {
|
|
||||||
return React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
||||||
});
|
|
||||||
12
assets/js/src/build/react-dom.min.js
vendored
12
assets/js/src/build/react-dom.min.js
vendored
@@ -1,12 +0,0 @@
|
|||||||
/**
|
|
||||||
* ReactDOM v15.3.2
|
|
||||||
*
|
|
||||||
* Copyright 2013-present, Facebook, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the BSD-style license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree. An additional grant
|
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e(require("react"));else if("function"==typeof define&&define.amd)define(["react"],e);else{var f;f="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,f.ReactDOM=e(f.React)}}(function(e){return e.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED});
|
|
||||||
22926
assets/js/src/build/react-with-addons.js
vendored
22926
assets/js/src/build/react-with-addons.js
vendored
File diff suppressed because it is too large
Load Diff
16
assets/js/src/build/react-with-addons.min.js
vendored
16
assets/js/src/build/react-with-addons.min.js
vendored
File diff suppressed because one or more lines are too long
20595
assets/js/src/build/react.js
vendored
20595
assets/js/src/build/react.js
vendored
File diff suppressed because it is too large
Load Diff
16
assets/js/src/build/react.min.js
vendored
16
assets/js/src/build/react.min.js
vendored
File diff suppressed because one or more lines are too long
31
bin/generate-mercure-jwt.php
Executable file
31
bin/generate-mercure-jwt.php
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
|
||||||
|
$secret = bin2hex(random_bytes(32));
|
||||||
|
|
||||||
|
$publisherToken = JWT::encode(
|
||||||
|
['mercure' => ['publish' => ['*']]],
|
||||||
|
$secret,
|
||||||
|
'HS256'
|
||||||
|
);
|
||||||
|
|
||||||
|
$subscriberToken = JWT::encode(
|
||||||
|
['mercure' => ['subscribe' => ['*']]],
|
||||||
|
$secret,
|
||||||
|
'HS256'
|
||||||
|
);
|
||||||
|
|
||||||
|
echo PHP_EOL;
|
||||||
|
echo "# ── .env ──────────────────────────────────────────────────────────────" . PHP_EOL;
|
||||||
|
echo "MERCURE_JWT_SECRET=\"{$secret}\"" . PHP_EOL;
|
||||||
|
echo "MERCURE_JWT_TOKEN={$publisherToken}" . PHP_EOL;
|
||||||
|
echo "MERCURE_SUBSCRIBER_JWT={$subscriberToken}" . PHP_EOL;
|
||||||
|
echo PHP_EOL;
|
||||||
|
echo "# ── /etc/caddy/conf.d/mine.caddy (inside the mercure {} block) ───────" . PHP_EOL;
|
||||||
|
echo "publisher_jwt {$secret} HS256" . PHP_EOL;
|
||||||
|
echo "subscriber_jwt {$secret} HS256" . PHP_EOL;
|
||||||
|
echo PHP_EOL;
|
||||||
887
bun.lock
Normal file
887
bun.lock
Normal file
@@ -0,0 +1,887 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 0,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "mine-seeker",
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.14.0",
|
||||||
|
"@emotion/styled": "^11.14.1",
|
||||||
|
"@fontsource/changa-one": "^5.2.8",
|
||||||
|
"@fontsource/open-sans": "^5.2.7",
|
||||||
|
"@fontsource/rajdhani": "^5.2.7",
|
||||||
|
"@fortawesome/fontawesome-free": "^7.2.0",
|
||||||
|
"@mui/material": "^9.0.0",
|
||||||
|
"@mui/x-charts": "^9.0.1",
|
||||||
|
"@tanstack/react-query": "^5.0.0",
|
||||||
|
"howler": "^2.1.2",
|
||||||
|
"lodash": "^4.18.1",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/eslintrc": "^3.3.5",
|
||||||
|
"@eslint/js": "^9.0.0",
|
||||||
|
"@stylistic/eslint-plugin": "^4.0.0",
|
||||||
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
|
"eslint": "^9.0.0",
|
||||||
|
"eslint-plugin-react": "^7.0.0",
|
||||||
|
"eslint-plugin-react-hooks": "^5.0.0",
|
||||||
|
"globals": "^15.0.0",
|
||||||
|
"sass": "^1.77.0",
|
||||||
|
"vite": "^8.0.8",
|
||||||
|
"vite-plugin-symfony": "^8.2.4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@babel/code-frame": ["@babel/code-frame@7.29.0", "http://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
|
||||||
|
|
||||||
|
"@babel/generator": ["@babel/generator@7.29.1", "http://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="],
|
||||||
|
|
||||||
|
"@babel/helper-globals": ["@babel/helper-globals@7.28.0", "http://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
|
||||||
|
|
||||||
|
"@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "http://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="],
|
||||||
|
|
||||||
|
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "http://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||||
|
|
||||||
|
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "http://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
|
||||||
|
|
||||||
|
"@babel/parser": ["@babel/parser@7.29.2", "http://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="],
|
||||||
|
|
||||||
|
"@babel/runtime": ["@babel/runtime@7.29.2", "http://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="],
|
||||||
|
|
||||||
|
"@babel/template": ["@babel/template@7.28.6", "http://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
|
||||||
|
|
||||||
|
"@babel/traverse": ["@babel/traverse@7.29.0", "http://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="],
|
||||||
|
|
||||||
|
"@babel/types": ["@babel/types@7.29.0", "http://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
||||||
|
|
||||||
|
"@emnapi/core": ["@emnapi/core@1.9.2", "http://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
|
||||||
|
|
||||||
|
"@emnapi/runtime": ["@emnapi/runtime@1.9.2", "http://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
|
||||||
|
|
||||||
|
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "http://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
|
||||||
|
|
||||||
|
"@emotion/babel-plugin": ["@emotion/babel-plugin@11.13.5", "http://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", { "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/serialize": "^1.3.3", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", "find-root": "^1.1.0", "source-map": "^0.5.7", "stylis": "4.2.0" } }, "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ=="],
|
||||||
|
|
||||||
|
"@emotion/cache": ["@emotion/cache@11.14.0", "http://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", { "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA=="],
|
||||||
|
|
||||||
|
"@emotion/hash": ["@emotion/hash@0.9.2", "http://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", {}, "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="],
|
||||||
|
|
||||||
|
"@emotion/is-prop-valid": ["@emotion/is-prop-valid@1.4.0", "http://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", { "dependencies": { "@emotion/memoize": "^0.9.0" } }, "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw=="],
|
||||||
|
|
||||||
|
"@emotion/memoize": ["@emotion/memoize@0.9.0", "http://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", {}, "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="],
|
||||||
|
|
||||||
|
"@emotion/react": ["@emotion/react@11.14.0", "http://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", { "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA=="],
|
||||||
|
|
||||||
|
"@emotion/serialize": ["@emotion/serialize@1.3.3", "http://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", { "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/unitless": "^0.10.0", "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA=="],
|
||||||
|
|
||||||
|
"@emotion/sheet": ["@emotion/sheet@1.4.0", "http://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", {}, "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="],
|
||||||
|
|
||||||
|
"@emotion/styled": ["@emotion/styled@11.14.1", "http://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", { "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/is-prop-valid": "^1.3.0", "@emotion/serialize": "^1.3.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2" }, "peerDependencies": { "@emotion/react": "^11.0.0-rc.0", "react": ">=16.8.0" } }, "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw=="],
|
||||||
|
|
||||||
|
"@emotion/unitless": ["@emotion/unitless@0.10.0", "http://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", {}, "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="],
|
||||||
|
|
||||||
|
"@emotion/use-insertion-effect-with-fallbacks": ["@emotion/use-insertion-effect-with-fallbacks@1.2.0", "http://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", { "peerDependencies": { "react": ">=16.8.0" } }, "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg=="],
|
||||||
|
|
||||||
|
"@emotion/utils": ["@emotion/utils@1.4.2", "http://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", {}, "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="],
|
||||||
|
|
||||||
|
"@emotion/weak-memoize": ["@emotion/weak-memoize@0.4.0", "http://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", {}, "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="],
|
||||||
|
|
||||||
|
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "http://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="],
|
||||||
|
|
||||||
|
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "http://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
|
||||||
|
|
||||||
|
"@eslint/config-array": ["@eslint/config-array@0.21.2", "http://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" } }, "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw=="],
|
||||||
|
|
||||||
|
"@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "http://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="],
|
||||||
|
|
||||||
|
"@eslint/core": ["@eslint/core@0.17.0", "http://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="],
|
||||||
|
|
||||||
|
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.5", "http://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", { "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" } }, "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg=="],
|
||||||
|
|
||||||
|
"@eslint/js": ["@eslint/js@9.39.4", "http://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", {}, "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw=="],
|
||||||
|
|
||||||
|
"@eslint/object-schema": ["@eslint/object-schema@2.1.7", "http://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="],
|
||||||
|
|
||||||
|
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "http://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="],
|
||||||
|
|
||||||
|
"@fontsource/changa-one": ["@fontsource/changa-one@5.2.8", "http://registry.npmjs.org/@fontsource/changa-one/-/changa-one-5.2.8.tgz", {}, "sha512-gagiU8sMWLs9ejh41NmrYlGdgasSYWFegz5/+22WqYdJVS9HZbaUEXj/6jj3ZKgi+dQZT0kYI+Nha2UQGbO/mA=="],
|
||||||
|
|
||||||
|
"@fontsource/open-sans": ["@fontsource/open-sans@5.2.7", "", {}, "sha512-8yfgDYjE5O0vmTPdrcjV35y4yMnctsokmi9gN49Gcsr0sjzkMkR97AnKDe6OqZh2SFkYlR28fxOvi21bYEgMSw=="],
|
||||||
|
|
||||||
|
"@fontsource/rajdhani": ["@fontsource/rajdhani@5.2.7", "http://registry.npmjs.org/@fontsource/rajdhani/-/rajdhani-5.2.7.tgz", {}, "sha512-7Gy10U688fCdeFfYKebUF2TZotdgH/ghKyMsseXPmB60lpaUHC8aoCSJl5/OpZ+KHKSU2TqBfKfteVkcIXxTAQ=="],
|
||||||
|
|
||||||
|
"@fortawesome/fontawesome-free": ["@fortawesome/fontawesome-free@7.2.0", "http://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.2.0.tgz", {}, "sha512-3DguDv/oUE+7vjMeTSOjCSG+KeawgVQOHrKRnvUuqYh1mfArrh7s+s8hXW3e4RerBA1+Wh+hBqf8sJNpqNrBWg=="],
|
||||||
|
|
||||||
|
"@humanfs/core": ["@humanfs/core@0.19.1", "http://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
||||||
|
|
||||||
|
"@humanfs/node": ["@humanfs/node@0.16.7", "http://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
|
||||||
|
|
||||||
|
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "http://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
|
||||||
|
|
||||||
|
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "http://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
||||||
|
|
||||||
|
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "http://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||||
|
|
||||||
|
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "http://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "http://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "http://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||||
|
|
||||||
|
"@mui/core-downloads-tracker": ["@mui/core-downloads-tracker@9.0.0", "http://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-9.0.0.tgz", {}, "sha512-uwQNGkhv0lf7ufxw6QXev77BW6pWbW+7uxYjU5+rfp4lBkFtMEgJCsarTM3Tn+i0lGx6+Ol2u88JdGXr0GDskA=="],
|
||||||
|
|
||||||
|
"@mui/material": ["@mui/material@9.0.0", "http://registry.npmjs.org/@mui/material/-/material-9.0.0.tgz", { "dependencies": { "@babel/runtime": "^7.29.2", "@mui/core-downloads-tracker": "^9.0.0", "@mui/system": "^9.0.0", "@mui/types": "^9.0.0", "@mui/utils": "^9.0.0", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", "csstype": "^3.2.3", "prop-types": "^15.8.1", "react-is": "^19.2.4", "react-transition-group": "^4.4.5" }, "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@mui/material-pigment-css": "^9.0.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/react", "@emotion/styled", "@mui/material-pigment-css", "@types/react"] }, "sha512-+VP/oQCDhDR87NQQgXnNBG8dwy6GNuQLnenS1pZvkbn2dKFSxRSRMybTpH9xUxXP+316mlYDy5CSbYtusnCWtw=="],
|
||||||
|
|
||||||
|
"@mui/private-theming": ["@mui/private-theming@9.0.0", "http://registry.npmjs.org/@mui/private-theming/-/private-theming-9.0.0.tgz", { "dependencies": { "@babel/runtime": "^7.29.2", "@mui/utils": "^9.0.0", "prop-types": "^15.8.1" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-JtuZoaiCqwD6vjgYu6Xp3T7DZkrxJlgtDz5yESzhI34fEX5hHMh2VJUbuL9UOg8xrfIFMrq6dcYoH/7Zi4G0RA=="],
|
||||||
|
|
||||||
|
"@mui/styled-engine": ["@mui/styled-engine@9.0.0", "http://registry.npmjs.org/@mui/styled-engine/-/styled-engine-9.0.0.tgz", { "dependencies": { "@babel/runtime": "^7.29.2", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", "csstype": "^3.2.3", "prop-types": "^15.8.1" }, "peerDependencies": { "@emotion/react": "^11.4.1", "@emotion/styled": "^11.3.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/react", "@emotion/styled"] }, "sha512-9RLGdX4Jg0aQPRuvqh/OLzYSPlgd5zyEw5/1HIRfdavSiOd03WtUaGZH9/w1RoTYuRKwpgy0hpIFaMHIqPVIWg=="],
|
||||||
|
|
||||||
|
"@mui/system": ["@mui/system@9.0.0", "http://registry.npmjs.org/@mui/system/-/system-9.0.0.tgz", { "dependencies": { "@babel/runtime": "^7.29.2", "@mui/private-theming": "^9.0.0", "@mui/styled-engine": "^9.0.0", "@mui/types": "^9.0.0", "@mui/utils": "^9.0.0", "clsx": "^2.1.1", "csstype": "^3.2.3", "prop-types": "^15.8.1" }, "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/react", "@emotion/styled", "@types/react"] }, "sha512-YnC5Zg6j04IxiLc/boAKs0464jfZlLFVa7mf5E8lF0XOtZVUvG6R6gJK50lgUYdaaLdyLfxF6xR7LaPuEpeT/g=="],
|
||||||
|
|
||||||
|
"@mui/types": ["@mui/types@9.0.0", "http://registry.npmjs.org/@mui/types/-/types-9.0.0.tgz", { "dependencies": { "@babel/runtime": "^7.29.2" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-i1cuFCAWN44b3AJWO7mh7tuh1sqbQSeVr/94oG0TX5uXivac8XalgE4/6fQZcmGZigzbQ35IXxj/4jLpRIBYZg=="],
|
||||||
|
|
||||||
|
"@mui/utils": ["@mui/utils@9.0.0", "http://registry.npmjs.org/@mui/utils/-/utils-9.0.0.tgz", { "dependencies": { "@babel/runtime": "^7.29.2", "@mui/types": "^9.0.0", "@types/prop-types": "^15.7.15", "clsx": "^2.1.1", "prop-types": "^15.8.1", "react-is": "^19.2.4" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-bQcqyg/gjULUqTuyUjSAFr6LQGLvtkNtDbJerAtoUn9kGZ0hg5QJiN1PLHMLbeFpe3te1831uq7GFl2ITokGdg=="],
|
||||||
|
|
||||||
|
"@mui/x-charts": ["@mui/x-charts@9.0.1", "http://registry.npmjs.org/@mui/x-charts/-/x-charts-9.0.1.tgz", { "dependencies": { "@babel/runtime": "^7.28.6", "@mui/utils": "9.0.0", "@mui/x-charts-vendor": "^9.0.0", "@mui/x-internal-gestures": "^9.0.0", "@mui/x-internals": "^9.0.0", "bezier-easing": "^2.1.0", "clsx": "^2.1.1", "prop-types": "^15.8.1", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@emotion/react": "^11.9.0", "@emotion/styled": "^11.8.1", "@mui/material": "^7.3.0 || ^9.0.0", "@mui/system": "^7.3.0 || ^9.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/react", "@emotion/styled"] }, "sha512-0LyhlGhUm07wGJY0d0U+hSljGS1EHKWgPBsTJ/lBNGDrNc4DI9zSbp4h802LN/eLwMUVXJSI7DH2W3Ef3WsqnQ=="],
|
||||||
|
|
||||||
|
"@mui/x-charts-vendor": ["@mui/x-charts-vendor@9.0.0", "http://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-9.0.0.tgz", { "dependencies": { "@babel/runtime": "^7.28.6", "@types/d3-array": "^3.2.2", "@types/d3-color": "^3.1.3", "@types/d3-format": "^3.0.4", "@types/d3-interpolate": "^3.0.4", "@types/d3-path": "^3.1.1", "@types/d3-scale": "^4.0.9", "@types/d3-shape": "^3.1.8", "@types/d3-time": "^3.0.4", "@types/d3-time-format": "^4.0.3", "@types/d3-timer": "^3.0.2", "d3-array": "^3.2.4", "d3-color": "^3.1.0", "d3-format": "^3.1.2", "d3-interpolate": "^3.0.1", "d3-path": "^3.1.0", "d3-scale": "^4.0.2", "d3-shape": "^3.2.0", "d3-time": "^3.1.0", "d3-time-format": "^4.1.0", "d3-timer": "^3.0.1", "flatqueue": "^3.0.0", "internmap": "^2.0.3" } }, "sha512-Do91i+fZiNj/4LN5oaGpJoutolzDBDwdfw6tHrx2LKXDMCRlaImCfreLbdbkk7dFsi9fuIP7hWiMV4vDJKPJTA=="],
|
||||||
|
|
||||||
|
"@mui/x-internal-gestures": ["@mui/x-internal-gestures@9.0.0", "http://registry.npmjs.org/@mui/x-internal-gestures/-/x-internal-gestures-9.0.0.tgz", { "dependencies": { "@babel/runtime": "^7.28.6" } }, "sha512-+fW1EUai25GJbivGRsi3GX4GYsSvzFPvUEjmMgB4POkRBDjrEZNaLdVWfapT6DlWv/Vfbi08bYSuyvhPXGMZjw=="],
|
||||||
|
|
||||||
|
"@mui/x-internals": ["@mui/x-internals@9.0.0", "http://registry.npmjs.org/@mui/x-internals/-/x-internals-9.0.0.tgz", { "dependencies": { "@babel/runtime": "^7.28.6", "@mui/utils": "9.0.0", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-E/4rdg69JjhyybpPGypCjAKSKLLnSdCFM+O6P/nkUg47+qt3uftxQEhjQO53rcn6ahHl6du/uNZ9BLgeY6kYxQ=="],
|
||||||
|
|
||||||
|
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.3", "http://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ=="],
|
||||||
|
|
||||||
|
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "http://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||||
|
|
||||||
|
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "http://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||||
|
|
||||||
|
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "http://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||||
|
|
||||||
|
"@oxc-project/types": ["@oxc-project/types@0.124.0", "http://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", {}, "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg=="],
|
||||||
|
|
||||||
|
"@parcel/watcher": ["@parcel/watcher@2.5.6", "http://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.3" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.6", "@parcel/watcher-darwin-arm64": "2.5.6", "@parcel/watcher-darwin-x64": "2.5.6", "@parcel/watcher-freebsd-x64": "2.5.6", "@parcel/watcher-linux-arm-glibc": "2.5.6", "@parcel/watcher-linux-arm-musl": "2.5.6", "@parcel/watcher-linux-arm64-glibc": "2.5.6", "@parcel/watcher-linux-arm64-musl": "2.5.6", "@parcel/watcher-linux-x64-glibc": "2.5.6", "@parcel/watcher-linux-x64-musl": "2.5.6", "@parcel/watcher-win32-arm64": "2.5.6", "@parcel/watcher-win32-ia32": "2.5.6", "@parcel/watcher-win32-x64": "2.5.6" } }, "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.6", "http://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", { "os": "android", "cpu": "arm64" }, "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.6", "http://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.6", "http://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.6", "http://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.6", "http://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", { "os": "linux", "cpu": "arm" }, "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.6", "http://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", { "os": "linux", "cpu": "arm" }, "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.6", "http://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.6", "http://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.6", "http://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", { "os": "linux", "cpu": "x64" }, "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.6", "http://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.6", "http://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.6", "http://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g=="],
|
||||||
|
|
||||||
|
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "http://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="],
|
||||||
|
|
||||||
|
"@polka/url": ["@polka/url@1.0.0-next.29", "http://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
|
||||||
|
|
||||||
|
"@popperjs/core": ["@popperjs/core@2.11.8", "http://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", { "os": "android", "cpu": "arm64" }, "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", { "os": "linux", "cpu": "arm" }, "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", { "os": "linux", "cpu": "x64" }, "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", { "os": "linux", "cpu": "x64" }, "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", { "os": "none", "cpu": "arm64" }, "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.3" }, "cpu": "none" }, "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA=="],
|
||||||
|
|
||||||
|
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", { "os": "win32", "cpu": "x64" }, "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g=="],
|
||||||
|
|
||||||
|
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.7", "http://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", {}, "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA=="],
|
||||||
|
|
||||||
|
"@stylistic/eslint-plugin": ["@stylistic/eslint-plugin@4.4.1", "http://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.4.1.tgz", { "dependencies": { "@typescript-eslint/utils": "^8.32.1", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "estraverse": "^5.3.0", "picomatch": "^4.0.2" }, "peerDependencies": { "eslint": ">=9.0.0" } }, "sha512-CEigAk7eOLyHvdgmpZsKFwtiqS2wFwI1fn4j09IU9GmD4euFM4jEBAViWeCqaNLlbX2k2+A/Fq9cje4HQBXuJQ=="],
|
||||||
|
|
||||||
|
"@tanstack/query-core": ["@tanstack/query-core@5.97.0", "http://registry.npmjs.org/@tanstack/query-core/-/query-core-5.97.0.tgz", {}, "sha512-QdpLP5VzVMgo4VtaPppRA2W04UFjIqX+bxke/ZJhE5cfd5UPkRzqIAJQt9uXkQJjqE8LBOMbKv7f8HCsZltXlg=="],
|
||||||
|
|
||||||
|
"@tanstack/react-query": ["@tanstack/react-query@5.97.0", "http://registry.npmjs.org/@tanstack/react-query/-/react-query-5.97.0.tgz", { "dependencies": { "@tanstack/query-core": "5.97.0" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-y4So4eGcQoK2WVMAcDNZE9ofB/p5v1OlKvtc1F3uqHwrtifobT7q+ZnXk2mRkc8E84HKYSlAE9z6HXl2V0+ySQ=="],
|
||||||
|
|
||||||
|
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "http://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||||
|
|
||||||
|
"@types/d3-array": ["@types/d3-array@3.2.2", "http://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
|
||||||
|
|
||||||
|
"@types/d3-color": ["@types/d3-color@3.1.3", "http://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
|
||||||
|
|
||||||
|
"@types/d3-format": ["@types/d3-format@3.0.4", "http://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", {}, "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="],
|
||||||
|
|
||||||
|
"@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "http://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="],
|
||||||
|
|
||||||
|
"@types/d3-path": ["@types/d3-path@3.1.1", "http://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="],
|
||||||
|
|
||||||
|
"@types/d3-scale": ["@types/d3-scale@4.0.9", "http://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
|
||||||
|
|
||||||
|
"@types/d3-shape": ["@types/d3-shape@3.1.8", "http://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="],
|
||||||
|
|
||||||
|
"@types/d3-time": ["@types/d3-time@3.0.4", "http://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
|
||||||
|
|
||||||
|
"@types/d3-time-format": ["@types/d3-time-format@4.0.3", "http://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", {}, "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="],
|
||||||
|
|
||||||
|
"@types/d3-timer": ["@types/d3-timer@3.0.2", "http://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
|
||||||
|
|
||||||
|
"@types/estree": ["@types/estree@1.0.8", "http://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||||
|
|
||||||
|
"@types/json-schema": ["@types/json-schema@7.0.15", "http://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||||
|
|
||||||
|
"@types/parse-json": ["@types/parse-json@4.0.2", "http://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
|
||||||
|
|
||||||
|
"@types/prop-types": ["@types/prop-types@15.7.15", "http://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="],
|
||||||
|
|
||||||
|
"@types/react": ["@types/react@19.2.14", "http://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
|
||||||
|
|
||||||
|
"@types/react-transition-group": ["@types/react-transition-group@4.4.12", "http://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", { "peerDependencies": { "@types/react": "*" } }, "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.58.1", "http://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.1.tgz", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.58.1", "@typescript-eslint/types": "^8.58.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.58.1", "http://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.1.tgz", { "dependencies": { "@typescript-eslint/types": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1" } }, "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.58.1", "http://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.1.tgz", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/types": ["@typescript-eslint/types@8.58.1", "http://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.1.tgz", {}, "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.58.1", "http://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.1.tgz", { "dependencies": { "@typescript-eslint/project-service": "8.58.1", "@typescript-eslint/tsconfig-utils": "8.58.1", "@typescript-eslint/types": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.58.1", "http://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.1.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.58.1", "@typescript-eslint/types": "8.58.1", "@typescript-eslint/typescript-estree": "8.58.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.1", "http://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.1.tgz", { "dependencies": { "@typescript-eslint/types": "8.58.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ=="],
|
||||||
|
|
||||||
|
"@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.1", "http://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.7" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ=="],
|
||||||
|
|
||||||
|
"acorn": ["acorn@8.16.0", "http://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
|
||||||
|
|
||||||
|
"acorn-jsx": ["acorn-jsx@5.3.2", "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||||
|
|
||||||
|
"ajv": ["ajv@6.14.0", "http://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
|
||||||
|
|
||||||
|
"ansi-styles": ["ansi-styles@4.3.0", "http://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
|
|
||||||
|
"argparse": ["argparse@2.0.1", "http://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||||
|
|
||||||
|
"array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "http://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
|
||||||
|
|
||||||
|
"array-includes": ["array-includes@3.1.9", "http://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="],
|
||||||
|
|
||||||
|
"array.prototype.findlast": ["array.prototype.findlast@1.2.5", "http://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="],
|
||||||
|
|
||||||
|
"array.prototype.flat": ["array.prototype.flat@1.3.3", "http://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="],
|
||||||
|
|
||||||
|
"array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "http://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="],
|
||||||
|
|
||||||
|
"array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "http://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA=="],
|
||||||
|
|
||||||
|
"arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "http://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="],
|
||||||
|
|
||||||
|
"async-function": ["async-function@1.0.0", "http://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
|
||||||
|
|
||||||
|
"available-typed-arrays": ["available-typed-arrays@1.0.7", "http://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
||||||
|
|
||||||
|
"babel-plugin-macros": ["babel-plugin-macros@3.1.0", "http://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="],
|
||||||
|
|
||||||
|
"balanced-match": ["balanced-match@1.0.0", "http://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", {}, "sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg=="],
|
||||||
|
|
||||||
|
"bezier-easing": ["bezier-easing@2.1.0", "http://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz", {}, "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig=="],
|
||||||
|
|
||||||
|
"brace-expansion": ["brace-expansion@1.1.11", "http://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||||
|
|
||||||
|
"braces": ["braces@3.0.3", "http://registry.npmjs.org/braces/-/braces-3.0.3.tgz", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||||
|
|
||||||
|
"call-bind": ["call-bind@1.0.9", "http://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="],
|
||||||
|
|
||||||
|
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "http://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||||
|
|
||||||
|
"call-bound": ["call-bound@1.0.4", "http://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||||
|
|
||||||
|
"callsites": ["callsites@3.1.0", "http://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||||
|
|
||||||
|
"chalk": ["chalk@4.1.2", "http://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
|
"chokidar": ["chokidar@4.0.3", "http://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||||
|
|
||||||
|
"clsx": ["clsx@2.1.1", "http://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||||
|
|
||||||
|
"color-convert": ["color-convert@2.0.1", "http://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||||
|
|
||||||
|
"color-name": ["color-name@1.1.4", "http://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||||
|
|
||||||
|
"concat-map": ["concat-map@0.0.1", "http://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||||
|
|
||||||
|
"convert-source-map": ["convert-source-map@1.9.0", "http://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="],
|
||||||
|
|
||||||
|
"cosmiconfig": ["cosmiconfig@7.1.0", "http://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" } }, "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA=="],
|
||||||
|
|
||||||
|
"cross-spawn": ["cross-spawn@7.0.6", "http://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||||
|
|
||||||
|
"csstype": ["csstype@3.2.3", "http://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||||
|
|
||||||
|
"d3-array": ["d3-array@3.2.4", "http://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
|
||||||
|
|
||||||
|
"d3-color": ["d3-color@3.1.0", "http://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
|
||||||
|
|
||||||
|
"d3-format": ["d3-format@3.1.2", "http://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="],
|
||||||
|
|
||||||
|
"d3-interpolate": ["d3-interpolate@3.0.1", "http://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
|
||||||
|
|
||||||
|
"d3-path": ["d3-path@3.1.0", "http://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
|
||||||
|
|
||||||
|
"d3-scale": ["d3-scale@4.0.2", "http://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
|
||||||
|
|
||||||
|
"d3-shape": ["d3-shape@3.2.0", "http://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
|
||||||
|
|
||||||
|
"d3-time": ["d3-time@3.1.0", "http://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
|
||||||
|
|
||||||
|
"d3-time-format": ["d3-time-format@4.1.0", "http://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
|
||||||
|
|
||||||
|
"d3-timer": ["d3-timer@3.0.1", "http://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
|
||||||
|
|
||||||
|
"data-view-buffer": ["data-view-buffer@1.0.2", "http://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
|
||||||
|
|
||||||
|
"data-view-byte-length": ["data-view-byte-length@1.0.2", "http://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
|
||||||
|
|
||||||
|
"data-view-byte-offset": ["data-view-byte-offset@1.0.1", "http://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
|
||||||
|
|
||||||
|
"debug": ["debug@4.4.3", "http://registry.npmjs.org/debug/-/debug-4.4.3.tgz", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||||
|
|
||||||
|
"deep-is": ["deep-is@0.1.4", "http://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||||
|
|
||||||
|
"define-data-property": ["define-data-property@1.1.4", "http://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
|
||||||
|
|
||||||
|
"define-properties": ["define-properties@1.2.1", "http://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
||||||
|
|
||||||
|
"detect-libc": ["detect-libc@2.1.2", "http://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||||
|
|
||||||
|
"doctrine": ["doctrine@2.1.0", "http://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
||||||
|
|
||||||
|
"dom-helpers": ["dom-helpers@5.2.1", "http://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
|
||||||
|
|
||||||
|
"dunder-proto": ["dunder-proto@1.0.1", "http://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||||
|
|
||||||
|
"error-ex": ["error-ex@1.3.4", "http://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="],
|
||||||
|
|
||||||
|
"es-abstract": ["es-abstract@1.24.2", "http://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg=="],
|
||||||
|
|
||||||
|
"es-define-property": ["es-define-property@1.0.1", "http://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||||
|
|
||||||
|
"es-errors": ["es-errors@1.3.0", "http://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||||
|
|
||||||
|
"es-iterator-helpers": ["es-iterator-helpers@1.3.2", "http://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "math-intrinsics": "^1.1.0" } }, "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw=="],
|
||||||
|
|
||||||
|
"es-object-atoms": ["es-object-atoms@1.1.1", "http://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||||
|
|
||||||
|
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "http://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
|
||||||
|
|
||||||
|
"es-shim-unscopables": ["es-shim-unscopables@1.1.0", "http://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="],
|
||||||
|
|
||||||
|
"es-to-primitive": ["es-to-primitive@1.3.0", "http://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="],
|
||||||
|
|
||||||
|
"escape-string-regexp": ["escape-string-regexp@4.0.0", "http://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||||
|
|
||||||
|
"eslint": ["eslint@9.39.4", "http://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ=="],
|
||||||
|
|
||||||
|
"eslint-plugin-react": ["eslint-plugin-react@7.37.5", "http://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="],
|
||||||
|
|
||||||
|
"eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "http://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
|
||||||
|
|
||||||
|
"eslint-scope": ["eslint-scope@8.4.0", "http://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
|
||||||
|
|
||||||
|
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "http://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
|
||||||
|
|
||||||
|
"espree": ["espree@10.4.0", "http://registry.npmjs.org/espree/-/espree-10.4.0.tgz", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
|
||||||
|
|
||||||
|
"esquery": ["esquery@1.7.0", "http://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="],
|
||||||
|
|
||||||
|
"esrecurse": ["esrecurse@4.3.0", "http://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
|
||||||
|
|
||||||
|
"estraverse": ["estraverse@5.3.0", "http://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
|
||||||
|
|
||||||
|
"esutils": ["esutils@2.0.3", "http://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
||||||
|
|
||||||
|
"fast-deep-equal": ["fast-deep-equal@3.1.3", "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||||
|
|
||||||
|
"fast-glob": ["fast-glob@3.3.3", "http://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
||||||
|
|
||||||
|
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "http://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
||||||
|
|
||||||
|
"fast-levenshtein": ["fast-levenshtein@2.0.6", "http://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
|
||||||
|
|
||||||
|
"fastq": ["fastq@1.20.1", "http://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
|
||||||
|
|
||||||
|
"fdir": ["fdir@6.5.0", "http://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
|
|
||||||
|
"file-entry-cache": ["file-entry-cache@8.0.0", "http://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||||
|
|
||||||
|
"fill-range": ["fill-range@7.1.1", "http://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||||
|
|
||||||
|
"find-root": ["find-root@1.1.0", "http://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="],
|
||||||
|
|
||||||
|
"find-up": ["find-up@5.0.0", "http://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
|
||||||
|
|
||||||
|
"flat-cache": ["flat-cache@4.0.1", "http://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
|
||||||
|
|
||||||
|
"flatqueue": ["flatqueue@3.0.0", "http://registry.npmjs.org/flatqueue/-/flatqueue-3.0.0.tgz", {}, "sha512-y1deYaVt+lIc/d2uIcWDNd0CrdQTO5xoCjeFdhX0kSXvm2Acm0o+3bAOiYklTEoRyzwio3sv3/IiBZdusbAe2Q=="],
|
||||||
|
|
||||||
|
"flatted": ["flatted@3.4.2", "http://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="],
|
||||||
|
|
||||||
|
"for-each": ["for-each@0.3.5", "http://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
||||||
|
|
||||||
|
"fsevents": ["fsevents@2.3.3", "http://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
|
"function-bind": ["function-bind@1.1.2", "http://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||||
|
|
||||||
|
"function.prototype.name": ["function.prototype.name@1.1.8", "http://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="],
|
||||||
|
|
||||||
|
"functions-have-names": ["functions-have-names@1.2.3", "http://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
|
||||||
|
|
||||||
|
"generator-function": ["generator-function@2.0.1", "http://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="],
|
||||||
|
|
||||||
|
"get-intrinsic": ["get-intrinsic@1.3.0", "http://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||||
|
|
||||||
|
"get-proto": ["get-proto@1.0.1", "http://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||||
|
|
||||||
|
"get-symbol-description": ["get-symbol-description@1.1.0", "http://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
|
||||||
|
|
||||||
|
"glob-parent": ["glob-parent@6.0.2", "http://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||||
|
|
||||||
|
"globals": ["globals@15.15.0", "http://registry.npmjs.org/globals/-/globals-15.15.0.tgz", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="],
|
||||||
|
|
||||||
|
"globalthis": ["globalthis@1.0.4", "http://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
|
||||||
|
|
||||||
|
"gopd": ["gopd@1.2.0", "http://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||||
|
|
||||||
|
"has-bigints": ["has-bigints@1.1.0", "http://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
|
||||||
|
|
||||||
|
"has-flag": ["has-flag@4.0.0", "http://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||||
|
|
||||||
|
"has-property-descriptors": ["has-property-descriptors@1.0.2", "http://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
|
||||||
|
|
||||||
|
"has-proto": ["has-proto@1.2.0", "http://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="],
|
||||||
|
|
||||||
|
"has-symbols": ["has-symbols@1.1.0", "http://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||||
|
|
||||||
|
"has-tostringtag": ["has-tostringtag@1.0.2", "http://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||||
|
|
||||||
|
"hasown": ["hasown@2.0.2", "http://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||||
|
|
||||||
|
"hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="],
|
||||||
|
|
||||||
|
"howler": ["howler@2.1.2", "http://registry.npmjs.org/howler/-/howler-2.1.2.tgz", {}, "sha512-oKrTFaVXsDRoB/jik7cEpWKTj7VieoiuzMYJ7E/EU5ayvmpRhumCv3YQ3823zi9VTJkSWAhbryHnlZAionGAJg=="],
|
||||||
|
|
||||||
|
"ignore": ["ignore@5.3.2", "http://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||||
|
|
||||||
|
"immutable": ["immutable@5.1.5", "http://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", {}, "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A=="],
|
||||||
|
|
||||||
|
"import-fresh": ["import-fresh@3.3.1", "http://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
||||||
|
|
||||||
|
"imurmurhash": ["imurmurhash@0.1.4", "http://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
||||||
|
|
||||||
|
"internal-slot": ["internal-slot@1.1.0", "http://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
|
||||||
|
|
||||||
|
"internmap": ["internmap@2.0.3", "http://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
|
||||||
|
|
||||||
|
"is-array-buffer": ["is-array-buffer@3.0.5", "http://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
|
||||||
|
|
||||||
|
"is-arrayish": ["is-arrayish@0.2.1", "http://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
|
||||||
|
|
||||||
|
"is-async-function": ["is-async-function@2.1.1", "http://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="],
|
||||||
|
|
||||||
|
"is-bigint": ["is-bigint@1.1.0", "http://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="],
|
||||||
|
|
||||||
|
"is-boolean-object": ["is-boolean-object@1.2.2", "http://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="],
|
||||||
|
|
||||||
|
"is-callable": ["is-callable@1.2.7", "http://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="],
|
||||||
|
|
||||||
|
"is-core-module": ["is-core-module@2.16.1", "http://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
|
||||||
|
|
||||||
|
"is-data-view": ["is-data-view@1.0.2", "http://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="],
|
||||||
|
|
||||||
|
"is-date-object": ["is-date-object@1.1.0", "http://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="],
|
||||||
|
|
||||||
|
"is-extglob": ["is-extglob@2.1.1", "http://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||||
|
|
||||||
|
"is-finalizationregistry": ["is-finalizationregistry@1.1.1", "http://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="],
|
||||||
|
|
||||||
|
"is-generator-function": ["is-generator-function@1.1.2", "http://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="],
|
||||||
|
|
||||||
|
"is-glob": ["is-glob@4.0.3", "http://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||||
|
|
||||||
|
"is-map": ["is-map@2.0.3", "http://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
|
||||||
|
|
||||||
|
"is-negative-zero": ["is-negative-zero@2.0.3", "http://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="],
|
||||||
|
|
||||||
|
"is-number": ["is-number@7.0.0", "http://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||||
|
|
||||||
|
"is-number-object": ["is-number-object@1.1.1", "http://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
|
||||||
|
|
||||||
|
"is-regex": ["is-regex@1.2.1", "http://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
|
||||||
|
|
||||||
|
"is-set": ["is-set@2.0.3", "http://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
|
||||||
|
|
||||||
|
"is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "http://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="],
|
||||||
|
|
||||||
|
"is-string": ["is-string@1.1.1", "http://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="],
|
||||||
|
|
||||||
|
"is-symbol": ["is-symbol@1.1.1", "http://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="],
|
||||||
|
|
||||||
|
"is-typed-array": ["is-typed-array@1.1.15", "http://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="],
|
||||||
|
|
||||||
|
"is-weakmap": ["is-weakmap@2.0.2", "http://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="],
|
||||||
|
|
||||||
|
"is-weakref": ["is-weakref@1.1.1", "http://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="],
|
||||||
|
|
||||||
|
"is-weakset": ["is-weakset@2.0.4", "http://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="],
|
||||||
|
|
||||||
|
"isarray": ["isarray@2.0.5", "http://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||||
|
|
||||||
|
"isexe": ["isexe@2.0.0", "http://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
|
|
||||||
|
"iterator.prototype": ["iterator.prototype@1.1.5", "http://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="],
|
||||||
|
|
||||||
|
"js-tokens": ["js-tokens@4.0.0", "http://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||||
|
|
||||||
|
"js-yaml": ["js-yaml@4.1.1", "http://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||||
|
|
||||||
|
"jsesc": ["jsesc@3.1.0", "http://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||||
|
|
||||||
|
"json-buffer": ["json-buffer@3.0.1", "http://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
||||||
|
|
||||||
|
"json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "http://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
|
||||||
|
|
||||||
|
"json-schema-traverse": ["json-schema-traverse@0.4.1", "http://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||||
|
|
||||||
|
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "http://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
||||||
|
|
||||||
|
"jsx-ast-utils": ["jsx-ast-utils@3.3.5", "http://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="],
|
||||||
|
|
||||||
|
"keyv": ["keyv@4.5.4", "http://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||||
|
|
||||||
|
"levn": ["levn@0.4.1", "http://registry.npmjs.org/levn/-/levn-0.4.1.tgz", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||||
|
|
||||||
|
"lightningcss": ["lightningcss@1.32.0", "http://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
|
||||||
|
|
||||||
|
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "http://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
|
||||||
|
|
||||||
|
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "http://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
|
||||||
|
|
||||||
|
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "http://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
|
||||||
|
|
||||||
|
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "http://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="],
|
||||||
|
|
||||||
|
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "http://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="],
|
||||||
|
|
||||||
|
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "http://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="],
|
||||||
|
|
||||||
|
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "http://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="],
|
||||||
|
|
||||||
|
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "http://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="],
|
||||||
|
|
||||||
|
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "http://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="],
|
||||||
|
|
||||||
|
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "http://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="],
|
||||||
|
|
||||||
|
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "http://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
|
||||||
|
|
||||||
|
"lines-and-columns": ["lines-and-columns@1.2.4", "http://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
|
||||||
|
|
||||||
|
"locate-path": ["locate-path@6.0.0", "http://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||||
|
|
||||||
|
"lodash": ["lodash@4.18.1", "http://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="],
|
||||||
|
|
||||||
|
"lodash.merge": ["lodash.merge@4.6.2", "http://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||||
|
|
||||||
|
"loose-envify": ["loose-envify@1.4.0", "http://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": "cli.js" }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
||||||
|
|
||||||
|
"math-intrinsics": ["math-intrinsics@1.1.0", "http://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||||
|
|
||||||
|
"merge2": ["merge2@1.4.1", "http://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||||
|
|
||||||
|
"micromatch": ["micromatch@4.0.8", "http://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||||
|
|
||||||
|
"minimatch": ["minimatch@3.1.5", "http://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
|
||||||
|
|
||||||
|
"mrmime": ["mrmime@2.0.1", "http://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
|
||||||
|
|
||||||
|
"ms": ["ms@2.1.3", "http://registry.npmjs.org/ms/-/ms-2.1.3.tgz", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"nanoid": ["nanoid@3.3.11", "http://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
|
"natural-compare": ["natural-compare@1.4.0", "http://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
||||||
|
|
||||||
|
"node-addon-api": ["node-addon-api@7.1.1", "http://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
|
||||||
|
|
||||||
|
"node-exports-info": ["node-exports-info@1.6.0", "http://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw=="],
|
||||||
|
|
||||||
|
"object-assign": ["object-assign@4.1.1", "http://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||||
|
|
||||||
|
"object-inspect": ["object-inspect@1.13.4", "http://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||||
|
|
||||||
|
"object-keys": ["object-keys@1.1.1", "http://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
|
||||||
|
|
||||||
|
"object.assign": ["object.assign@4.1.7", "http://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="],
|
||||||
|
|
||||||
|
"object.entries": ["object.entries@1.1.9", "http://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="],
|
||||||
|
|
||||||
|
"object.fromentries": ["object.fromentries@2.0.8", "http://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="],
|
||||||
|
|
||||||
|
"object.values": ["object.values@1.2.1", "http://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
|
||||||
|
|
||||||
|
"optionator": ["optionator@0.9.4", "http://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
||||||
|
|
||||||
|
"own-keys": ["own-keys@1.0.1", "http://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
|
||||||
|
|
||||||
|
"p-limit": ["p-limit@3.1.0", "http://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
||||||
|
|
||||||
|
"p-locate": ["p-locate@5.0.0", "http://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
||||||
|
|
||||||
|
"parent-module": ["parent-module@1.0.1", "http://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
||||||
|
|
||||||
|
"parse-json": ["parse-json@5.2.0", "http://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
|
||||||
|
|
||||||
|
"path-exists": ["path-exists@4.0.0", "http://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
||||||
|
|
||||||
|
"path-key": ["path-key@3.1.1", "http://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||||
|
|
||||||
|
"path-parse": ["path-parse@1.0.7", "http://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
||||||
|
|
||||||
|
"path-type": ["path-type@4.0.0", "http://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
|
||||||
|
|
||||||
|
"picocolors": ["picocolors@1.1.1", "http://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
|
|
||||||
|
"picomatch": ["picomatch@4.0.4", "http://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
||||||
|
|
||||||
|
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "http://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
|
||||||
|
|
||||||
|
"postcss": ["postcss@8.5.9", "http://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw=="],
|
||||||
|
|
||||||
|
"prelude-ls": ["prelude-ls@1.2.1", "http://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||||
|
|
||||||
|
"prop-types": ["prop-types@15.7.2", "http://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.8.1" } }, "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ=="],
|
||||||
|
|
||||||
|
"punycode": ["punycode@2.3.1", "http://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||||
|
|
||||||
|
"queue-microtask": ["queue-microtask@1.2.3", "http://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||||
|
|
||||||
|
"react": ["react@19.2.5", "http://registry.npmjs.org/react/-/react-19.2.5.tgz", {}, "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA=="],
|
||||||
|
|
||||||
|
"react-dom": ["react-dom@19.2.5", "http://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.5" } }, "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag=="],
|
||||||
|
|
||||||
|
"react-is": ["react-is@19.2.5", "http://registry.npmjs.org/react-is/-/react-is-19.2.5.tgz", {}, "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ=="],
|
||||||
|
|
||||||
|
"react-transition-group": ["react-transition-group@4.4.5", "http://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
|
||||||
|
|
||||||
|
"readdirp": ["readdirp@4.1.2", "http://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||||
|
|
||||||
|
"reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "http://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
|
||||||
|
|
||||||
|
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "http://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
|
||||||
|
|
||||||
|
"reselect": ["reselect@5.1.1", "http://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="],
|
||||||
|
|
||||||
|
"resolve": ["resolve@2.0.0-next.6", "http://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA=="],
|
||||||
|
|
||||||
|
"resolve-from": ["resolve-from@4.0.0", "http://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||||
|
|
||||||
|
"reusify": ["reusify@1.1.0", "http://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||||
|
|
||||||
|
"rolldown": ["rolldown@1.0.0-rc.15", "http://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", { "dependencies": { "@oxc-project/types": "=0.124.0", "@rolldown/pluginutils": "1.0.0-rc.15" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-x64": "1.0.0-rc.15", "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g=="],
|
||||||
|
|
||||||
|
"run-parallel": ["run-parallel@1.2.0", "http://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||||
|
|
||||||
|
"safe-array-concat": ["safe-array-concat@1.1.3", "http://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="],
|
||||||
|
|
||||||
|
"safe-push-apply": ["safe-push-apply@1.0.0", "http://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="],
|
||||||
|
|
||||||
|
"safe-regex-test": ["safe-regex-test@1.1.0", "http://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
|
||||||
|
|
||||||
|
"sass": ["sass@1.99.0", "http://registry.npmjs.org/sass/-/sass-1.99.0.tgz", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": "sass.js" }, "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q=="],
|
||||||
|
|
||||||
|
"scheduler": ["scheduler@0.27.0", "http://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||||
|
|
||||||
|
"semver": ["semver@6.3.1", "http://registry.npmjs.org/semver/-/semver-6.3.1.tgz", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
|
"set-function-length": ["set-function-length@1.2.2", "http://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
||||||
|
|
||||||
|
"set-function-name": ["set-function-name@2.0.2", "http://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
|
||||||
|
|
||||||
|
"set-proto": ["set-proto@1.0.0", "http://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="],
|
||||||
|
|
||||||
|
"shebang-command": ["shebang-command@2.0.0", "http://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||||
|
|
||||||
|
"shebang-regex": ["shebang-regex@3.0.0", "http://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||||
|
|
||||||
|
"side-channel": ["side-channel@1.1.0", "http://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
||||||
|
|
||||||
|
"side-channel-list": ["side-channel-list@1.0.1", "http://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="],
|
||||||
|
|
||||||
|
"side-channel-map": ["side-channel-map@1.0.1", "http://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
|
||||||
|
|
||||||
|
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "http://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||||
|
|
||||||
|
"sirv": ["sirv@3.0.2", "http://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="],
|
||||||
|
|
||||||
|
"source-map": ["source-map@0.5.7", "http://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="],
|
||||||
|
|
||||||
|
"source-map-js": ["source-map-js@1.2.1", "http://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||||
|
|
||||||
|
"stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "http://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
||||||
|
|
||||||
|
"string.prototype.matchall": ["string.prototype.matchall@4.0.12", "http://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="],
|
||||||
|
|
||||||
|
"string.prototype.repeat": ["string.prototype.repeat@1.0.0", "http://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="],
|
||||||
|
|
||||||
|
"string.prototype.trim": ["string.prototype.trim@1.2.10", "http://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="],
|
||||||
|
|
||||||
|
"string.prototype.trimend": ["string.prototype.trimend@1.0.9", "http://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="],
|
||||||
|
|
||||||
|
"string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "http://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="],
|
||||||
|
|
||||||
|
"strip-json-comments": ["strip-json-comments@3.1.1", "http://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||||
|
|
||||||
|
"stylis": ["stylis@4.2.0", "http://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", {}, "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="],
|
||||||
|
|
||||||
|
"supports-color": ["supports-color@7.2.0", "http://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||||
|
|
||||||
|
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "http://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||||
|
|
||||||
|
"tinyglobby": ["tinyglobby@0.2.16", "http://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
|
||||||
|
|
||||||
|
"to-regex-range": ["to-regex-range@5.0.1", "http://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||||
|
|
||||||
|
"totalist": ["totalist@3.0.1", "http://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
|
||||||
|
|
||||||
|
"ts-api-utils": ["ts-api-utils@2.5.0", "http://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="],
|
||||||
|
|
||||||
|
"tslib": ["tslib@2.8.1", "http://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
|
"type-check": ["type-check@0.4.0", "http://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
||||||
|
|
||||||
|
"typed-array-buffer": ["typed-array-buffer@1.0.3", "http://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
|
||||||
|
|
||||||
|
"typed-array-byte-length": ["typed-array-byte-length@1.0.3", "http://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="],
|
||||||
|
|
||||||
|
"typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "http://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="],
|
||||||
|
|
||||||
|
"typed-array-length": ["typed-array-length@1.0.7", "http://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@6.0.2", "http://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="],
|
||||||
|
|
||||||
|
"unbox-primitive": ["unbox-primitive@1.1.0", "http://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
|
||||||
|
|
||||||
|
"uri-js": ["uri-js@4.4.1", "http://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||||
|
|
||||||
|
"use-sync-external-store": ["use-sync-external-store@1.6.0", "http://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
|
||||||
|
|
||||||
|
"vite": ["vite@8.0.8", "http://registry.npmjs.org/vite/-/vite-8.0.8.tgz", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw=="],
|
||||||
|
|
||||||
|
"vite-plugin-symfony": ["vite-plugin-symfony@8.2.4", "http://registry.npmjs.org/vite-plugin-symfony/-/vite-plugin-symfony-8.2.4.tgz", { "dependencies": { "debug": "^4.4.1", "fast-glob": "^3.3.3", "picocolors": "^1.1.1", "sirv": "^3.0.1" }, "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-ph98EMPx8FhA6QIp43ZiK0zjODV9jumB7EHXfZEhRme2lo9oBa9sAXCNCCIvdVk/m9EWkNZpcRBjequjXZiSuA=="],
|
||||||
|
|
||||||
|
"which": ["which@2.0.2", "http://registry.npmjs.org/which/-/which-2.0.2.tgz", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||||
|
|
||||||
|
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "http://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
|
||||||
|
|
||||||
|
"which-builtin-type": ["which-builtin-type@1.2.1", "http://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="],
|
||||||
|
|
||||||
|
"which-collection": ["which-collection@1.0.2", "http://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="],
|
||||||
|
|
||||||
|
"which-typed-array": ["which-typed-array@1.1.20", "http://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="],
|
||||||
|
|
||||||
|
"word-wrap": ["word-wrap@1.2.5", "http://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
||||||
|
|
||||||
|
"yaml": ["yaml@1.10.3", "http://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
|
||||||
|
|
||||||
|
"yocto-queue": ["yocto-queue@0.1.0", "http://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||||
|
|
||||||
|
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "http://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||||
|
|
||||||
|
"@eslint/eslintrc/globals": ["globals@14.0.0", "http://registry.npmjs.org/globals/-/globals-14.0.0.tgz", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
|
||||||
|
|
||||||
|
"@mui/material/prop-types": ["prop-types@15.8.1", "http://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||||
|
|
||||||
|
"@mui/private-theming/prop-types": ["prop-types@15.8.1", "http://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||||
|
|
||||||
|
"@mui/styled-engine/prop-types": ["prop-types@15.8.1", "http://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||||
|
|
||||||
|
"@mui/system/prop-types": ["prop-types@15.8.1", "http://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||||
|
|
||||||
|
"@mui/utils/prop-types": ["prop-types@15.8.1", "http://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||||
|
|
||||||
|
"@mui/x-charts/prop-types": ["prop-types@15.8.1", "http://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.5", "http://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.4", "http://registry.npmjs.org/semver/-/semver-7.7.4.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "http://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
|
||||||
|
|
||||||
|
"babel-plugin-macros/resolve": ["resolve@1.22.12", "http://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="],
|
||||||
|
|
||||||
|
"eslint-plugin-react/prop-types": ["prop-types@15.8.1", "http://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||||
|
|
||||||
|
"fast-glob/glob-parent": ["glob-parent@5.1.2", "http://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
|
"hoist-non-react-statics/react-is": ["react-is@16.13.1", "http://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"micromatch/picomatch": ["picomatch@2.3.2", "http://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||||
|
|
||||||
|
"prop-types/react-is": ["react-is@16.11.0", "http://registry.npmjs.org/react-is/-/react-is-16.11.0.tgz", {}, "sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw=="],
|
||||||
|
|
||||||
|
"react-transition-group/prop-types": ["prop-types@15.8.1", "http://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||||
|
|
||||||
|
"rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.15", "http://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", {}, "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g=="],
|
||||||
|
|
||||||
|
"@mui/material/prop-types/react-is": ["react-is@16.13.1", "http://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"@mui/private-theming/prop-types/react-is": ["react-is@16.13.1", "http://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"@mui/styled-engine/prop-types/react-is": ["react-is@16.13.1", "http://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"@mui/system/prop-types/react-is": ["react-is@16.13.1", "http://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"@mui/utils/prop-types/react-is": ["react-is@16.13.1", "http://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"@mui/x-charts/prop-types/react-is": ["react-is@16.13.1", "http://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.5", "http://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="],
|
||||||
|
|
||||||
|
"eslint-plugin-react/prop-types/react-is": ["react-is@16.13.1", "http://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"react-transition-group/prop-types/react-is": ["react-is@16.13.1", "http://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "http://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
125
compose.yaml
Normal file
125
compose.yaml
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: '${APP_NAME}-app'
|
||||||
|
ports:
|
||||||
|
- "10080:80"
|
||||||
|
environment:
|
||||||
|
SERVER_NAME: ${SERVER_NAME:-:80}
|
||||||
|
APP_ENV: prod
|
||||||
|
APP_SECRET: ${APP_SECRET}
|
||||||
|
APP_PUBLIC_HOSTNAME: ${APP_PUBLIC_HOSTNAME:-localhost}
|
||||||
|
APP_CONTACT_MAIL_ADDRESS: ${APP_CONTACT_MAIL_ADDRESS:-7system7@gmail.com}
|
||||||
|
DATABASE_URL: >-
|
||||||
|
postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?serverVersion=${POSTGRES_VERSION}&charset=utf8
|
||||||
|
POSTGRES_URL: db
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
MERCURE_URL: http://localhost/.well-known/mercure
|
||||||
|
MERCURE_PUBLIC_URL: https://${APP_PUBLIC_HOSTNAME:-localhost}/.well-known/mercure
|
||||||
|
MERCURE_JWT_SECRET: ${MERCURE_JWT_SECRET}
|
||||||
|
MERCURE_JWT_TOKEN: ${MERCURE_JWT_TOKEN}
|
||||||
|
MERCURE_SUBSCRIBER_JWT: ${MERCURE_SUBSCRIBER_JWT}
|
||||||
|
MAILER_DSN: smtp://mail:25?verify_peer=0
|
||||||
|
RECAPTCHA_SITE_KEY: ${RECAPTCHA_SITE_KEY}
|
||||||
|
RECAPTCHA_SECRET_KEY: ${RECAPTCHA_SECRET_KEY}
|
||||||
|
WEBAUTHN_RP_ID: ${WEBAUTHN_RP_ID:-localhost}
|
||||||
|
WEBAUTHN_ORIGIN: ${WEBAUTHN_ORIGIN:-https://localhost}
|
||||||
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||||
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||||
|
MINIO_ENDPOINT: http://minio:9000
|
||||||
|
MINIO_PUBLIC_URL: ${MINIO_PUBLIC_URL:-http://localhost:9000}
|
||||||
|
# IMPORTANT: Set TRUSTED_PROXIES to your reverse proxy IP in production.
|
||||||
|
# For Docker on same host, use: 172.17.0.1 (default bridge) or 172.16.0.0/12 (overlay network)
|
||||||
|
# For Kubernetes or external proxy, use the proxy's IP address.
|
||||||
|
# WARNING: Using 0.0.0.0/0 is insecure in production environments!
|
||||||
|
TRUSTED_PROXIES: ${TRUSTED_PROXIES:-127.0.0.1}
|
||||||
|
volumes:
|
||||||
|
- app_var:/app/var
|
||||||
|
- caddy_data:/data
|
||||||
|
- caddy_config:/config
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
mail:
|
||||||
|
condition: service_started
|
||||||
|
minio:
|
||||||
|
image: minio/minio:RELEASE.2025-09-07T16-13-09Z-cpuv1
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: '${APP_NAME}-minio'
|
||||||
|
command: server /data --console-address ":9001"
|
||||||
|
ports:
|
||||||
|
- "9000:9000"
|
||||||
|
- "9001:9001"
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||||
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- minio_data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
start_period: 10s
|
||||||
|
minio_init:
|
||||||
|
image: minio/minio:RELEASE.2025-09-07T16-13-09Z-cpuv1
|
||||||
|
restart: "no"
|
||||||
|
container_name: '${APP_NAME}-minio-init'
|
||||||
|
depends_on:
|
||||||
|
minio:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||||
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- ./docker/minio-init.sh:/minio-init.sh:ro
|
||||||
|
entrypoint: ["/bin/sh", "/minio-init.sh"]
|
||||||
|
mail:
|
||||||
|
image: boky/postfix:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: '${APP_NAME}-mail'
|
||||||
|
hostname: postfix.mail.${MAIL_DOMAIN:-localhost}
|
||||||
|
environment:
|
||||||
|
ALLOWED_SENDER_DOMAINS: ${MAIL_DOMAIN:-localhost}
|
||||||
|
HOSTNAME: postfix.mail.${MAIL_DOMAIN:-localhost}
|
||||||
|
POSTFIX_myhostname: postfix.mail.${MAIL_DOMAIN:-localhost}
|
||||||
|
POSTFIX_mynetworks: "127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16"
|
||||||
|
POSTFIX_smtpd_tls_security_level: none
|
||||||
|
RELAYHOST: ${MAIL_RELAYHOST:-}
|
||||||
|
RELAYHOST_AUTH: ${MAIL_RELAYHOST_AUTH:-}
|
||||||
|
RELAYHOST_PASSWORD: ${MAIL_RELAYHOST_PASSWORD:-}
|
||||||
|
volumes:
|
||||||
|
- postfix_spool:/var/spool/postfix
|
||||||
|
- ./docker/aliases:/tmp/aliases:ro
|
||||||
|
db:
|
||||||
|
image: postgres:${POSTGRES_VERSION:-18}-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: '${APP_NAME}-db'
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}" ]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 10
|
||||||
|
start_period: 10s
|
||||||
|
ports:
|
||||||
|
- "15432:5432"
|
||||||
|
volumes:
|
||||||
|
app_var:
|
||||||
|
postgres_data:
|
||||||
|
caddy_data:
|
||||||
|
caddy_config:
|
||||||
|
postfix_spool:
|
||||||
|
minio_data:
|
||||||
|
|
||||||
|
|
||||||
171
composer.json
171
composer.json
@@ -1,76 +1,103 @@
|
|||||||
{
|
{
|
||||||
"type": "project",
|
"name": "splendid-bear/mineseeker",
|
||||||
"license": "proprietary",
|
"version": "2026.2.1",
|
||||||
"require": {
|
"license": "GPL-3.0-or-later",
|
||||||
"php": "^7.3",
|
"author": "https://www.splendidbear.org",
|
||||||
"ext-iconv": "*",
|
"bugs": "https://source.splendidbear.org",
|
||||||
"ext-json": "*",
|
"description": "This is a minesweeper game that is inspired from MSN Messenger's game.",
|
||||||
"doctrine/doctrine-migrations-bundle": "^2.0",
|
"minimum-stability": "dev",
|
||||||
"doctrine/orm": "^2.6",
|
"type": "project",
|
||||||
"gos/web-socket-bundle": "^1.8",
|
"prefer-stable": true,
|
||||||
"sensio/framework-extra-bundle": "^5.5",
|
"private": true,
|
||||||
"sonata-project/admin-bundle": "^3.0",
|
"require": {
|
||||||
"sonata-project/doctrine-orm-admin-bundle": "^3.0",
|
"php": ">=8.5",
|
||||||
"sonata-project/user-bundle": "^4.0",
|
"ext-iconv": "*",
|
||||||
"symfony/console": "^4.0",
|
"ext-json": "*",
|
||||||
"symfony/flex": "^1.0",
|
"ext-gd": "*",
|
||||||
"symfony/framework-bundle": "^4.0",
|
"doctrine/dbal": "^3.7",
|
||||||
"symfony/mailer": "^4.0",
|
"doctrine/doctrine-bundle": ">=2.11 <2.14",
|
||||||
"symfony/monolog-bundle": "^3.4",
|
"doctrine/doctrine-migrations-bundle": "^3.0",
|
||||||
"symfony/orm-pack": "^1.0",
|
"doctrine/orm": "^2.6",
|
||||||
"symfony/polyfill-apcu": "^1.0",
|
"endroid/qr-code": "^6.1",
|
||||||
"symfony/twig-bundle": "^4.0",
|
"league/flysystem-aws-s3-v3": "^3.0",
|
||||||
"symfony/webpack-encore-bundle": "^1.7",
|
"league/flysystem-bundle": "^3.6",
|
||||||
"symfony/yaml": "^4.0"
|
"liip/imagine-bundle": "^2.13",
|
||||||
|
"pentatrion/vite-bundle": "^8.2",
|
||||||
|
"scheb/2fa-backup-code": "^8.5",
|
||||||
|
"scheb/2fa-bundle": "^8.5",
|
||||||
|
"scheb/2fa-totp": "^8.5",
|
||||||
|
"symfony/console": "7.4.*",
|
||||||
|
"symfony/flex": "^2.10.0",
|
||||||
|
"symfony/form": "7.4.*",
|
||||||
|
"symfony/framework-bundle": "7.4.*",
|
||||||
|
"symfony/http-client": "7.4.*",
|
||||||
|
"symfony/mailer": "7.4.*",
|
||||||
|
"symfony/mercure": "^0.6",
|
||||||
|
"symfony/mercure-bundle": "*",
|
||||||
|
"symfony/monolog-bundle": "^3.8",
|
||||||
|
"symfony/security-bundle": "7.4.*",
|
||||||
|
"symfony/translation": "7.4.*",
|
||||||
|
"symfony/twig-bundle": "7.4.*",
|
||||||
|
"symfony/validator": "7.4.*",
|
||||||
|
"symfony/yaml": "7.4.*",
|
||||||
|
"web-auth/webauthn-framework": "^5.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"firebase/php-jwt": "^7.0",
|
||||||
|
"roave/security-advisories": "dev-master",
|
||||||
|
"symfony/dotenv": "7.4.*",
|
||||||
|
"symfony/maker-bundle": "^1.5",
|
||||||
|
"symfony/stopwatch": "7.4.*",
|
||||||
|
"symfony/web-profiler-bundle": "7.4.*"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"preferred-install": {
|
||||||
|
"*": "dist"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"sort-packages": true,
|
||||||
"roave/security-advisories": "dev-master",
|
"allow-plugins": {
|
||||||
"symfony/dotenv": "^4.0",
|
"symfony/flex": true
|
||||||
"symfony/profiler-pack": "^1.0",
|
|
||||||
"symfony/maker-bundle": "^1.5"
|
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"preferred-install": {
|
|
||||||
"*": "dist"
|
|
||||||
},
|
|
||||||
"sort-packages": true
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"App\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload-dev": {
|
|
||||||
"psr-4": {
|
|
||||||
"App\\Tests\\": "tests/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"replace": {
|
|
||||||
"symfony/polyfill-iconv": "*",
|
|
||||||
"symfony/polyfill-php73": "*",
|
|
||||||
"symfony/polyfill-php72": "*",
|
|
||||||
"symfony/polyfill-php71": "*",
|
|
||||||
"symfony/polyfill-php70": "*",
|
|
||||||
"symfony/polyfill-php56": "*"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"auto-scripts": {
|
|
||||||
"cache:clear": "symfony-cmd",
|
|
||||||
"assets:install --symlink --relative %PUBLIC_DIR%": "symfony-cmd"
|
|
||||||
},
|
|
||||||
"post-install-cmd": [
|
|
||||||
"@auto-scripts"
|
|
||||||
],
|
|
||||||
"post-update-cmd": [
|
|
||||||
"@auto-scripts"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"conflict": {
|
|
||||||
"symfony/symfony": "*"
|
|
||||||
},
|
|
||||||
"extra": {
|
|
||||||
"symfony": {
|
|
||||||
"allow-contrib": false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"App\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"App\\Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"replace": {
|
||||||
|
"symfony/polyfill-iconv": "*",
|
||||||
|
"symfony/polyfill-ctype": "*",
|
||||||
|
"symfony/polyfill-php73": "*",
|
||||||
|
"symfony/polyfill-php72": "*",
|
||||||
|
"symfony/polyfill-php71": "*",
|
||||||
|
"symfony/polyfill-php70": "*",
|
||||||
|
"symfony/polyfill-php56": "*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"auto-scripts": {
|
||||||
|
"cache:clear": "symfony-cmd",
|
||||||
|
"assets:install --symlink --relative %PUBLIC_DIR%": "symfony-cmd"
|
||||||
|
},
|
||||||
|
"post-install-cmd": [
|
||||||
|
"@auto-scripts"
|
||||||
|
],
|
||||||
|
"post-update-cmd": [
|
||||||
|
"@auto-scripts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"symfony/symfony": "*",
|
||||||
|
"doctrine/persistence": "<1.3"
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"symfony": {
|
||||||
|
"allow-contrib": false,
|
||||||
|
"require": "7.4.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11960
composer.lock
generated
11960
composer.lock
generated
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user