33 Commits

Author SHA1 Message Date
John Niang
1bcfc50e78 Add missing ui-path for cd workflow (#57) 2025-06-27 11:18:41 +08:00
John Niang
dfaccb5323 Bump Halo devtools to 0.6.1 (#56) 2025-06-26 11:53:54 +08:00
Ryan Wang
18235095f3 docs: add deprecated information in readme (#55)
Signed-off-by: Ryan Wang <i@ryanc.cc>
2025-06-26 11:53:45 +08:00
John Niang
a91c64bbd5 Make sure the processUiResources task is executed after processResources (#54) 2025-06-23 22:51:58 +08:00
Ryan Wang
c81ec5a2ae chore: update ui template (#53)
Signed-off-by: Ryan Wang <i@ryanc.cc>
2025-06-20 17:00:00 +08:00
John Niang
e31b994a7f Refactor build scripts and workflows (#52)
* Refactor build scripts and workflows

* Add a unit test sample

* chore: bump @halo-dev/ui-plugin-bundler-kit version

Signed-off-by: Ryan Wang <i@ryanc.cc>

---------

Signed-off-by: Ryan Wang <i@ryanc.cc>
Co-authored-by: Ryan Wang <i@ryanc.cc>
2025-06-19 16:50:31 +08:00
Ryan Wang
d7629caa1f chore: remove OWNER file (#47)
移除 OWNER 文件,这对于社区仓库来说是没必要的,如果 Halo 的组织需要可以手动添加。

```release-note
None
```
2024-11-13 10:46:00 +00:00
guqing
5a4a25db25 chore: update project config (#45)
### What this PR does?
更新项目配置

```release-note
None
```
2024-10-15 09:43:21 +00:00
Takagi
0574a0ea32 chore: upgrade devtool version to 0.1.1 (#41)
#### What this PR does / why we need it:

升级 Devtools 版本号至 0.1.1

#### Does this PR introduce a user-facing change?
```release-note
None
```
2024-08-06 05:04:29 +00:00
Ryan Wang
b302131c52 chore: update infrastructure for UI (#39)
更新 UI 部分的基础设施。

1. 更新所有的依赖为最新版本。
2. 添加 `@halo-dev/api-client` 和 `axios` 依赖。
3. pnpm 要求提升至 9。
4. 更新 Node 的 Gradle 插件。

```release-note
None
```
2024-07-02 05:41:22 +00:00
guqing
dad7849964 chore: upgrade devtools version to 0.0.9 (#34) 2024-05-13 12:13:23 +08:00
Ryan Wang
bf58bf0637 chore: add issues field for plugin manifest file (#30)
为插件定义文件添加 issues 字段。

Refer https://github.com/halo-dev/halo/pull/5755

```release-note
None
```
2024-04-26 17:02:54 +00:00
Ryan Wang
c891124c6f chore: update plugin.yaml fields (#29)
适配 https://github.com/halo-dev/halo/pull/4061 的改动。

更新 plugin.yaml 示例,添加 repo 字段。之后,homepage 和 repo 的区别如下:

1. homepage 可用于提供插件官网或者介绍页面,比如 Halo 应用市场的应用页面。
2. repo 仅作为源码仓库地址。

```release-note
None
```
2024-04-16 09:46:07 +00:00
guqing
9f04f1bce7 chore: update workflows config (#28)
### What this PR does?
使用新的  https://github.com/halo-sigs/reusable-workflows 来作为项目 CI ,简化配置后方便插件开发者使用而不必过多修改


```release-note
None
```
2024-04-03 07:04:07 +00:00
Mystery0 M
b3179920ae Upgrade the version of the action steps (#27) 2024-03-07 15:00:57 +08:00
Ryan Wang
e9ef894f21 docs: update README.md (#26)
更新 README 的部分文档指向链接。

/kind documentation

```release-note
None
```
2024-02-18 02:30:15 +00:00
Ryan Wang
e3bf74f8db chore: update project infra (#25)
Signed-off-by: Ryan Wang <i@ryanc.cc>
2024-02-18 10:20:50 +08:00
guqing
6f7d9367cf chore: update project infra config (#24)
### What this PR does?
更新项目配置

```release-note
None
```
2024-01-30 08:54:14 +00:00
Ryan Wang
1182e9497d chore: rename console folder to ui (#23)
Signed-off-by: Ryan Wang <i@ryanc.cc>
2023-12-27 10:27:48 +08:00
yangzidemuou
eb3a128324 Update build.gradle (#21)
解决windows下构建项目出现的编码错误问题 #19 

```release-note
None
```
2023-12-14 07:44:11 +00:00
Ryan Wang
ba473f75e4 docs: refine readme (#18)
完善 README 的开发文档,添加使用 `./gradlew haloServer` 运行插件的方式。

/kind documentation

```release-note
None
```
2023-12-05 09:00:10 +00:00
Ryan Wang
5d1f25e771 chore: use @halo-dev/ui-plugin-bundler-kit to refactor vite config (#17)
使用 Halo 新提供的 `@halo-dev/ui-plugin-bundler-kit` 包重构 Vite 打包配置。

see https://github.com/halo-dev/halo/pull/4916

```release-note
None
```
2023-12-05 04:48:09 +00:00
guqing
a2986389b1 chore: bump devtools version to 0.0.7 (#16)
### What this PR does?
升级 devtools 版本到 0.0.7

```release-note
None
```
2023-09-26 07:58:14 +00:00
Ryan Wang
0dc49191a1 chore: improve the front-end infrastructure (#14) 2023-06-12 10:49:38 +08:00
guqing
a9735da1f6 chore: upgrade devtools version 2023-06-05 11:13:31 +08:00
guqing
b67187a9d2 chore: update pnpm lock file to incompatible with pnpm@8 2023-05-16 10:42:46 +08:00
guqing
339f72d11f chore: add halo plugin devtools dependency (#11)
* chore: add halo plugin devtools dependency

* refacto: upgrade devtools
2023-05-11 20:09:37 +08:00
guqing
b82aa9316b refactor: optimizing project structure and configuration (#10)
### What this PR does?
优化项目配置

- 使用 Halo plugin maven 依赖替换原先 lib 依赖,为了此依赖必须将 gradle 升级至 8 版本
- 将 resources/console 加入到 .gitignore 中
- 增加了 .editconfig 配置

```release-note
None
```
2023-05-11 09:30:20 +00:00
Ryan Wang
6f8b3ddd8a chore: update license in plugin.yaml (#9)
更新 plugin.yaml 中的 License 类型,修复与仓库的 License 文件不对应的问题。

/kind improvement

```release-note
None
```
2023-05-07 13:05:35 +00:00
Ryan Wang
54a6a695c2 docs: update readme (#8)
更新 README。

/kind documentation

```release-note
None
```
2023-02-27 07:26:15 +00:00
guqing
43c88472fc refactor: plugin yaml 2023-02-09 10:48:47 +08:00
Ryan Wang
d4c6584435 perf: refine example page (#7)
完善 Console 端的示例页面,引导开发者查阅下一步的文档。

/kind improvement

<img width="1693" alt="image" src="https://user-images.githubusercontent.com/21301288/217480424-d53fdc3e-56e8-4157-8daf-decd86ae5094.png">

```release-note
None
```
2023-02-09 02:26:13 +00:00
guqing
d37eba069c chore: update plugin config 2023-02-06 15:55:40 +08:00
51 changed files with 5814 additions and 4333 deletions

510
.editorconfig Normal file
View File

@@ -0,0 +1,510 @@
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 120
tab_width = 4
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_wrap_on_typing = false
[*.java]
max_line_length = 100
ij_continuation_indent_size = 4
ij_java_align_consecutive_assignments = false
ij_java_align_consecutive_variable_declarations = false
ij_java_align_group_field_declarations = false
ij_java_align_multiline_annotation_parameters = false
ij_java_align_multiline_array_initializer_expression = false
ij_java_align_multiline_assignment = false
ij_java_align_multiline_binary_operation = false
ij_java_align_multiline_chained_methods = false
ij_java_align_multiline_extends_list = false
ij_java_align_multiline_for = true
ij_java_align_multiline_method_parentheses = false
ij_java_align_multiline_parameters = false
ij_java_align_multiline_parameters_in_calls = false
ij_java_align_multiline_parenthesized_expression = false
ij_java_align_multiline_records = true
ij_java_align_multiline_resources = true
ij_java_align_multiline_ternary_operation = false
ij_java_align_multiline_text_blocks = false
ij_java_align_multiline_throws_list = false
ij_java_align_subsequent_simple_methods = false
ij_java_align_throws_keyword = false
ij_java_annotation_parameter_wrap = off
ij_java_array_initializer_new_line_after_left_brace = false
ij_java_array_initializer_right_brace_on_new_line = false
ij_java_array_initializer_wrap = normal
ij_java_assert_statement_colon_on_next_line = false
ij_java_assert_statement_wrap = normal
ij_java_assignment_wrap = normal
ij_java_binary_operation_sign_on_next_line = true
ij_java_binary_operation_wrap = normal
ij_java_blank_lines_after_anonymous_class_header = 0
ij_java_blank_lines_after_class_header = 0
ij_java_blank_lines_after_imports = 1
ij_java_blank_lines_after_package = 1
ij_java_blank_lines_around_class = 1
ij_java_blank_lines_around_field = 0
ij_java_blank_lines_around_field_in_interface = 0
ij_java_blank_lines_around_initializer = 1
ij_java_blank_lines_around_method = 1
ij_java_blank_lines_around_method_in_interface = 1
ij_java_blank_lines_before_class_end = 0
ij_java_blank_lines_before_imports = 0
ij_java_blank_lines_before_method_body = 0
ij_java_blank_lines_before_package = 1
ij_java_block_brace_style = end_of_line
ij_java_block_comment_at_first_column = false
ij_java_call_parameters_new_line_after_left_paren = false
ij_java_call_parameters_right_paren_on_new_line = false
ij_java_call_parameters_wrap = normal
ij_java_case_statement_on_separate_line = true
ij_java_catch_on_new_line = false
ij_java_class_annotation_wrap = split_into_lines
ij_java_class_brace_style = end_of_line
ij_java_class_count_to_use_import_on_demand = 999
ij_java_class_names_in_javadoc = 1
ij_java_do_not_indent_top_level_class_members = false
ij_java_do_not_wrap_after_single_annotation = false
ij_java_do_while_brace_force = always
ij_java_doc_add_blank_line_after_description = true
ij_java_doc_add_blank_line_after_param_comments = false
ij_java_doc_add_blank_line_after_return = false
ij_java_doc_add_p_tag_on_empty_lines = true
ij_java_doc_align_exception_comments = true
ij_java_doc_align_param_comments = false
ij_java_doc_do_not_wrap_if_one_line = false
ij_java_doc_enable_formatting = true
ij_java_doc_enable_leading_asterisks = true
ij_java_doc_indent_on_continuation = false
ij_java_doc_keep_empty_lines = true
ij_java_doc_keep_empty_parameter_tag = true
ij_java_doc_keep_empty_return_tag = true
ij_java_doc_keep_empty_throws_tag = true
ij_java_doc_keep_invalid_tags = true
ij_java_doc_param_description_on_new_line = false
ij_java_doc_preserve_line_breaks = false
ij_java_doc_use_throws_not_exception_tag = true
ij_java_else_on_new_line = false
ij_java_enum_constants_wrap = normal
ij_java_extends_keyword_wrap = normal
ij_java_extends_list_wrap = normal
ij_java_field_annotation_wrap = split_into_lines
ij_java_finally_on_new_line = false
ij_java_for_brace_force = always
ij_java_for_statement_new_line_after_left_paren = false
ij_java_for_statement_right_paren_on_new_line = false
ij_java_for_statement_wrap = normal
ij_java_generate_final_locals = false
ij_java_generate_final_parameters = false
ij_java_if_brace_force = always
ij_java_imports_layout = $*, |, *, |, *
ij_java_indent_case_from_switch = true
ij_java_insert_inner_class_imports = false
ij_java_insert_override_annotation = true
ij_java_keep_blank_lines_before_right_brace = 2
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
ij_java_keep_blank_lines_in_code = 2
ij_java_keep_blank_lines_in_declarations = 2
ij_java_keep_control_statement_in_one_line = true
ij_java_keep_first_column_comment = true
ij_java_keep_indents_on_empty_lines = false
ij_java_keep_line_breaks = true
ij_java_keep_multiple_expressions_in_one_line = false
ij_java_keep_simple_blocks_in_one_line = false
ij_java_keep_simple_classes_in_one_line = false
ij_java_keep_simple_lambdas_in_one_line = false
ij_java_keep_simple_methods_in_one_line = false
ij_java_label_indent_absolute = false
ij_java_label_indent_size = 0
ij_java_lambda_brace_style = end_of_line
ij_java_layout_static_imports_separately = true
ij_java_line_comment_add_space = true
ij_java_line_comment_at_first_column = false
ij_java_method_annotation_wrap = split_into_lines
ij_java_method_brace_style = end_of_line
ij_java_method_call_chain_wrap = normal
ij_java_method_parameters_new_line_after_left_paren = false
ij_java_method_parameters_right_paren_on_new_line = false
ij_java_method_parameters_wrap = normal
ij_java_modifier_list_wrap = false
ij_java_names_count_to_use_import_on_demand = 999
ij_java_new_line_after_lparen_in_record_header = false
ij_java_parameter_annotation_wrap = normal
ij_java_parentheses_expression_new_line_after_left_paren = false
ij_java_parentheses_expression_right_paren_on_new_line = false
ij_java_place_assignment_sign_on_next_line = false
ij_java_prefer_longer_names = true
ij_java_prefer_parameters_wrap = false
ij_java_record_components_wrap = normal
ij_java_repeat_synchronized = true
ij_java_replace_instanceof_and_cast = false
ij_java_replace_null_check = true
ij_java_replace_sum_lambda_with_method_ref = true
ij_java_resource_list_new_line_after_left_paren = false
ij_java_resource_list_right_paren_on_new_line = false
ij_java_resource_list_wrap = normal
ij_java_rparen_on_new_line_in_record_header = false
ij_java_space_after_closing_angle_bracket_in_type_argument = false
ij_java_space_after_colon = true
ij_java_space_after_comma = true
ij_java_space_after_comma_in_type_arguments = true
ij_java_space_after_for_semicolon = true
ij_java_space_after_quest = true
ij_java_space_after_type_cast = true
ij_java_space_before_annotation_array_initializer_left_brace = false
ij_java_space_before_annotation_parameter_list = false
ij_java_space_before_array_initializer_left_brace = true
ij_java_space_before_catch_keyword = true
ij_java_space_before_catch_left_brace = true
ij_java_space_before_catch_parentheses = true
ij_java_space_before_class_left_brace = true
ij_java_space_before_colon = true
ij_java_space_before_colon_in_foreach = true
ij_java_space_before_comma = false
ij_java_space_before_do_left_brace = true
ij_java_space_before_else_keyword = true
ij_java_space_before_else_left_brace = true
ij_java_space_before_finally_keyword = true
ij_java_space_before_finally_left_brace = true
ij_java_space_before_for_left_brace = true
ij_java_space_before_for_parentheses = true
ij_java_space_before_for_semicolon = false
ij_java_space_before_if_left_brace = true
ij_java_space_before_if_parentheses = true
ij_java_space_before_method_call_parentheses = false
ij_java_space_before_method_left_brace = true
ij_java_space_before_method_parentheses = false
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
ij_java_space_before_quest = true
ij_java_space_before_switch_left_brace = true
ij_java_space_before_switch_parentheses = true
ij_java_space_before_synchronized_left_brace = true
ij_java_space_before_synchronized_parentheses = true
ij_java_space_before_try_left_brace = true
ij_java_space_before_try_parentheses = true
ij_java_space_before_type_parameter_list = false
ij_java_space_before_while_keyword = true
ij_java_space_before_while_left_brace = true
ij_java_space_before_while_parentheses = true
ij_java_space_inside_one_line_enum_braces = false
ij_java_space_within_empty_array_initializer_braces = false
ij_java_space_within_empty_method_call_parentheses = false
ij_java_space_within_empty_method_parentheses = false
ij_java_spaces_around_additive_operators = true
ij_java_spaces_around_assignment_operators = true
ij_java_spaces_around_bitwise_operators = true
ij_java_spaces_around_equality_operators = true
ij_java_spaces_around_lambda_arrow = true
ij_java_spaces_around_logical_operators = true
ij_java_spaces_around_method_ref_dbl_colon = false
ij_java_spaces_around_multiplicative_operators = true
ij_java_spaces_around_relational_operators = true
ij_java_spaces_around_shift_operators = true
ij_java_spaces_around_type_bounds_in_type_parameters = true
ij_java_spaces_around_unary_operator = false
ij_java_spaces_within_angle_brackets = false
ij_java_spaces_within_annotation_parentheses = false
ij_java_spaces_within_array_initializer_braces = false
ij_java_spaces_within_braces = false
ij_java_spaces_within_brackets = false
ij_java_spaces_within_cast_parentheses = false
ij_java_spaces_within_catch_parentheses = false
ij_java_spaces_within_for_parentheses = false
ij_java_spaces_within_if_parentheses = false
ij_java_spaces_within_method_call_parentheses = false
ij_java_spaces_within_method_parentheses = false
ij_java_spaces_within_parentheses = false
ij_java_spaces_within_switch_parentheses = false
ij_java_spaces_within_synchronized_parentheses = false
ij_java_spaces_within_try_parentheses = false
ij_java_spaces_within_while_parentheses = false
ij_java_special_else_if_treatment = true
ij_java_subclass_name_suffix = Impl
ij_java_ternary_operation_signs_on_next_line = true
ij_java_ternary_operation_wrap = normal
ij_java_test_name_suffix = Test
ij_java_throws_keyword_wrap = normal
ij_java_throws_list_wrap = normal
ij_java_use_external_annotations = false
ij_java_use_fq_class_names = false
ij_java_use_relative_indents = false
ij_java_use_single_class_imports = true
ij_java_variable_annotation_wrap = normal
ij_java_visibility = public
ij_java_while_brace_force = always
ij_java_while_on_new_line = false
ij_java_wrap_comments = false
ij_java_wrap_first_method_in_call_chain = false
ij_java_wrap_long_lines = true
[*.properties]
ij_properties_align_group_field_declarations = false
ij_properties_keep_blank_lines = false
ij_properties_key_value_delimiter = equals
ij_properties_spaces_around_key_value_delimiter = false
[.editorconfig]
ij_editorconfig_align_group_field_declarations = false
ij_editorconfig_space_after_colon = false
ij_editorconfig_space_after_comma = true
ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true
[{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.jspx, *.pom, *.rng, *.tagx, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul}]
ij_xml_align_attributes = true
ij_xml_align_text = false
ij_xml_attribute_wrap = normal
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 = false
ij_xml_text_wrap = normal
[{*.bash, *.sh, *.zsh}]
indent_size = 2
tab_width = 2
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
[{*.gant, *.gradle, *.groovy, *.gy}]
ij_groovy_align_group_field_declarations = false
ij_groovy_align_multiline_array_initializer_expression = false
ij_groovy_align_multiline_assignment = false
ij_groovy_align_multiline_binary_operation = false
ij_groovy_align_multiline_chained_methods = false
ij_groovy_align_multiline_extends_list = false
ij_groovy_align_multiline_for = true
ij_groovy_align_multiline_list_or_map = true
ij_groovy_align_multiline_method_parentheses = false
ij_groovy_align_multiline_parameters = true
ij_groovy_align_multiline_parameters_in_calls = false
ij_groovy_align_multiline_resources = true
ij_groovy_align_multiline_ternary_operation = false
ij_groovy_align_multiline_throws_list = false
ij_groovy_align_named_args_in_map = true
ij_groovy_align_throws_keyword = false
ij_groovy_array_initializer_new_line_after_left_brace = false
ij_groovy_array_initializer_right_brace_on_new_line = false
ij_groovy_array_initializer_wrap = off
ij_groovy_assert_statement_wrap = off
ij_groovy_assignment_wrap = off
ij_groovy_binary_operation_wrap = off
ij_groovy_blank_lines_after_class_header = 0
ij_groovy_blank_lines_after_imports = 1
ij_groovy_blank_lines_after_package = 1
ij_groovy_blank_lines_around_class = 1
ij_groovy_blank_lines_around_field = 0
ij_groovy_blank_lines_around_field_in_interface = 0
ij_groovy_blank_lines_around_method = 1
ij_groovy_blank_lines_around_method_in_interface = 1
ij_groovy_blank_lines_before_imports = 1
ij_groovy_blank_lines_before_method_body = 0
ij_groovy_blank_lines_before_package = 0
ij_groovy_block_brace_style = end_of_line
ij_groovy_block_comment_at_first_column = true
ij_groovy_call_parameters_new_line_after_left_paren = false
ij_groovy_call_parameters_right_paren_on_new_line = false
ij_groovy_call_parameters_wrap = off
ij_groovy_catch_on_new_line = false
ij_groovy_class_annotation_wrap = split_into_lines
ij_groovy_class_brace_style = end_of_line
ij_groovy_class_count_to_use_import_on_demand = 5
ij_groovy_do_while_brace_force = never
ij_groovy_else_on_new_line = false
ij_groovy_enum_constants_wrap = off
ij_groovy_extends_keyword_wrap = off
ij_groovy_extends_list_wrap = off
ij_groovy_field_annotation_wrap = split_into_lines
ij_groovy_finally_on_new_line = false
ij_groovy_for_brace_force = never
ij_groovy_for_statement_new_line_after_left_paren = false
ij_groovy_for_statement_right_paren_on_new_line = false
ij_groovy_for_statement_wrap = off
ij_groovy_if_brace_force = never
ij_groovy_import_annotation_wrap = 2
ij_groovy_indent_case_from_switch = true
ij_groovy_indent_label_blocks = true
ij_groovy_insert_inner_class_imports = false
ij_groovy_keep_blank_lines_before_right_brace = 2
ij_groovy_keep_blank_lines_in_code = 2
ij_groovy_keep_blank_lines_in_declarations = 2
ij_groovy_keep_control_statement_in_one_line = true
ij_groovy_keep_first_column_comment = true
ij_groovy_keep_indents_on_empty_lines = false
ij_groovy_keep_line_breaks = true
ij_groovy_keep_multiple_expressions_in_one_line = false
ij_groovy_keep_simple_blocks_in_one_line = false
ij_groovy_keep_simple_classes_in_one_line = true
ij_groovy_keep_simple_lambdas_in_one_line = true
ij_groovy_keep_simple_methods_in_one_line = true
ij_groovy_label_indent_absolute = false
ij_groovy_label_indent_size = 0
ij_groovy_lambda_brace_style = end_of_line
ij_groovy_layout_static_imports_separately = true
ij_groovy_line_comment_add_space = false
ij_groovy_line_comment_at_first_column = true
ij_groovy_method_annotation_wrap = split_into_lines
ij_groovy_method_brace_style = end_of_line
ij_groovy_method_call_chain_wrap = off
ij_groovy_method_parameters_new_line_after_left_paren = false
ij_groovy_method_parameters_right_paren_on_new_line = false
ij_groovy_method_parameters_wrap = off
ij_groovy_modifier_list_wrap = false
ij_groovy_names_count_to_use_import_on_demand = 3
ij_groovy_parameter_annotation_wrap = off
ij_groovy_parentheses_expression_new_line_after_left_paren = false
ij_groovy_parentheses_expression_right_paren_on_new_line = false
ij_groovy_prefer_parameters_wrap = false
ij_groovy_resource_list_new_line_after_left_paren = false
ij_groovy_resource_list_right_paren_on_new_line = false
ij_groovy_resource_list_wrap = off
ij_groovy_space_after_assert_separator = true
ij_groovy_space_after_colon = true
ij_groovy_space_after_comma = true
ij_groovy_space_after_comma_in_type_arguments = true
ij_groovy_space_after_for_semicolon = true
ij_groovy_space_after_quest = true
ij_groovy_space_after_type_cast = true
ij_groovy_space_before_annotation_parameter_list = false
ij_groovy_space_before_array_initializer_left_brace = false
ij_groovy_space_before_assert_separator = false
ij_groovy_space_before_catch_keyword = true
ij_groovy_space_before_catch_left_brace = true
ij_groovy_space_before_catch_parentheses = true
ij_groovy_space_before_class_left_brace = true
ij_groovy_space_before_closure_left_brace = true
ij_groovy_space_before_colon = true
ij_groovy_space_before_comma = false
ij_groovy_space_before_do_left_brace = true
ij_groovy_space_before_else_keyword = true
ij_groovy_space_before_else_left_brace = true
ij_groovy_space_before_finally_keyword = true
ij_groovy_space_before_finally_left_brace = true
ij_groovy_space_before_for_left_brace = true
ij_groovy_space_before_for_parentheses = true
ij_groovy_space_before_for_semicolon = false
ij_groovy_space_before_if_left_brace = true
ij_groovy_space_before_if_parentheses = true
ij_groovy_space_before_method_call_parentheses = false
ij_groovy_space_before_method_left_brace = true
ij_groovy_space_before_method_parentheses = false
ij_groovy_space_before_quest = true
ij_groovy_space_before_switch_left_brace = true
ij_groovy_space_before_switch_parentheses = true
ij_groovy_space_before_synchronized_left_brace = true
ij_groovy_space_before_synchronized_parentheses = true
ij_groovy_space_before_try_left_brace = true
ij_groovy_space_before_try_parentheses = true
ij_groovy_space_before_while_keyword = true
ij_groovy_space_before_while_left_brace = true
ij_groovy_space_before_while_parentheses = true
ij_groovy_space_in_named_argument = true
ij_groovy_space_in_named_argument_before_colon = false
ij_groovy_space_within_empty_array_initializer_braces = false
ij_groovy_space_within_empty_method_call_parentheses = false
ij_groovy_spaces_around_additive_operators = true
ij_groovy_spaces_around_assignment_operators = true
ij_groovy_spaces_around_bitwise_operators = true
ij_groovy_spaces_around_equality_operators = true
ij_groovy_spaces_around_lambda_arrow = true
ij_groovy_spaces_around_logical_operators = true
ij_groovy_spaces_around_multiplicative_operators = true
ij_groovy_spaces_around_regex_operators = true
ij_groovy_spaces_around_relational_operators = true
ij_groovy_spaces_around_shift_operators = true
ij_groovy_spaces_within_annotation_parentheses = false
ij_groovy_spaces_within_array_initializer_braces = false
ij_groovy_spaces_within_braces = true
ij_groovy_spaces_within_brackets = false
ij_groovy_spaces_within_cast_parentheses = false
ij_groovy_spaces_within_catch_parentheses = false
ij_groovy_spaces_within_for_parentheses = false
ij_groovy_spaces_within_gstring_injection_braces = false
ij_groovy_spaces_within_if_parentheses = false
ij_groovy_spaces_within_list_or_map = false
ij_groovy_spaces_within_method_call_parentheses = false
ij_groovy_spaces_within_method_parentheses = false
ij_groovy_spaces_within_parentheses = false
ij_groovy_spaces_within_switch_parentheses = false
ij_groovy_spaces_within_synchronized_parentheses = false
ij_groovy_spaces_within_try_parentheses = false
ij_groovy_spaces_within_tuple_expression = false
ij_groovy_spaces_within_while_parentheses = false
ij_groovy_special_else_if_treatment = true
ij_groovy_ternary_operation_wrap = off
ij_groovy_throws_keyword_wrap = off
ij_groovy_throws_list_wrap = off
ij_groovy_use_flying_geese_braces = false
ij_groovy_use_fq_class_names = false
ij_groovy_use_fq_class_names_in_javadoc = true
ij_groovy_use_relative_indents = false
ij_groovy_use_single_class_imports = true
ij_groovy_variable_annotation_wrap = off
ij_groovy_while_brace_force = never
ij_groovy_while_on_new_line = false
ij_groovy_wrap_long_lines = false
[{*.har, *.json}]
indent_size = 2
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
ij_json_space_after_colon = true
ij_json_space_after_comma = true
ij_json_space_before_colon = true
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}]
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 = normal
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, p
ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot
ij_html_enforce_quotes = false
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, span, 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 = never
ij_html_new_line_before_first_attribute = never
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 = false
ij_html_text_wrap = normal
[{*.yaml, *.yml}]
indent_size = 2
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = true

18
.github/workflows/cd.yaml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: CD
on:
release:
types:
- published
jobs:
cd:
uses: halo-sigs/reusable-workflows/.github/workflows/plugin-cd.yaml@v3
permissions:
contents: write
with:
ui-path: ui
pnpm-version: 9
node-version: 22
java-version: 21
skip-appstore-release: true

18
.github/workflows/ci.yaml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
ci:
uses: halo-sigs/reusable-workflows/.github/workflows/plugin-ci.yaml@v3
with:
ui-path: "ui"
pnpm-version: 9
node-version: 22
java-version: 21

View File

@@ -1,105 +0,0 @@
name: Build Plugin JAR File
on:
push:
branches: [ main ]
release:
types:
- created
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
distribution: 'temurin'
cache: 'gradle'
java-version: 17
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2.0.1
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/console/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install Frontend Dependencies
run: |
./gradlew pnpmInstall
- name: Build with Gradle
run: |
# Set the version with tag name when releasing
version=${{ github.event.release.tag_name }}
version=${version#v}
sed -i "s/version=.*-SNAPSHOT$/version=$version/1" gradle.properties
./gradlew clean build -x test
- name: Archive plugin-starter jar
uses: actions/upload-artifact@v2
with:
name: plugin-starter
path: |
build/libs/*.jar
retention-days: 1
github-release:
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'release'
steps:
- name: Download plugin-starter jar
uses: actions/download-artifact@v2
with:
name: plugin-starter
path: build/libs
- name: Get Name of Artifact
id: get_artifact
run: |
ARTIFACT_PATHNAME=$(ls build/libs/*.jar | head -n 1)
ARTIFACT_NAME=$(basename ${ARTIFACT_PATHNAME})
echo "Artifact pathname: ${ARTIFACT_PATHNAME}"
echo "Artifact name: ${ARTIFACT_NAME}"
echo "ARTIFACT_PATHNAME=${ARTIFACT_PATHNAME}" >> $GITHUB_ENV
echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
echo "RELEASE_ID=${{ github.event.release.id }}" >> $GITHUB_ENV
- name: Upload a Release Asset
uses: actions/github-script@v2
if: github.event_name == 'release'
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
console.log('environment', process.versions);
const fs = require('fs').promises;
const { repo: { owner, repo }, sha } = context;
console.log({ owner, repo, sha });
const releaseId = process.env.RELEASE_ID
const artifactPathName = process.env.ARTIFACT_PATHNAME
const artifactName = process.env.ARTIFACT_NAME
console.log('Releasing', releaseId, artifactPathName, artifactName)
await github.repos.uploadReleaseAsset({
owner, repo,
release_id: releaseId,
name: artifactName,
data: await fs.readFile(artifactPathName)
});

3
.gitignore vendored
View File

@@ -70,5 +70,6 @@ application-local.yml
application-local.yaml
application-local.properties
/admin-frontend/node_modules/
/ui/node_modules/
/workplace/
/src/main/resources/console/

10
OWNERS
View File

@@ -1,10 +0,0 @@
reviewers:
- ruibaby
- guqing
- JohnNiang
- wangzhen-fit2cloud
approvers:
- ruibaby
- guqing
- JohnNiang

410
README.md
View File

@@ -1,393 +1,79 @@
# plugin-starter
Halo 2.0 插件开发快速开始模板WIP
Halo 2.x 插件开发快速开始模板
## 项目结构介绍
```text
.
├── LICENSE
├── README.md
├── console
│   ├── README.md
│   ├── env.d.ts
│   ├── package.json
│   ├── pnpm-lock.yaml
│   ├── src
│   │   ├── assets
│   │   │   └── logo.svg
│   │   ├── components
│   │   │   └── HelloWorld.vue
│   │   ├── index.ts # Console Frontend Entry file
│   │   ├── styles
│   │   │   └── index.css
│   │   └── views
│   │   └── DefaultView.vue # Views Component
│   ├── tsconfig.app.json
│   ├── tsconfig.config.json
│   ├── tsconfig.json
│   ├── tsconfig.vitest.json
│   └── vite.config.ts
├── build.gradle
├── gradle
│   └── wrapper
│   ├── gradle-wrapper.jar
│   └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── lib
│   └── halo-2.0.0-SNAPSHOT-plain.jar
├── settings.gradle
└── src
└── main
├── java
│   └── run
│   └── halo
│   └── starter
│   ├── Apple.java
│   ├── ApplesController.java
│   └── StarterPlugin.java # Main Class
└── resources
├── console
│   ├── main.js # Console Frontend Entry file(production build)
│   └── style.css
├── extensions
│   ├── apple.yaml
│   ├── reverseProxy.yaml # Reverse Proxy Config
│   ├── roleTemplate.yaml # Role Template Config
│   └── settings.yaml # Settings Config
├── plugin.yaml # Plugin Config
└── static
├── image.jpeg
├── some.txt
└── test.html
```
对于以上目录树:
- `console` 插件前端项目目录,为一个 Vue 项目,技术栈为 Vue 3 + Vite其中已经预配置好了构建策略。
- `build`:插件后端构建目录,`build/libs` 下的 jar 包为最终插件产物。
- `lib`:为临时的 Halo 依赖,为了使用 Halo 中提供的类在 `build.gradle`
中作为编译时依赖引入 `compileOnly files("lib/halo-2.0.0-SNAPSHOT-plain.jar")`
,待 `2.0` 正式发布会将其发布到 `maven` 中央仓库,便可通过 `gradle` 依赖。
- `src` 为插件后端源码目录。
- `Apple.java` 为自定义模型。
```java
@GVK(group = "apple.guqing.xyz", kind = "Apple",
version = "v1alpha1", singular = "apple", plural = "apples")
@Data
@EqualsAndHashCode(callSuper = true)
public class Apple extends AbstractExtension {
}
```
关键在于标注 `@GVK` 注解和 `extends AbstractExtension`,当如此定义了一个模型后,插件启动时会根据 `@GVK`
配置自动生成`CRUD`的 `RESTful API`
,以此为例子会生成如下 `APIs`
```http
GET /apis/apple.guqing.xyz/v1alpha1/apples
POST /apis/apple.guqing.xyz/v1alpha1/apples
GET /apis/apple.guqing.xyz/v1alpha1/apples/{name}
PUT /apis/apple.guqing.xyz/v1alpha1/apples/{name}
DELETE /apis/apple.guqing.xyz/v1alpha1/apples/{name}
```
生成规则见:[Halo extension RFC](https://github.com/halo-dev/rfcs/tree/main/extension)
- `TemplatePlugin.java`:插件生命周期入口,它继承 `BasePlugin`,可以通过 `getApplicationContext()`
方法获取到 `SchemeManager`,然后在 `start()`
方法中注册自定义模型,这一步必不可少,所有定义的自定义模型都需要在此注册,并在 `stop()` 生命周期方法中清理资源。
```java
public class StarterPlugin extends BasePlugin {
// ...
@Override
public void start() {
schemeManager.register(Apple.class);
}
@Override
public void stop() {
Scheme scheme = schemeManager.get(Apple.class);
schemeManager.unregister(scheme);
}
// ...
}
```
注意:该类不能标注 `@Component` 等能将其声明为 `Spring Bean` 的注解
- `ApplesController.java`:如果根据模型自动生成的 `CURD RESTful APIs` 无法满足业务需要,可以写常规 `Controller`
来自定义 `APIs`,示例:
```java
@ApiVersion("v1alpha1")
@RestController
@RequestMapping("colors")
public class ApplesController {
@GetMapping
public Mono<String> hello() {
return Mono.just("Hello world");
}
}
```
插件定义 `Controller` 必须要标注 `@ApiVersion` 注解,否则启动时会报错。如果定义了这样的 `Controller`
,插件启动后会生成如下的 `APIs`
```http
GET /api/v1alpha1/plugins/apples/colors
```
生成规则为 `/api/{version}/plugins/{plugin-name}/{mapping-in-class}/{mapping-in-method}`
其中:
- `version`:来自 `@ApiVersion("v1alpha1")` 的
value详情参考[Halo plugin API composition](https://github.com/halo-dev/rfcs/blob/main/plugin/pluggable-design.md#api-%E6%9E%84%E6%88%90%E8%AE%A8%E8%AE%BA)
- `plugin-name`:值来自 `plugin.yaml` 中的 `metadata.name` 属性
- `mapping-in-class`:来自标注在类上的 `@RequestMapping("colors")`
- `mapping-in-method`:来自标注在方法上的 `@GetMapping`
插件还允许使用 `@Service`、`@Component` 注解将其声明为一个 `Spring Bean`,便可通过依赖注入使用 `Spring Bean`,示例:
```java
@Service
public class ColorService {
public String getColor(String appleName) {
return "red";
}
}
@ApiVersion("v1alpha1")
@RestController
@RequestMapping("colors")
public class ApplesController {
private final ColorService colorService;
// 构造器注入,当然也同样允许 @Autowired 注入 和 setter 方法注入
public ApplesController(ColorService colorService) {
this.colorService = colorService;
}
}
```
- `resources`:目录为插件资源目录
- `console` 目录下为插件前端打包后的产物存放目录,固定为 `main.js` 和 `style.css `两个文件
- `extensions` 存放自定义模型资源配置
- `plugin.yaml`为插件描述配置
- `static` 为静态资源示例目录
插件启动时会加载 `extensions` 目录的 `yaml` 保存到数据库,`apple.yaml` 为本项目自定义的模型的数据示例,当启用插件后调用
```text
GET /apis/apple.guqing.xyz/v1alpha1/apples
```
便可查询到 `apple.yaml` 中定义的记录
```json
[
{
"spec": {
"varieties": "Fuji",
"color": "red",
"size": "middle",
"producingArea": "China"
},
"apiVersion": "apple.guqing.xyz/v1alpha1",
"kind": "Apple",
"metadata": {
"name": "Fuji-apple",
"labels": {
"plugin.halo.run/plugin-name": "apples"
},
"version": 0,
"creationTimestamp": "2022-06-24T04:03:22.890741Z"
}
}
]
```
`reverseProxy.yaml` 为 Halo 中提供的模型,表示反向代理,允许插件配置规则代理到插件目录
```yaml
apiVersion: plugin.halo.run/v1alpha1
kind: ReverseProxy
metadata:
name: reverse-proxy-template
rules:
- path: /static/**
file:
directory: static
```
它表示访问 `GET /assets/{plugin-name}/static/**` 时代理访问到插件的 `resources/static` 目录,本插件便可访问到一下静态资源
```
GET /assets/apples/static/image.jpeg
GET /assets/apples/static/some.txt
GET /assets/apples/static/test.html
```
`roleTemplate.yaml` 文件中 `kind: Role` 表示插件允许提供角色模版,定义格式如下:
```yaml
apiVersion: v1alpha1
kind: Role
metadata:
name: a name here
labels:
plugin.halo.run/role-template: "true"
annotations:
plugin.halo.run/module: "module name"
plugin.halo.run/alias-name: "display name"
rules:
# ...
```
必须带有`plugin.halo.run/role-template: "true"` labels表示该角色为角色模版当用户启用插件后创建角色或修改角色时会将其列在权限列表位置。
插件如果不提供角色模版除非是超级管理员否则其他账号没有权限访问,因为 Halo 规定 `/api` 和 `/apis` 开头的 `api`
都需要授权才能访问,因此插件不提供角色模版的自定义资源,就无法将其分配给用户。
更多详情参考:[Halo security RFC](https://github.com/halo-dev/rfcs/blob/main/identity/002-security.md)
> [!WARNING]
> 此项目将在未来被标记为过时,后续建议使用 [halo-dev/create-halo-plugin](https://github.com/halo-dev/create-halo-plugin) 以交互式的方式创建 Halo 插件项目。
## 开发环境
### 环境要求
插件开发的详细文档请查阅:<https://docs.halo.run/developer-guide/plugin/introduction>
- OpenJDK 17
- NodeJS 16+
- pnpm 7+
所需环境:
### 拉取 Halo 相关项目源码
1. JDK 21
2. Node 20
3. pnpm 9
4. Docker (可选)
克隆项目:
```bash
mkdir ./halo-dev
git clone git@github.com:halo-sigs/plugin-starter.git
mkdir ./halo-dev/dev-plugins # 存放插件源码
# 或者当你 fork 之后
git clone git@github.com:{your_github_id}/plugin-starter.git
```
```bash
cd ./halo-dev
cd path/to/plugin-starter
```
git clone https://github.com/halo-dev/halo --branch main
### 运行方式 1推荐
> 此方式需要本地安装 Docker
```bash
# macOS / Linux
./gradlew pnpmInstall
# Windows
./gradlew.bat pnpmInstall
```
```bash
git clone https://github.com/halo-dev/console --branch main
# macOS / Linux
./gradlew haloServer
# Windows
./gradlew.bat haloServer
```
执行此命令后,会自动创建一个 Halo 的 Docker 容器并加载当前的插件,更多文档可查阅:<https://docs.halo.run/developer-guide/plugin/basics/devtools>
### 运行方式 2
> 此方式需要使用源码运行 Halo
编译插件:
```bash
cd ./dev-plugins
# macOS / Linux
./gradlew build
git clone https://github.com/halo-dev/plugin-starter
# Windows
./gradlew.bat build
```
### Halo 配置文件修改
修改 halo/src/main/resources/application-dev.yaml
修改 Halo 配置文件
```yaml
halo:
security:
initializer:
super-admin-username: admin
super-admin-password: P@88w0rd
oauth2:
jwt:
jwsAlgorithm: rs512
public-key-location: classpath:app.pub
private-key-location: classpath:app.key
plugin:
runtime-mode: development # development, deployment
classes-directories:
- "build/classes"
- "build/resources"
lib-directories:
- "libs"
fixedPluginPath:
runtime-mode: development
fixedPluginPath:
- "/path/to/plugin-starter"
```
### 编译插件
下载前端依赖:
```bash
cd ./halo-dev/dev-plugins/plugin-starter
./gradlew.bat pnpmInstall
# or macOS/Linux
./gradlew pnpmInstall
```
构建:
```bash
./gradlew.bat build
# or macOS/Linux
./gradlew build
```
### 启动 Halo
```bash
cd ./halo-dev/halo
./gradlew.bat bootRun --args="--spring.profiles.active=dev"
# or macOS/Linux
./gradlew bootRun --args="--spring.profiles.active=dev"
```
或者在 IntelliJ IDEA 中运行 Application 启动类。但注意需要配置好 `spring.profiles.active` 为 dev。
### 启动 Console
```bash
cd ./halo-dev/console
pnpm install
pnpm build:packages
pnpm dev
```
### 访问后台
在浏览器中访问 https://localhost:3000 即可,登录用户名和密码为上方 `application-dev.yaml` 配置中的 `super-admin-username`
和 `super-admin-password`。
然后在左侧菜单中选择 `插件`,即可查看所有插件的状态。
### 开发
修改前端代码或者后端代码,然后运行 `./gradlew.bat build` 或者 `./gradlew build`macOS/Linux即可构建插件无需重启
Halo。但修改配置文件后需要 build 插件以及重启 Halo。
## 构建生产产物
```
./gradlew -x build
```
然后只需复制例如`build/libs/plugin-starter-0.0.1-SNAPSHOT-plain.jar` 的 `jar` 包即可使用。
最后重启 Halo 项目即可。

View File

@@ -1,63 +1,50 @@
plugins {
id "com.github.node-gradle.node" version "3.3.0"
id "io.github.guqing.plugin-development" version "0.0.6-SNAPSHOT"
id 'java'
id "io.freefair.lombok" version "8.13"
id "run.halo.plugin.devtools" version "0.6.1"
}
group 'run.halo.starter'
sourceCompatibility = JavaVersion.VERSION_17
repositories {
maven { url 'https://s01.oss.sonatype.org/content/repositories/releases' }
maven { url 'https://repo.spring.io/milestone' }
mavenCentral()
}
jar {
enabled = true
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
manifest.attributes(
'Plugin-Version': "${project.version}",
)
from {
configurations.runtimeClasspath.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
dependencies {
compileOnly platform("run.halo.dependencies:halo-dependencies:1.0.0")
implementation platform('run.halo.tools.platform:plugin:2.21.0')
compileOnly 'run.halo.app:api'
compileOnly files("lib/halo-2.0.0-SNAPSHOT-plain.jar")
compileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
testImplementation 'run.halo.app:api'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
test {
useJUnitPlatform()
}
node {
nodeProjectDir = file("${project.projectDir}/console")
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
task buildFrontend(type: NpxTask) {
command = 'pnpm'
args = ['build']
tasks.withType(JavaCompile).configureEach {
options.encoding = "UTF-8"
options.release = 21
}
task pnpmInstall(type: NpxTask) {
command = "pnpm"
args = ["install"]
tasks.register('processUiResources', Copy) {
from project(':ui').layout.buildDirectory.dir('dist')
into layout.buildDirectory.dir('resources/main/console')
dependsOn project(':ui').tasks.named('assemble')
shouldRunAfter tasks.named('processResources')
}
build {
// build frontend before build
tasks.getByName('compileJava').dependsOn('buildFrontend')
tasks.getByName("buildFrontend").dependsOn("pnpmInstall")
tasks.named('classes') {
dependsOn tasks.named('processUiResources')
}
halo {
version = '2.21'
}

View File

@@ -1,57 +0,0 @@
# Halo 前后端服务的 Docker Compose
通过 Docker Compose 的方式启动 Halo 前后端服务快速查看插件效果。
## 开发指引
### 环境要求
- OpenJDK 17
- NodeJS 16+
- pnpm 7+
- Docker
- Docker Compose
### 编译插件
下载前端依赖:
```bash
cd ./plugin-starter
./gradlew.bat pnpmInstall
# or macOS/Linux
./gradlew pnpmInstall
```
构建:
```bash
./gradlew.bat build
# or macOS/Linux
./gradlew build
```
### 启动 Halo
```bash
cd plugin-starter/compose
docker-compose up -d
docker-compose ps
```
### 访问后台
在浏览器中访问 <https://localhost:8090/console> 即可,登录用户名和密码为当前目录中 `application-dev.yaml` 配置中的 `super-admin-username`
`super-admin-password`
然后在左侧菜单中选择 `插件`,即可查看所有插件的状态。
### 开发
修改前端代码或者后端代码,然后运行 `./gradlew.bat build` 或者 `./gradlew build`macOS/Linux即可构建插件无需重启
Halo。但修改配置文件后需要 build 插件以及重启 Halo。

View File

@@ -1,40 +0,0 @@
server:
port: 8090
spring:
profiles:
active: dev
halo:
external-url: http://localhost:8090/
security:
initializer:
super-admin-username: admin
super-admin-password: P@88w0rd
oauth2:
jwt:
jwsAlgorithm: rs512
public-key-location: classpath:app.pub
private-key-location: classpath:app.key
plugin:
runtime-mode: development # development, deployment
classes-directories:
- "build/classes"
- "build/resources"
lib-directories:
- "libs"
fixedPluginPath:
- "/plugin"
springdoc:
api-docs:
enabled: true
swagger-ui:
enabled: true
show-login-endpoint: false
show-actuator: false
use-management-port: false
management:
endpoints:
web:
exposure:
include: health,info,metrics,startup,shutdown
server:
port: 8090

View File

@@ -1,14 +0,0 @@
services:
halo-next:
image: halohub/halo-dev:main
ports:
- 8090:8090
environment:
- SPRING_CONFIG_ADDITIONAL_LOCATION=optional:file:/etc/config/
volumes:
- ./application.yaml:/etc/config/application.yaml
- ../:/plugin
networks:
- halo
networks:
halo: {}

View File

@@ -1,12 +0,0 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = true

View File

@@ -1,15 +0,0 @@
/* eslint-env node */
require("@rushstack/eslint-patch/modern-module-resolution");
module.exports = {
"root": true,
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/eslint-config-typescript/recommended",
"@vue/eslint-config-prettier"
],
"env": {
"vue/setup-compiler-macros": true
}
}

6
console/env.d.ts vendored
View File

@@ -1,6 +0,0 @@
/// <reference types="vite/client" />
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}

View File

@@ -1,38 +0,0 @@
{
"name": "@halo-dev/plugin-starter",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "vite build --watch",
"build": "vite build",
"preview": "vite preview --port 4173",
"test:unit": "vitest --environment jsdom",
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
},
"dependencies": {
"@halo-dev/components": "^1.0.0",
"@halo-dev/console-shared": "^2.0.0",
"vue": "^3.2.41"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.2.0",
"@types/jsdom": "^20.0.0",
"@types/node": "^16.18.0",
"@vitejs/plugin-vue": "^3.1.2",
"@vitejs/plugin-vue-jsx": "^2.0.1",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.2",
"@vue/test-utils": "^2.2.0",
"@vue/tsconfig": "^0.1.3",
"eslint": "^8.26.0",
"eslint-plugin-vue": "^9.6.0",
"jsdom": "^19.0.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.7.1",
"typescript": "~4.7.4",
"vite": "^3.1.8",
"vitest": "^0.24.3",
"vue-tsc": "^1.0.9"
}
}

3449
console/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +0,0 @@
<script lang="ts">
import {defineComponent, ref} from "vue";
export default defineComponent({
name: "HelloWorld",
setup() {
const msg = ref("Hello World");
return {
msg,
};
},
});
</script>
<template>
<div>{{ msg }}</div>
</template>

View File

@@ -1,42 +0,0 @@
import {definePlugin} from "@halo-dev/console-shared";
import DefaultView from "./views/DefaultView.vue";
import {IconGrid} from "@halo-dev/components";
import "./styles/index.css";
import {markRaw} from "vue";
export default definePlugin({
name: "PluginStarter",
components: [],
routes: [
{
parentName: "Root",
route:
{
path: "/hello-world",
children: [
{
path: "",
name: "HelloWorld",
component: DefaultView,
meta: {
permissions: ["plugin:apples:view"],
title: "HelloWorld",
searchable: true,
menu: {
name: "迁移",
group: "From PluginStarter",
icon: markRaw(IconGrid),
priority: 0,
},
},
},
],
},
}
],
extensionPoints: {},
activated() {
},
deactivated() {
},
});

View File

@@ -1,3 +0,0 @@
.title {
font-size: 20px;
}

View File

@@ -1,22 +0,0 @@
<script lang="ts">
import {defineComponent} from "vue";
import logo from "../assets/logo.svg";
export default defineComponent({
name: "DefaultView",
setup() {
return {
logo,
};
},
});
</script>
<template>
<div class="title">Hello World</div>
<img v-permission="['plugin:apples:view']" :src="logo" alt="logo"/>
</template>
<style scoped>
.title {
color: red;
}
</style>

View File

@@ -1,12 +0,0 @@
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["./env.d.ts", "./src/**/*", "./src/**/*.vue"],
"exclude": ["./src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

View File

@@ -1,8 +0,0 @@
{
"extends": "@vue/tsconfig/tsconfig.node.json",
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
"compilerOptions": {
"composite": true,
"types": ["node"]
}
}

View File

@@ -1,44 +0,0 @@
import { fileURLToPath, URL } from "url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), vueJsx()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
build: {
outDir: fileURLToPath(
new URL("../src/main/resources/console", import.meta.url)
),
emptyOutDir: true,
lib: {
entry: "src/index.ts",
name: "PluginStarter",
formats: ["iife"],
fileName: () => "main.js",
},
rollupOptions: {
external: [
"vue",
"@halo-dev/shared",
"@halo-dev/components",
"vue-router",
],
output: {
globals: {
vue: "Vue",
"vue-router": "VueRouter",
"@halo-dev/components": "HaloComponents",
"@halo-dev/console-shared": "HaloConsoleShared",
},
extend: true,
},
},
},
});

Binary file not shown.

View File

@@ -1,5 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

18
gradlew vendored
View File

@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,10 +80,10 @@ do
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
@@ -143,12 +143,16 @@ fi
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -205,6 +209,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.

15
gradlew.bat vendored
View File

@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

Binary file not shown.

View File

@@ -1,11 +1,7 @@
pluginManagement {
repositories {
maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
maven { url 'https://maven.aliyun.com/repository/spring-plugin' }
maven { url 'https://repo.spring.io/milestone' }
gradlePluginPortal()
}
}
rootProject.name = 'plugin-starter'
include 'ui'

View File

@@ -1,20 +1,22 @@
package run.halo.starter;
import org.pf4j.PluginWrapper;
import org.springframework.stereotype.Component;
import run.halo.app.extension.Scheme;
import run.halo.app.extension.SchemeManager;
import run.halo.app.plugin.BasePlugin;
import run.halo.app.plugin.PluginContext;
/**
* <p>Plugin main class to manage the lifecycle of the plugin.</p>
* <p>This class must be public and have a public constructor.</p>
* <p>Only one main class extending {@link BasePlugin} is allowed per plugin.</p>
*
* @author guqing
* @since 2.0.0
* @since 1.0.0
*/
@Component
public class StarterPlugin extends BasePlugin {
public StarterPlugin(PluginWrapper wrapper) {
super(wrapper);
public StarterPlugin(PluginContext pluginContext) {
super(pluginContext);
}
@Override

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
.title[data-v-e98165fe]{color:red}.title{font-size:20px}

BIN
src/main/resources/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -1,23 +1,22 @@
# Refer https://docs.halo.run/developer-guide/plugin/basics/manifest
apiVersion: plugin.halo.run/v1alpha1
kind: Plugin
metadata:
# The name defines how the plugin is invoked,A unique name
name: PluginStarter
# The name defines how the plugin is invoked, A unique name
name: starter
spec:
enabled: true
# 'version' is a valid semantic version string (see semver.org).
version: 1.0.0-SNAPSHOT
requires: "*"
requires: ">=2.21.0"
author:
name: Halo OSS Team
name: Halo
website: https://github.com/halo-dev
logo: https://halo.run/logo
settingName: starter-settings
configMapName: starter-settings
# 'homepage' usually links to the GitHub repository of the plugin
homepage: https://github.com/halo-dev/plugin-starter
# 'displayName' explains what the plugin does in only a few words
logo: logo.png
homepage: https://github.com/halo-dev/plugin-starter#readme
repo: https://github.com/halo-dev/plugin-starter
issues: https://github.com/halo-dev/plugin-starter/issues
displayName: "插件快速开始模板"
description: "这是一个插件快速开始模板"
license:
- name: "MIT"
- name: "GPL-3.0"
url: "https://github.com/halo-dev/plugin-starter/blob/main/LICENSE"

View File

@@ -0,0 +1,24 @@
package run.halo.starter;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import run.halo.app.plugin.PluginContext;
@ExtendWith(MockitoExtension.class)
class StarterPluginTest {
@Mock
PluginContext context;
@InjectMocks
StarterPlugin plugin;
@Test
void contextLoads() {
plugin.start();
plugin.stop();
}
}

9
ui/.editorconfig Normal file
View File

@@ -0,0 +1,9 @@
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
charset = utf-8
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
max_line_length = 100

View File

7
ui/.prettierrc.json Normal file
View File

@@ -0,0 +1,7 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2
}

33
ui/build.gradle Normal file
View File

@@ -0,0 +1,33 @@
plugins {
id 'base'
id "com.github.node-gradle.node" version "7.1.0"
}
group 'run.halo.starter.ui'
tasks.register('buildFrontend', PnpmTask) {
group = 'build'
description = 'Builds the UI project using pnpm.'
args = ['build']
dependsOn tasks.named('pnpmInstall')
inputs.dir(layout.projectDirectory.dir('src'))
inputs.files(fileTree(
dir: layout.projectDirectory,
includes: ['*.cjs', '*.ts', '*.js', '*.json', '*.yaml']))
outputs.dir(layout.buildDirectory.dir('dist'))
}
tasks.register('pnpmCheck', PnpmTask) {
group = 'verification'
description = 'Runs unit tests using pnpm.'
args = ['test:unit']
dependsOn tasks.named('pnpmInstall')
}
tasks.named('check') {
dependsOn tasks.named('pnpmCheck')
}
tasks.named('assemble') {
dependsOn tasks.named('buildFrontend')
}

2
ui/env.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
/// <reference types="vite/client" />
/// <reference types="unplugin-icons/types/vue" />

30
ui/eslint.config.ts Normal file
View File

@@ -0,0 +1,30 @@
import { globalIgnores } from 'eslint/config'
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
import pluginVue from 'eslint-plugin-vue'
import pluginVitest from '@vitest/eslint-plugin'
import pluginOxlint from 'eslint-plugin-oxlint'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
// import { configureVueProject } from '@vue/eslint-config-typescript'
// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
export default defineConfigWithVueTs(
{
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}'],
},
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
pluginVue.configs['flat/essential'],
vueTsConfigs.recommended,
{
...pluginVitest.configs.recommended,
files: ['src/**/__tests__/*'],
},
...pluginOxlint.configs['flat/recommended'],
skipFormatting,
)

50
ui/package.json Normal file
View File

@@ -0,0 +1,50 @@
{
"type": "module",
"scripts": {
"build": "run-p type-check \"build-only {@}\" --",
"build-only": "vite build",
"dev": "vite build --watch --mode=development",
"lint:oxlint": "oxlint . --fix -D correctness --ignore-path .gitignore",
"lint:eslint": "eslint . --fix",
"lint": "run-s lint:*",
"prettier": "prettier --write src/",
"test:unit": "vitest --passWithNoTests",
"type-check": "vue-tsc --build"
},
"dependencies": {
"@halo-dev/api-client": "^2.21.0",
"@halo-dev/components": "^2.21.0",
"@halo-dev/console-shared": "^2.21.0",
"axios": "^1.7.2",
"canvas-confetti": "^1.9.3",
"vue": "^3.5.17"
},
"devDependencies": {
"@halo-dev/ui-plugin-bundler-kit": "^2.21.1",
"@iconify/json": "^2.2.350",
"@tsconfig/node20": "^20.1.6",
"@types/canvas-confetti": "^1.9.0",
"@types/jsdom": "^21.1.7",
"@types/node": "^20.19.1",
"@vitejs/plugin-vue": "^5.2.4",
"@vitest/eslint-plugin": "^1.2.7",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.5.1",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.7.0",
"eslint": "^9.29.0",
"eslint-plugin-oxlint": "^0.16.12",
"eslint-plugin-vue": "~10.0.1",
"jsdom": "^26.1.0",
"npm-run-all2": "^7.0.2",
"oxlint": "^0.16.12",
"prettier": "3.5.3",
"sass": "^1.89.2",
"typescript": "~5.8.3",
"unplugin-icons": "^22.1.0",
"vite": "^5.3.2",
"vitest": "^3.2.4",
"vue-tsc": "^2.2.10"
},
"packageManager": "pnpm@9.15.9"
}

4744
ui/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

29
ui/src/index.ts Normal file
View File

@@ -0,0 +1,29 @@
import { definePlugin } from '@halo-dev/console-shared'
import HomeView from './views/HomeView.vue'
import { IconPlug } from '@halo-dev/components'
import { markRaw } from 'vue'
export default definePlugin({
components: {},
routes: [
{
parentName: 'Root',
route: {
path: '/example',
name: 'Example',
component: HomeView,
meta: {
title: '示例页面',
searchable: true,
menu: {
name: '示例页面',
group: '示例分组',
icon: markRaw(IconPlug),
priority: 0,
},
},
},
},
],
extensionPoints: {},
})

166
ui/src/views/HomeView.vue Normal file
View File

@@ -0,0 +1,166 @@
<script setup lang="ts">
import confetti from 'canvas-confetti'
import { onMounted } from 'vue'
import RiShareCircleLine from '~icons/ri/share-circle-line'
import RiCodeBoxLine from '~icons/ri/code-box-line'
import RiBookReadLine from '~icons/ri/book-read-line'
import RiComputerLine from '~icons/ri/computer-line'
import RiArrowRightSLine from '~icons/ri/arrow-right-s-line'
onMounted(() => {
confetti({
particleCount: 100,
spread: 70,
origin: { y: 0.6, x: 0.58 },
})
})
</script>
<template>
<section id="plugin-starter">
<div class="wrapper">
<span class="title"> 你已经成功运行起了插件 </span>
<span class="message">你可以点击下方文档继续下一步</span>
<div class="docs">
<a
href="https://docs.halo.run/developer-guide/plugin/publish"
class="docs__box"
target="_blank"
>
<h2 class="docs__box-title"><RiShareCircleLine />发布一个插件</h2>
<span class="docs__box-message"> 了解如何与我们的社区分享您的扩展 </span>
<span class="docs__box-arrow">
<RiArrowRightSLine />
</span>
</a>
<a
href="https://docs.halo.run/category/%E5%9F%BA%E7%A1%80"
class="docs__box"
target="_blank"
>
<h2 class="docs__box-title"><RiComputerLine />基础概览</h2>
<span class="docs__box-message"> 了解插件的项目结构生命周期资源配置等 </span>
<span class="docs__box-arrow">
<RiArrowRightSLine />
</span>
</a>
<a
href="https://docs.halo.run/developer-guide/plugin/examples/todolist"
class="docs__box group"
target="_blank"
>
<h2 class="docs__box-title"><RiBookReadLine />示例插件</h2>
<span class="docs__box-message">帮助你从 0 1 完成一个插件</span>
<span class="docs__box-arrow">
<RiArrowRightSLine />
</span>
</a>
<a
href="https://docs.halo.run/category/api-%E5%8F%82%E8%80%83"
class="docs__box"
target="_blank"
>
<h2 class="docs__box-title"><RiCodeBoxLine />API 参考</h2>
<span class="docs__box-message">插件中的 API 列表</span>
<span class="docs__box-arrow">
<RiArrowRightSLine />
</span>
</a>
</div>
</div>
</section>
</template>
<style lang="scss" scoped>
#plugin-starter {
height: 100vh;
background-color: #f8fafc;
}
.wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
gap: 1.5rem;
.title {
font-weight: 700;
font-size: 1.25rem;
line-height: 1.75rem;
}
.message {
font-size: 0.875rem;
line-height: 1.25rem;
color: #4b5563;
}
.docs {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 1rem;
max-width: 48rem;
.docs__box {
background-color: #fff;
border-radius: 0.375rem;
padding: 0.75rem;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 300ms;
cursor: pointer;
filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));
&:hover {
box-shadow:
0 0 0 0px #fff,
0 0 0 1px rgb(59 130 246 / 0.5),
0 0 #0000;
}
.docs__box-title {
display: flex;
flex-direction: row;
font-size: 1.125rem;
line-height: 1.75rem;
font-weight: 700;
margin-bottom: 2rem;
gap: 0.5rem;
align-items: center;
}
.docs__box-message {
font-size: 0.875rem;
line-height: 1.25rem;
color: #4b5563;
}
.docs__box-arrow {
pointer-events: none;
position: absolute;
top: 1rem;
right: 1rem;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
color: #d1d5db;
}
&:hover {
.docs__box-arrow {
color: #9ca3af;
transform: translate(00.375rem, 0) rotate(0) skewX(0) skewY(0) scaleX(1) scaleY(1);
}
}
}
}
@media (min-width: 640px) {
.docs {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
}
</style>

12
ui/tsconfig.app.json Normal file
View File

@@ -0,0 +1,12 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"paths": {
"@/*": ["./src/*"]
}
}
}

View File

@@ -2,7 +2,7 @@
"files": [],
"references": [
{
"path": "./tsconfig.config.json"
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"

12
ui/tsconfig.node.json Normal file
View File

@@ -0,0 +1,12 @@
{
"extends": "@tsconfig/node20/tsconfig.json",
"include": ["vite.config.*", "vitest.config.*"],
"compilerOptions": {
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

View File

@@ -1,8 +1,10 @@
{
"extends": "./tsconfig.app.json",
"include": ["src/**/__tests__/*", "env.d.ts"],
"exclude": [],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo",
"lib": [],
"types": ["node", "jsdom"]
}

25
ui/vite.config.ts Normal file
View File

@@ -0,0 +1,25 @@
import { fileURLToPath, URL } from 'url'
import { viteConfig } from '@halo-dev/ui-plugin-bundler-kit'
import Icons from 'unplugin-icons/vite'
import { configDefaults } from 'vitest/config'
// For more info,
// please see https://github.com/halo-dev/halo/tree/main/ui/packages/ui-plugin-bundler-kit
export default viteConfig({
vite: {
plugins: [Icons({ compiler: 'vue3' })],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
// If you don't use Vitest, you can remove the following configuration
test: {
environment: 'jsdom',
exclude: [...configDefaults.exclude, 'e2e/**'],
root: fileURLToPath(new URL('./', import.meta.url)),
},
},
})