mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: support inserting local image (#2913)
This commit is contained in:
parent
c870dbeac4
commit
11d05b303d
@ -452,6 +452,12 @@
|
||||
"center": "Center",
|
||||
"right": "Right",
|
||||
"defaultColor": "Default"
|
||||
},
|
||||
"image": {
|
||||
"copiedToPasteBoard": "The image link has been copied to the clipboard"
|
||||
},
|
||||
"outline": {
|
||||
"addHeadingToCreateOutline": "Add headings to create a table of contents."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -118,7 +118,7 @@ const _sample = r'''
|
||||
---
|
||||
[] Highlight any text, and use the editing menu to _style_ **your** writing `however` you ~~like.~~
|
||||
|
||||
[] Type / followed by /bullet or /num to create a list.
|
||||
[] Type followed by bullet or num to create a list.
|
||||
|
||||
[x] Click `+ New Page` button at the bottom of your sidebar to add a new page.
|
||||
|
||||
|
@ -9,7 +9,7 @@ import 'package:appflowy/plugins/document/application/doc_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-document2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pbserver.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart'
|
||||
show EditorState, LogLevel, TransactionTime;
|
||||
show EditorState, LogLevel, TransactionTime, Selection, paragraphNode;
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -155,6 +155,9 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
return;
|
||||
}
|
||||
await _transactionAdapter.apply(event.$2, editorState);
|
||||
|
||||
// check if the document is empty.
|
||||
applyRules();
|
||||
});
|
||||
|
||||
// output the log from the editor when debug mode
|
||||
@ -166,6 +169,39 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> applyRules() async {
|
||||
ensureAtLeastOneParagraphExists();
|
||||
ensureLastNodeIsEditable();
|
||||
}
|
||||
|
||||
Future<void> ensureLastNodeIsEditable() async {
|
||||
final editorState = this.editorState;
|
||||
if (editorState == null) {
|
||||
return;
|
||||
}
|
||||
final document = editorState.document;
|
||||
final lastNode = document.root.children.lastOrNull;
|
||||
if (lastNode == null || lastNode.delta == null) {
|
||||
final transaction = editorState.transaction;
|
||||
transaction.insertNode([document.root.children.length], paragraphNode());
|
||||
await editorState.apply(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> ensureAtLeastOneParagraphExists() async {
|
||||
final editorState = this.editorState;
|
||||
if (editorState == null) {
|
||||
return;
|
||||
}
|
||||
final document = editorState.document;
|
||||
if (document.root.children.isEmpty) {
|
||||
final transaction = editorState.transaction;
|
||||
transaction.insertNode([0], paragraphNode());
|
||||
transaction.afterSelection = Selection.collapse([0], 0);
|
||||
await editorState.apply(transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
@ -1,10 +1,9 @@
|
||||
import 'package:appflowy/plugins/document/application/doc_bloc.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/option_action.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_list.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/inline_page/inline_page_reference.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@ -55,20 +54,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
highlightColorItem,
|
||||
];
|
||||
|
||||
late final slashMenuItems = [
|
||||
inlineGridMenuItem(documentBloc),
|
||||
referencedGridMenuItem,
|
||||
inlineBoardMenuItem(documentBloc),
|
||||
referencedBoardMenuItem,
|
||||
inlineCalendarMenuItem(documentBloc),
|
||||
referencedCalendarMenuItem,
|
||||
calloutItem,
|
||||
mathEquationItem,
|
||||
codeBlockItem,
|
||||
emojiMenuItem,
|
||||
autoGeneratorMenuItem,
|
||||
outlineItem,
|
||||
];
|
||||
late final List<SelectionMenuItem> slashMenuItems;
|
||||
|
||||
late final Map<String, BlockComponentBuilder> blockComponentBuilders =
|
||||
_customAppFlowyBlockComponentBuilders();
|
||||
@ -119,6 +105,9 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
slashMenuItems = _customSlashMenuItems();
|
||||
|
||||
effectiveScrollController = widget.scrollController ?? ScrollController();
|
||||
}
|
||||
|
||||
@ -219,6 +208,15 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
),
|
||||
ImageBlockKeys.type: ImageBlockComponentBuilder(
|
||||
configuration: configuration,
|
||||
showMenu: true,
|
||||
menuBuilder: (node, state) => Positioned(
|
||||
top: 0,
|
||||
right: 10,
|
||||
child: ImageMenu(
|
||||
node: node,
|
||||
state: state,
|
||||
),
|
||||
),
|
||||
),
|
||||
DatabaseBlockKeys.gridType: DatabaseViewBlockComponentBuilder(
|
||||
configuration: configuration,
|
||||
@ -254,8 +252,15 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
),
|
||||
AutoCompletionBlockKeys.type: AutoCompletionBlockComponentBuilder(),
|
||||
SmartEditBlockKeys.type: SmartEditBlockComponentBuilder(),
|
||||
ToggleListBlockKeys.type: ToggleListBlockComponentBuilder(),
|
||||
OutlineBlockKeys.type: OutlineBlockComponentBuilder(),
|
||||
ToggleListBlockKeys.type: ToggleListBlockComponentBuilder(
|
||||
configuration: configuration,
|
||||
),
|
||||
OutlineBlockKeys.type: OutlineBlockComponentBuilder(
|
||||
configuration: configuration.copyWith(
|
||||
placeholderTextStyle: (_) =>
|
||||
styleCustomizer.outlineBlockPlaceholderStyleBuilder(),
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
final builders = {
|
||||
@ -325,6 +330,34 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
return builders;
|
||||
}
|
||||
|
||||
List<SelectionMenuItem> _customSlashMenuItems() {
|
||||
final items = [...standardSelectionMenuItems];
|
||||
final imageItem = items.firstWhereOrNull(
|
||||
(element) => element.name == AppFlowyEditorLocalizations.current.image,
|
||||
);
|
||||
if (imageItem != null) {
|
||||
final imageItemIndex = items.indexOf(imageItem);
|
||||
if (imageItemIndex != -1) {
|
||||
items[imageItemIndex] = customImageMenuItem;
|
||||
}
|
||||
}
|
||||
return [
|
||||
...items,
|
||||
inlineGridMenuItem(documentBloc),
|
||||
referencedGridMenuItem,
|
||||
inlineBoardMenuItem(documentBloc),
|
||||
referencedBoardMenuItem,
|
||||
inlineCalendarMenuItem(documentBloc),
|
||||
referencedCalendarMenuItem,
|
||||
calloutItem,
|
||||
outlineItem,
|
||||
mathEquationItem,
|
||||
codeBlockItem,
|
||||
emojiMenuItem,
|
||||
autoGeneratorMenuItem,
|
||||
];
|
||||
}
|
||||
|
||||
(bool, Selection?) _computeAutoFocusParameters() {
|
||||
if (widget.editorState.document.isEmpty) {
|
||||
return (true, Selection.collapse([0], 0));
|
||||
|
@ -0,0 +1,290 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide FlowySvg;
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/ignore_parent_gesture.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ImageMenu extends StatefulWidget {
|
||||
const ImageMenu({
|
||||
super.key,
|
||||
required this.node,
|
||||
required this.state,
|
||||
});
|
||||
|
||||
final Node node;
|
||||
final ImageBlockComponentWidgetState state;
|
||||
|
||||
@override
|
||||
State<ImageMenu> createState() => _ImageMenuState();
|
||||
}
|
||||
|
||||
class _ImageMenuState extends State<ImageMenu> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Container(
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.cardColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
blurRadius: 5,
|
||||
spreadRadius: 1,
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
),
|
||||
],
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const HSpace(4),
|
||||
_ImageCopyLinkButton(
|
||||
onTap: copyImageLink,
|
||||
),
|
||||
const HSpace(4),
|
||||
_ImageAlignButton(
|
||||
node: widget.node,
|
||||
state: widget.state,
|
||||
),
|
||||
const _Divider(),
|
||||
_ImageDeleteButton(
|
||||
onTap: () => deleteImage(),
|
||||
),
|
||||
const HSpace(4),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void copyImageLink() {
|
||||
final url = widget.node.attributes[ImageBlockKeys.url];
|
||||
if (url != null) {
|
||||
Clipboard.setData(ClipboardData(text: url));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: FlowyText(
|
||||
LocaleKeys.document_plugins_image_copiedToPasteBoard.tr(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteImage() async {
|
||||
final node = widget.node;
|
||||
final editorState = context.read<EditorState>();
|
||||
final transaction = editorState.transaction;
|
||||
transaction.deleteNode(node);
|
||||
transaction.afterSelection = null;
|
||||
await editorState.apply(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
class _ImageCopyLinkButton extends StatelessWidget {
|
||||
const _ImageCopyLinkButton({
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: const FlowySvg(
|
||||
name: 'editor/copy',
|
||||
size: Size.square(16),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ImageAlignButton extends StatefulWidget {
|
||||
const _ImageAlignButton({
|
||||
required this.node,
|
||||
required this.state,
|
||||
});
|
||||
|
||||
final Node node;
|
||||
final ImageBlockComponentWidgetState state;
|
||||
|
||||
@override
|
||||
State<_ImageAlignButton> createState() => _ImageAlignButtonState();
|
||||
}
|
||||
|
||||
const interceptorKey = 'image-align';
|
||||
|
||||
class _ImageAlignButtonState extends State<_ImageAlignButton> {
|
||||
final gestureInterceptor = SelectionGestureInterceptor(
|
||||
key: interceptorKey,
|
||||
canTap: (details) => false,
|
||||
);
|
||||
|
||||
String get align => widget.node.attributes['align'] ?? 'center';
|
||||
final popoverController = PopoverController();
|
||||
late final EditorState editorState;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
editorState = context.read<EditorState>();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
allowMenuClose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IgnoreParentGestureWidget(
|
||||
child: AppFlowyPopover(
|
||||
onClose: allowMenuClose,
|
||||
controller: popoverController,
|
||||
windowPadding: const EdgeInsets.all(0),
|
||||
margin: const EdgeInsets.all(0),
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
offset: const Offset(0, 10),
|
||||
child: buildAlignIcon(),
|
||||
popupBuilder: (_) {
|
||||
preventMenuClose();
|
||||
return _AlignButtons(
|
||||
onAlignChanged: onAlignChanged,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onAlignChanged(String align) {
|
||||
popoverController.close();
|
||||
|
||||
final transaction = editorState.transaction;
|
||||
transaction.updateNode(widget.node, {
|
||||
ImageBlockKeys.align: align,
|
||||
});
|
||||
editorState.apply(transaction);
|
||||
|
||||
allowMenuClose();
|
||||
}
|
||||
|
||||
void preventMenuClose() {
|
||||
widget.state.alwaysShowMenu = true;
|
||||
editorState.service.selectionService.registerGestureInterceptor(
|
||||
gestureInterceptor,
|
||||
);
|
||||
}
|
||||
|
||||
void allowMenuClose() {
|
||||
widget.state.alwaysShowMenu = false;
|
||||
editorState.service.selectionService.unregisterGestureInterceptor(
|
||||
interceptorKey,
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildAlignIcon() {
|
||||
return FlowySvg(
|
||||
name: 'editor/align/$align',
|
||||
size: const Size.square(16),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AlignButtons extends StatelessWidget {
|
||||
const _AlignButtons({
|
||||
required this.onAlignChanged,
|
||||
});
|
||||
|
||||
final Function(String align) onAlignChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 32,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const HSpace(4),
|
||||
_AlignButton(
|
||||
align: 'left',
|
||||
onTap: () => onAlignChanged('left'),
|
||||
),
|
||||
const _Divider(),
|
||||
_AlignButton(
|
||||
align: 'left',
|
||||
onTap: () => onAlignChanged('center'),
|
||||
),
|
||||
const _Divider(),
|
||||
_AlignButton(
|
||||
align: 'left',
|
||||
onTap: () => onAlignChanged('right'),
|
||||
),
|
||||
const HSpace(4),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AlignButton extends StatelessWidget {
|
||||
const _AlignButton({
|
||||
required this.align,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final String align;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: FlowySvg(
|
||||
name: 'editor/align/$align',
|
||||
size: const Size.square(16),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ImageDeleteButton extends StatelessWidget {
|
||||
const _ImageDeleteButton({
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: const FlowySvg(
|
||||
name: 'editor/delete',
|
||||
size: Size.square(16),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Divider extends StatelessWidget {
|
||||
const _Divider();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Container(
|
||||
width: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
final customImageMenuItem = SelectionMenuItem(
|
||||
name: AppFlowyEditorLocalizations.current.image,
|
||||
icon: (editorState, isSelected, style) => SelectionMenuIconWidget(
|
||||
name: 'image',
|
||||
isSelected: isSelected,
|
||||
style: style,
|
||||
),
|
||||
keywords: ['image', 'picture', 'img', 'photo'],
|
||||
handler: (editorState, menuService, context) {
|
||||
final container = Overlay.of(context);
|
||||
showImageMenu(
|
||||
container,
|
||||
editorState,
|
||||
menuService,
|
||||
onInsertImage: (url) async {
|
||||
// if the url is http, we can insert it directly
|
||||
// otherwise, if it's a file url, we need to copy the file to the app's document directory
|
||||
|
||||
final regex = RegExp('^(http|https)://');
|
||||
if (regex.hasMatch(url)) {
|
||||
await editorState.insertImageNode(url);
|
||||
} else {
|
||||
final path = await getIt<ApplicationDataStorage>().getPath();
|
||||
final imagePath = p.join(
|
||||
path,
|
||||
'images',
|
||||
);
|
||||
try {
|
||||
// create the directory if not exists
|
||||
final directory = Directory(imagePath);
|
||||
if (!directory.existsSync()) {
|
||||
await directory.create(recursive: true);
|
||||
}
|
||||
final copyToPath = p.join(
|
||||
imagePath,
|
||||
'${uuid()}${p.extension(url)}',
|
||||
);
|
||||
await File(url).copy(
|
||||
copyToPath,
|
||||
);
|
||||
await editorState.insertImageNode(copyToPath);
|
||||
} catch (e) {
|
||||
Log.error('cannot copy image file', e);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
@ -120,6 +120,15 @@ class _OutlineBlockWidgetState extends State<OutlineBlockWidget>
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
if (children.isEmpty) {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
LocaleKeys.document_plugins_outline_addHeadingToCreateOutline.tr(),
|
||||
style: configuration.placeholderTextStyle(node),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
|
||||
@ -184,7 +193,7 @@ class OutlineItemWidget extends StatelessWidget {
|
||||
|
||||
extension on Node {
|
||||
double get leftIndent {
|
||||
assert(type != HeadingBlockKeys.type);
|
||||
assert(type == HeadingBlockKeys.type);
|
||||
if (type != HeadingBlockKeys.type) {
|
||||
return 0.0;
|
||||
}
|
||||
|
@ -16,3 +16,7 @@ export 'openai/widgets/smart_edit_toolbar_item.dart';
|
||||
export 'toggle/toggle_block_component.dart';
|
||||
export 'toggle/toggle_block_shortcut_event.dart';
|
||||
export 'outline/outline_block_component.dart';
|
||||
export 'image/image_menu.dart';
|
||||
export 'image/image_selection_menu.dart';
|
||||
export 'actions/option_action.dart';
|
||||
export 'actions/block_action_list.dart';
|
||||
|
@ -127,6 +127,17 @@ class EditorStyleCustomizer {
|
||||
);
|
||||
}
|
||||
|
||||
TextStyle outlineBlockPlaceholderStyleBuilder() {
|
||||
final theme = Theme.of(context);
|
||||
final fontSize = context.read<DocumentAppearanceCubit>().state.fontSize;
|
||||
return TextStyle(
|
||||
fontFamily: 'poppins',
|
||||
fontSize: fontSize,
|
||||
height: 1.5,
|
||||
color: theme.colorScheme.onBackground.withOpacity(0.6),
|
||||
);
|
||||
}
|
||||
|
||||
SelectionMenuStyle selectionMenuStyleBuilder() {
|
||||
final theme = Theme.of(context);
|
||||
return SelectionMenuStyle(
|
||||
|
@ -25,6 +25,11 @@ class DocumentAppearanceCubit extends Cubit<DocumentAppearance> {
|
||||
void fetch() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final fontSize = prefs.getDouble(_kDocumentAppearanceFontSize) ?? 16.0;
|
||||
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
fontSize: fontSize,
|
||||
@ -35,6 +40,11 @@ class DocumentAppearanceCubit extends Cubit<DocumentAppearance> {
|
||||
void syncFontSize(double fontSize) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
prefs.setDouble(_kDocumentAppearanceFontSize, fontSize);
|
||||
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
fontSize: fontSize,
|
||||
|
@ -53,9 +53,9 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: "250b1a5"
|
||||
resolved-ref: "250b1a59856b337fc2d4b26a1dabdec265e80acf"
|
||||
url: "https://github.com/AppFlowy-IO/appflowy-editor"
|
||||
ref: "572a174"
|
||||
resolved-ref: "572a174892267e2f78f9c3d7f1fe4ca71c9be0db"
|
||||
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
|
||||
source: git
|
||||
version: "1.0.4"
|
||||
appflowy_popover:
|
||||
|
@ -45,9 +45,8 @@ dependencies:
|
||||
# appflowy_editor: ^1.0.4
|
||||
appflowy_editor:
|
||||
git:
|
||||
url: https://github.com/AppFlowy-IO/appflowy-editor
|
||||
ref: 250b1a5
|
||||
|
||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||
ref: 572a174
|
||||
appflowy_popover:
|
||||
path: packages/appflowy_popover
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user