mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: support toggle list (#3016)
This commit is contained in:
parent
a00dd5498e
commit
2da37122e4
@ -7,6 +7,7 @@ import 'document_with_database_test.dart' as document_with_database_test;
|
||||
import 'document_with_inline_math_equation_test.dart'
|
||||
as document_with_inline_math_equation_test;
|
||||
import 'document_with_inline_page_test.dart' as document_with_inline_page_test;
|
||||
import 'document_with_toggle_list_test.dart' as document_with_toggle_list_test;
|
||||
import 'edit_document_test.dart' as document_edit_test;
|
||||
|
||||
void startTesting() {
|
||||
@ -19,4 +20,5 @@ void startTesting() {
|
||||
document_with_inline_page_test.main();
|
||||
document_with_inline_math_equation_test.main();
|
||||
document_with_cover_image_test.main();
|
||||
document_with_toggle_list_test.main();
|
||||
}
|
||||
|
@ -0,0 +1,237 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/ime.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('toggle list in document', () {
|
||||
void expectToggleListOpened() {
|
||||
expect(find.byIcon(Icons.arrow_drop_down), findsOneWidget);
|
||||
expect(find.byIcon(Icons.arrow_right), findsNothing);
|
||||
}
|
||||
|
||||
void expectToggleListClosed() {
|
||||
expect(find.byIcon(Icons.arrow_drop_down), findsNothing);
|
||||
expect(find.byIcon(Icons.arrow_right), findsOneWidget);
|
||||
}
|
||||
|
||||
testWidgets('convert > to toggle list, and click the icon to close it',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
// insert a toggle list
|
||||
const text = 'This is a toggle list sample';
|
||||
await tester.ime.insertText('> $text');
|
||||
await tester.editor.updateSelection(
|
||||
Selection.single(
|
||||
path: [0],
|
||||
startOffset: 0,
|
||||
endOffset: text.length,
|
||||
),
|
||||
);
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
final toggleList = editorState.document.nodeAtPath([0])!;
|
||||
expect(
|
||||
toggleList.type,
|
||||
ToggleListBlockKeys.type,
|
||||
);
|
||||
expect(
|
||||
toggleList.attributes[ToggleListBlockKeys.collapsed],
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
toggleList.delta!.toPlainText(),
|
||||
text,
|
||||
);
|
||||
|
||||
// Press the arrow down key to move the cursor to the next line
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.arrowDown,
|
||||
);
|
||||
const text2 = 'This is a child node';
|
||||
await tester.ime.insertText(text2);
|
||||
expect(find.text(text2, findRichText: true), findsOneWidget);
|
||||
|
||||
// Click the toggle list icon to close it
|
||||
final toggleListIcon = find.byIcon(Icons.arrow_drop_down);
|
||||
await tester.tapButton(toggleListIcon);
|
||||
|
||||
// expect the toggle list to be closed
|
||||
expect(find.text(text2, findRichText: true), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('press enter key when the toggle list is closed',
|
||||
(tester) async {
|
||||
// if the toggle list is closed, press enter key will insert a new toggle list after it
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
// insert a toggle list
|
||||
const text = 'Hello AppFlowy';
|
||||
await tester.ime.insertText('> $text');
|
||||
|
||||
// Click the toggle list icon to close it
|
||||
final toggleListIcon = find.byIcon(Icons.arrow_drop_down);
|
||||
await tester.tapButton(toggleListIcon);
|
||||
|
||||
// Press the enter key
|
||||
await tester.editor.updateSelection(
|
||||
Selection.collapse(
|
||||
[0],
|
||||
'Hello '.length,
|
||||
),
|
||||
);
|
||||
await tester.ime.insertCharacter('\n');
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
final node0 = editorState.getNodeAtPath([0])!;
|
||||
final node1 = editorState.getNodeAtPath([1])!;
|
||||
|
||||
expect(node0.type, ToggleListBlockKeys.type);
|
||||
expect(node0.attributes[ToggleListBlockKeys.collapsed], true);
|
||||
expect(node0.delta!.toPlainText(), 'Hello ');
|
||||
expect(node1.type, ToggleListBlockKeys.type);
|
||||
expect(node1.delta!.toPlainText(), 'AppFlowy');
|
||||
});
|
||||
|
||||
testWidgets('press enter key when the toggle list is open', (tester) async {
|
||||
// if the toggle list is open, press enter key will insert a new paragraph inside it
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
// insert a toggle list
|
||||
const text = 'Hello AppFlowy';
|
||||
await tester.ime.insertText('> $text');
|
||||
|
||||
// Press the enter key
|
||||
await tester.editor.updateSelection(
|
||||
Selection.collapse(
|
||||
[0],
|
||||
'Hello '.length,
|
||||
),
|
||||
);
|
||||
await tester.ime.insertCharacter('\n');
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
final node0 = editorState.getNodeAtPath([0])!;
|
||||
final node00 = editorState.getNodeAtPath([0, 0])!;
|
||||
final node1 = editorState.getNodeAtPath([1]);
|
||||
|
||||
expect(node0.type, ToggleListBlockKeys.type);
|
||||
expect(node0.attributes[ToggleListBlockKeys.collapsed], false);
|
||||
expect(node0.delta!.toPlainText(), 'Hello ');
|
||||
expect(node00.type, ParagraphBlockKeys.type);
|
||||
expect(node00.delta!.toPlainText(), 'AppFlowy');
|
||||
expect(node1, isNull);
|
||||
});
|
||||
|
||||
testWidgets('clear the format if toggle list if empty', (tester) async {
|
||||
// if the toggle list is open, press enter key will insert a new paragraph inside it
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
// insert a toggle list
|
||||
await tester.ime.insertText('> ');
|
||||
|
||||
// Press the enter key
|
||||
// Click the toggle list icon to close it
|
||||
final toggleListIcon = find.byIcon(Icons.arrow_drop_down);
|
||||
await tester.tapButton(toggleListIcon);
|
||||
|
||||
await tester.editor.updateSelection(
|
||||
Selection.collapse(
|
||||
[0],
|
||||
0,
|
||||
),
|
||||
);
|
||||
await tester.ime.insertCharacter('\n');
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
final node0 = editorState.getNodeAtPath([0])!;
|
||||
|
||||
expect(node0.type, ParagraphBlockKeys.type);
|
||||
});
|
||||
|
||||
testWidgets('use cmd/ctrl + enter to open/close the toggle list',
|
||||
(tester) async {
|
||||
// if the toggle list is open, press enter key will insert a new paragraph inside it
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
// insert a toggle list
|
||||
await tester.ime.insertText('> Hello');
|
||||
|
||||
expectToggleListOpened();
|
||||
|
||||
await tester.editor.updateSelection(
|
||||
Selection.collapse(
|
||||
[0],
|
||||
0,
|
||||
),
|
||||
);
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.enter,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
);
|
||||
|
||||
expectToggleListClosed();
|
||||
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.enter,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
);
|
||||
|
||||
expectToggleListOpened();
|
||||
});
|
||||
});
|
||||
}
|
@ -37,6 +37,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
final inlinePageReferenceService = InlinePageReferenceService();
|
||||
|
||||
final List<CommandShortcutEvent> commandShortcutEvents = [
|
||||
toggleToggleListCommand,
|
||||
...codeBlockCommands,
|
||||
...standardCommandShortcutEvents,
|
||||
];
|
||||
@ -68,7 +69,8 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
...codeBlockCharacterEvents,
|
||||
|
||||
// toggle list
|
||||
// formatGreaterToToggleList,
|
||||
formatGreaterToToggleList,
|
||||
insertChildNodeInsideToggleList,
|
||||
|
||||
// customize the slash menu command
|
||||
customSlashCommand(
|
||||
@ -107,6 +109,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
indentableBlockTypes.add(ToggleListBlockKeys.type);
|
||||
slashMenuItems = _customSlashMenuItems();
|
||||
|
||||
effectiveScrollController = widget.scrollController ?? ScrollController();
|
||||
@ -286,6 +289,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
TodoListBlockKeys.type,
|
||||
CalloutBlockKeys.type,
|
||||
OutlineBlockKeys.type,
|
||||
ToggleListBlockKeys.type,
|
||||
];
|
||||
|
||||
final supportAlignBuilderType = [
|
||||
@ -313,7 +317,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
final top = builder.configuration.padding(context.node).top;
|
||||
final padding = context.node.type == HeadingBlockKeys.type
|
||||
? EdgeInsets.only(top: top + 8.0)
|
||||
: EdgeInsets.only(top: top);
|
||||
: EdgeInsets.only(top: top + 2.0);
|
||||
return Padding(
|
||||
padding: padding,
|
||||
child: BlockActionList(
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ToggleListBlockKeys {
|
||||
const ToggleListBlockKeys._();
|
||||
@ -11,24 +10,35 @@ class ToggleListBlockKeys {
|
||||
/// The content of a code block.
|
||||
///
|
||||
/// The value is a String.
|
||||
static const String delta = 'delta';
|
||||
static const String delta = blockComponentDelta;
|
||||
|
||||
static const String backgroundColor = blockComponentBackgroundColor;
|
||||
|
||||
static const String textDirection = blockComponentTextDirection;
|
||||
|
||||
/// The value is a bool.
|
||||
static const String collapsed = 'collapsed';
|
||||
}
|
||||
|
||||
Node toggleListBlockNode({
|
||||
String? text,
|
||||
Delta? delta,
|
||||
bool collapsed = false,
|
||||
String? textDirection,
|
||||
Attributes? attributes,
|
||||
Iterable<Node>? children,
|
||||
}) {
|
||||
final attributes = {
|
||||
ToggleListBlockKeys.delta: (delta ?? Delta()).toJson(),
|
||||
ToggleListBlockKeys.collapsed: collapsed,
|
||||
};
|
||||
return Node(
|
||||
type: ToggleListBlockKeys.type,
|
||||
attributes: attributes,
|
||||
children: [paragraphNode()],
|
||||
attributes: {
|
||||
ToggleListBlockKeys.collapsed: collapsed,
|
||||
ToggleListBlockKeys.delta:
|
||||
(delta ?? (Delta()..insert(text ?? ''))).toJson(),
|
||||
if (attributes != null) ...attributes,
|
||||
if (textDirection != null)
|
||||
ToggleListBlockKeys.textDirection: textDirection,
|
||||
},
|
||||
children: children ?? [paragraphNode()],
|
||||
);
|
||||
}
|
||||
|
||||
@ -86,7 +96,9 @@ class _ToggleListBlockComponentWidgetState
|
||||
SelectableMixin,
|
||||
DefaultSelectableMixin,
|
||||
BlockComponentConfigurable,
|
||||
BlockComponentBackgroundColorMixin {
|
||||
BlockComponentBackgroundColorMixin,
|
||||
NestedBlockComponentStatefulWidgetMixin,
|
||||
BlockComponentTextDirectionMixin {
|
||||
// the key used to forward focus to the richtext child
|
||||
@override
|
||||
final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
|
||||
@ -105,63 +117,65 @@ class _ToggleListBlockComponentWidgetState
|
||||
@override
|
||||
Node get node => widget.node;
|
||||
|
||||
bool get collapsed => node.attributes[ToggleListBlockKeys.collapsed] ?? false;
|
||||
@override
|
||||
EdgeInsets get indentPadding => configuration.indentPadding(
|
||||
node,
|
||||
calculateTextDirection(
|
||||
defaultTextDirection: Directionality.maybeOf(context),
|
||||
),
|
||||
);
|
||||
|
||||
late final editorState = context.read<EditorState>();
|
||||
bool get collapsed => node.attributes[ToggleListBlockKeys.collapsed] ?? false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return collapsed
|
||||
? buildToggleListBlockComponent(context)
|
||||
: buildToggleListBlockComponentWithChildren(context);
|
||||
? buildComponent(context)
|
||||
: buildComponentWithChildren(context);
|
||||
}
|
||||
|
||||
Widget buildToggleListBlockComponentWithChildren(BuildContext context) {
|
||||
return Container(
|
||||
color: backgroundColor,
|
||||
child: NestedListWidget(
|
||||
children: editorState.renderer.buildList(
|
||||
context,
|
||||
widget.node.children,
|
||||
),
|
||||
child: buildToggleListBlockComponent(context),
|
||||
),
|
||||
@override
|
||||
Widget buildComponent(BuildContext context) {
|
||||
final textDirection = calculateTextDirection(
|
||||
defaultTextDirection: Directionality.maybeOf(context),
|
||||
);
|
||||
}
|
||||
|
||||
// build the richtext child
|
||||
Widget buildToggleListBlockComponent(BuildContext context) {
|
||||
Widget child = Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// the emoji picker button for the note
|
||||
FlowyIconButton(
|
||||
width: 24.0,
|
||||
icon: Icon(
|
||||
collapsed ? Icons.arrow_right : Icons.arrow_drop_down,
|
||||
),
|
||||
onPressed: onCollapsed,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 4.0,
|
||||
),
|
||||
Expanded(
|
||||
child: AppFlowyRichText(
|
||||
key: forwardKey,
|
||||
node: widget.node,
|
||||
editorState: editorState,
|
||||
placeholderText: placeholderText,
|
||||
lineHeight: 1.5,
|
||||
textSpanDecorator: (textSpan) => textSpan.updateTextStyle(
|
||||
textStyle,
|
||||
Widget child = Container(
|
||||
color: backgroundColor,
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// the emoji picker button for the note
|
||||
FlowyIconButton(
|
||||
width: 24.0,
|
||||
icon: Icon(
|
||||
collapsed ? Icons.arrow_right : Icons.arrow_drop_down,
|
||||
),
|
||||
placeholderTextSpanDecorator: (textSpan) =>
|
||||
textSpan.updateTextStyle(
|
||||
placeholderTextStyle,
|
||||
onPressed: onCollapsed,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 4.0,
|
||||
),
|
||||
Expanded(
|
||||
child: AppFlowyRichText(
|
||||
key: forwardKey,
|
||||
node: widget.node,
|
||||
editorState: editorState,
|
||||
placeholderText: placeholderText,
|
||||
lineHeight: 1.5,
|
||||
textSpanDecorator: (textSpan) => textSpan.updateTextStyle(
|
||||
textStyle,
|
||||
),
|
||||
placeholderTextSpanDecorator: (textSpan) =>
|
||||
textSpan.updateTextStyle(
|
||||
placeholderTextStyle,
|
||||
),
|
||||
textDirection: textDirection,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
child = Padding(
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/toggle/toggle_block_component.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const _greater = '>';
|
||||
|
||||
@ -22,3 +23,107 @@ CharacterShortcutEvent formatGreaterToToggleList = CharacterShortcutEvent(
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/// Press enter key to insert child node inside the toggle list
|
||||
///
|
||||
/// - support
|
||||
/// - desktop
|
||||
/// - mobile
|
||||
/// - web
|
||||
CharacterShortcutEvent insertChildNodeInsideToggleList = CharacterShortcutEvent(
|
||||
key: 'insert child node inside toggle list',
|
||||
character: '\n',
|
||||
handler: (editorState) async {
|
||||
final selection = editorState.selection;
|
||||
if (selection == null || !selection.isCollapsed) {
|
||||
return false;
|
||||
}
|
||||
final node = editorState.getNodeAtPath(selection.start.path);
|
||||
final delta = node?.delta;
|
||||
if (node == null ||
|
||||
node.type != ToggleListBlockKeys.type ||
|
||||
delta == null) {
|
||||
return false;
|
||||
}
|
||||
final slicedDelta = delta.slice(selection.start.offset);
|
||||
final transaction = editorState.transaction;
|
||||
final collapsed = node.attributes[ToggleListBlockKeys.collapsed] as bool;
|
||||
if (collapsed) {
|
||||
// if the delta is empty, clear the format
|
||||
if (delta.isEmpty) {
|
||||
transaction
|
||||
..insertNode(
|
||||
selection.start.path.next,
|
||||
paragraphNode(),
|
||||
)
|
||||
..deleteNode(node)
|
||||
..afterSelection = Selection.collapse(selection.start.path, 0);
|
||||
} else {
|
||||
// insert a toggle list block below the current toggle list block
|
||||
transaction
|
||||
..deleteText(node, selection.startIndex, slicedDelta.length)
|
||||
..insertNode(
|
||||
selection.start.path.next,
|
||||
toggleListBlockNode(collapsed: true, delta: slicedDelta),
|
||||
)
|
||||
..afterSelection = Selection.collapse(selection.start.path.next, 0);
|
||||
}
|
||||
} else {
|
||||
// insert a paragraph block inside the current toggle list block
|
||||
transaction
|
||||
..deleteText(node, selection.startIndex, slicedDelta.length)
|
||||
..insertNode(
|
||||
selection.start.path + [0],
|
||||
paragraphNode(delta: slicedDelta),
|
||||
)
|
||||
..afterSelection = Selection.collapse(selection.start.path + [0], 0);
|
||||
}
|
||||
await editorState.apply(transaction);
|
||||
return true;
|
||||
},
|
||||
);
|
||||
|
||||
/// cmd/ctrl + enter to close or open the toggle list
|
||||
///
|
||||
/// - support
|
||||
/// - desktop
|
||||
/// - web
|
||||
///
|
||||
|
||||
// toggle the todo list
|
||||
final CommandShortcutEvent toggleToggleListCommand = CommandShortcutEvent(
|
||||
key: 'toggle the toggle list',
|
||||
command: 'ctrl+enter',
|
||||
macOSCommand: 'cmd+enter',
|
||||
handler: _toggleToggleListCommandHandler,
|
||||
);
|
||||
|
||||
CommandShortcutEventHandler _toggleToggleListCommandHandler = (editorState) {
|
||||
if (PlatformExtension.isMobile) {
|
||||
assert(false, 'enter key is not supported on mobile platform.');
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
final selection = editorState.selection;
|
||||
if (selection == null) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
final nodes = editorState.getNodesInSelection(selection);
|
||||
if (nodes.isEmpty || nodes.length > 1) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
final node = nodes.first;
|
||||
if (node.type != ToggleListBlockKeys.type) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
final collapsed = node.attributes[ToggleListBlockKeys.collapsed] as bool;
|
||||
final transaction = editorState.transaction;
|
||||
transaction.updateNode(node, {
|
||||
ToggleListBlockKeys.collapsed: !collapsed,
|
||||
});
|
||||
transaction.afterSelection = selection;
|
||||
editorState.apply(transaction);
|
||||
return KeyEventResult.handled;
|
||||
};
|
||||
|
@ -45,7 +45,7 @@ dependencies:
|
||||
appflowy_editor:
|
||||
git:
|
||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||
ref: 33b18d9
|
||||
ref: 023f3c8
|
||||
appflowy_popover:
|
||||
path: packages/appflowy_popover
|
||||
|
||||
|
7
frontend/flowy-server-config/Cargo.lock
generated
Normal file
7
frontend/flowy-server-config/Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "flowy-server-config"
|
||||
version = "0.1.0"
|
Loading…
Reference in New Issue
Block a user