Toggle navigation
Toggle navigation
This project
Loading...
Sign in
flutter_package
/
gpt_markdown
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
saminsohag
2024-12-12 20:53:54 +0600
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
c3dc6e6195acdaac38695cb17e8aac82996dfeb8
c3dc6e61
1 parent
a91f040a
theme extantions added
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
640 additions
and
228 deletions
gpt_markdown/CHANGELOG.md
gpt_markdown/example/lib/main.dart
gpt_markdown/example/lib/selectable_adapter.dart
gpt_markdown/example/pubspec.lock
gpt_markdown/example/pubspec.yaml
gpt_markdown/lib/markdown_component.dart
gpt_markdown/lib/theme.dart
gpt_markdown/pubspec.yaml
gpt_markdown/CHANGELOG.md
View file @
c3dc6e6
## 0.1.11
*
`GptMarkdownTheme` and `GptMarkdownThemeData`
classes added.
## 0.1.10
*
components are now selectable.
...
...
gpt_markdown/example/lib/main.dart
View file @
c3dc6e6
...
...
@@ -4,7 +4,9 @@ import 'package:desktop_drop/desktop_drop.dart';
import
'package:flutter/material.dart'
;
import
'package:gpt_markdown/gpt_markdown.dart'
;
import
'package:flutter_math_fork/flutter_math.dart'
;
import
'package:gpt_markdown/theme.dart'
;
import
'package:watcher/watcher.dart'
;
import
'selectable_adapter.dart'
;
void
main
(
)
{
runApp
(
const
MyApp
());
...
...
@@ -31,10 +33,14 @@ class _MyAppState extends State<MyApp> {
colorSchemeSeed:
Colors
.
blue
,
),
darkTheme:
ThemeData
(
useMaterial3:
true
,
brightness:
Brightness
.
dark
,
colorSchemeSeed:
Colors
.
blue
,
),
useMaterial3:
true
,
brightness:
Brightness
.
dark
,
colorSchemeSeed:
Colors
.
blue
,
extensions:
[
GptMarkdownThemeData
(
highlightColor:
Colors
.
red
,
),
]),
home:
MyHomePage
(
title:
'GptMarkdown'
,
onPressed:
()
{
...
...
@@ -147,241 +153,254 @@ Markdown and LaTeX can be powerful tools for formatting text and mathematical ex
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: [
IconButton(
onPressed: () {
setState(() {
selectable = !selectable;
});
},
icon: Icon(
Icons.select_all_outlined,
color: selectable
? Theme.of(context).colorScheme.onSurfaceVariant
: Theme.of(context).colorScheme.onSurface.withOpacity(0.38),
),
),
IconButton(
onPressed: () {
setState(() {
_direction = TextDirection.values[(_direction.index + 1) % 2];
});
},
icon: const [Text("LTR"), Text("RTL")][_direction.index],
),
IconButton(
onPressed: widget.onPressed,
icon: const Icon(Icons.sunny),
),
IconButton(
onPressed: () => setState(() {
writingMod = !writingMod;
}),
icon:
Icon(writingMod ? Icons.arrow_drop_down : Icons.arrow_drop_up),
),
],
return GptMarkdownTheme(
gptThemeData: GptMarkdownTheme.of(context).copyWith(
highlightColor: Colors.purple,
),
body: DropTarget(
onDragDone: (details) {
var files = details.files;
if (files.length != 1) {
return;
}
var file = files[0];
String path = file.path;
this.file = File(path);
load();
},
child: Stack(
children: [
Column(
children: [
Expanded(
child: ListView(
children: [
AnimatedBuilder(
animation: _controller,
builder: (context, _) {
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: Theme.of(context).colorScheme.outline),
),
child: Theme(
data: Theme.of(context),
// .copyWith(
// textTheme: const TextTheme(
// // For H1.
// headlineLarge: TextStyle(fontSize: 55),
// // For H2.
// headlineMedium: TextStyle(fontSize: 45),
// // For H3.
// headlineSmall: TextStyle(fontSize: 35),
// // For H4.
// titleLarge: TextStyle(fontSize: 25),
// // For H5.
// titleMedium: TextStyle(fontSize: 15),
// // For H6.
// titleSmall: TextStyle(fontSize: 10),
// ),
// ),
child: Builder(
builder: (context) {
Widget child = TexMarkdown(
_controller.text,
textDirection: _direction,
onLinkTab: (url, title) {
debugPrint(url);
debugPrint(title);
},
textAlign: TextAlign.justify,
textScaler: const TextScaler.linear(1),
style: const TextStyle(
fontSize: 15,
),
latexWorkaround: (tex) {
List<String> stack = [];
tex = tex.splitMapJoin(
RegExp(r"
\\
text
\
{|
\
{|
\
}|
\
_"),
onMatch: (p) {
String input = p[0] ?? "";
if (input == r"
\t
ext{") {
stack.add(input);
}
if (stack.isNotEmpty) {
if (input == r"{") {
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: [
IconButton(
onPressed: () {
setState(() {
selectable = !selectable;
});
},
icon: Icon(
Icons.select_all_outlined,
color: selectable
? Theme.of(context).colorScheme.onSurfaceVariant
: Theme.of(context).colorScheme.onSurface.withOpacity(0.38),
),
),
IconButton(
onPressed: () {
setState(() {
_direction = TextDirection.values[(_direction.index + 1) % 2];
});
},
icon: const [Text("LTR"), Text("RTL")][_direction.index],
),
IconButton(
onPressed: widget.onPressed,
icon: const Icon(Icons.sunny),
),
IconButton(
onPressed: () => setState(() {
writingMod = !writingMod;
}),
icon: Icon(
writingMod ? Icons.arrow_drop_down : Icons.arrow_drop_up),
),
],
),
body: DropTarget(
onDragDone: (details) {
var files = details.files;
if (files.length != 1) {
return;
}
var file = files[0];
String path = file.path;
this.file = File(path);
load();
},
child: Stack(
children: [
Column(
children: [
Expanded(
child: ListView(
children: [
AnimatedBuilder(
animation: _controller,
builder: (context, _) {
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
border: Border.all(
width: 1,
color:
Theme.of(context).colorScheme.outline),
),
child: Theme(
data: Theme.of(context),
// .copyWith(
// textTheme: const TextTheme(
// // For H1.
// headlineLarge: TextStyle(fontSize: 55),
// // For H2.
// headlineMedium: TextStyle(fontSize: 45),
// // For H3.
// headlineSmall: TextStyle(fontSize: 35),
// // For H4.
// titleLarge: TextStyle(fontSize: 25),
// // For H5.
// titleMedium: TextStyle(fontSize: 15),
// // For H6.
// titleSmall: TextStyle(fontSize: 10),
// ),
// ),
child: Builder(
builder: (context) {
Widget child = TexMarkdown(
_controller.text,
textDirection: _direction,
onLinkTab: (url, title) {
debugPrint(url);
debugPrint(title);
},
textAlign: TextAlign.justify,
textScaler: const TextScaler.linear(1),
style: const TextStyle(
fontSize: 15,
),
latexWorkaround: (tex) {
List<String> stack = [];
tex = tex.splitMapJoin(
RegExp(r"
\\
text
\
{|
\
{|
\
}|
\
_"),
onMatch: (p) {
String input = p[0] ?? "";
if (input == r"
\t
ext{") {
stack.add(input);
}
if (input == r"}") {
stack.removeLast();
if (stack.isNotEmpty) {
if (input == r"{") {
stack.add(input);
}
if (input == r"}") {
stack.removeLast();
}
if (input == r"_") {
return r"
\
_";
}
}
if (input == r"_") {
return r"
\
_";
}
}
return input;
},
);
return tex.replaceAllMapped(
RegExp(r"align
\
*"),
(match) => "aligned");
},
latexBuilder:
(contex, tex, textStyle, inline) {
if (tex.contains(r"
\
begin{tabular}")) {
// return table.
String tableString = "|
${(RegExp(
r"^\\begin\{tabular\}
\
{.*?
\
}(.*?)
\\
end
\
{tabular
\
}
$
",
multiLine: true,
dotAll: true,
).firstMatch(tex)?[1] ?? "").trim()}|";
tableString = tableString
.replaceAll(r"
\\
", "|
\n
|")
.replaceAll(r"
\
hline", "")
.replaceAll(
RegExp(r"(?<!
\\
)&"), "|");
var tableStringList = tableString
.split("
\n
")
..insert(1, "|---|");
tableString =
tableStringList.join("
\n
");
return TexMarkdown(tableString);
}
var controller = ScrollController();
Widget child = Math.tex(
tex,
textStyle: textStyle,
);
if (!inline) {
child = Padding(
padding: const EdgeInsets.all(0.0),
child: Material(
color: Theme.of(context)
.colorScheme
.onInverseSurface,
child: Padding(
padding:
const EdgeInsets.all(8.0),
child: Scrollbar(
controller: controller,
child: SingleChildScrollView(
return input;
},
);
return tex.replaceAllMapped(
RegExp(r"align
\
*"),
(match) => "aligned");
},
latexBuilder:
(contex, tex, textStyle, inline) {
if (tex.contains(r"
\
begin{tabular}")) {
// return table.
String tableString = "|
${(RegExp(
r"^\\begin\{tabular\}
\
{.*?
\
}(.*?)
\\
end
\
{tabular
\
}
$
",
multiLine: true,
dotAll: true,
).firstMatch(tex)?[1] ?? "").trim()}|";
tableString = tableString
.replaceAll(r"
\\
", "|
\n
|")
.replaceAll(r"
\
hline", "")
.replaceAll(
RegExp(r"(?<!
\\
)&"), "|");
var tableStringList = tableString
.split("
\n
")
..insert(1, "|---|");
tableString =
tableStringList.join("
\n
");
return TexMarkdown(tableString);
}
var controller = ScrollController();
Widget child = Math.tex(
tex,
textStyle: textStyle,
);
if (!inline) {
child = Padding(
padding: const EdgeInsets.all(0.0),
child: Material(
color: Theme.of(context)
.colorScheme
.onInverseSurface,
child: Padding(
padding:
const EdgeInsets.all(8.0),
child: Scrollbar(
controller: controller,
scrollDirection:
Axis.horizontal,
child: Math.tex(
tex,
textStyle: textStyle,
child: SingleChildScrollView(
controller: controller,
scrollDirection:
Axis.horizontal,
child: Math.tex(
tex,
textStyle: textStyle,
),
),
),
),
),
);
}
child = SelectableAdapter(
selectedText: tex,
child: Math.tex(tex),
);
child = InkWell(
onTap: () {
debugPrint("Hello world");
},
child: child,
);
return child;
},
sourceTagBuilder:
(buildContext, string, textStyle) {
var value = int.tryParse(string);
value ??= -1;
value += 1;
return SizedBox(
height: 20,
width: 20,
child: Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius:
BorderRadius.circular(10),
),
child:
Center(child: Text("
$value
")),
),
);
}
child = InkWell(
onTap: () {
debugPrint("Hello world");
},
},
);
if (selectable) {
child = SelectionArea(
child: child,
);
return child;
},
sourceTagBuilder:
(buildContext, string, textStyle) {
var value = int.tryParse(string);
value ??= -1;
value += 1;
return SizedBox(
height: 20,
width: 20,
child: Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius:
BorderRadius.circular(10),
),
child: Center(child: Text("
$value
")),
),
);
},
);
if (selectable) {
child = SelectionArea(child: child);
}
return child;
},
}
return child;
},
),
// child: const Text("Hello"),
),
// child: const Text("Hello"),
),
);
},
),
],
);
},
),
],
),
),
),
if (writingMod)
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 200),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
label: Text("Type here:")),
maxLines: null,
controller: _controller,
if (writingMod)
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 200),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
label: Text("Type here:")),
maxLines: null,
controller: _controller,
),
),
),
),
],
),
],
],
),
],
),
),
),
);
...
...
gpt_markdown/example/lib/selectable_adapter.dart
0 → 100644
View file @
c3dc6e6
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
class
SelectableAdapter
extends
StatelessWidget
{
const
SelectableAdapter
(
{
super
.
key
,
required
this
.
selectedText
,
required
this
.
child
});
final
Widget
child
;
final
String
selectedText
;
@override
Widget
build
(
BuildContext
context
)
{
final
SelectionRegistrar
?
registrar
=
SelectionContainer
.
maybeOf
(
context
);
if
(
registrar
==
null
)
{
return
child
;
}
return
MouseRegion
(
cursor:
SystemMouseCursors
.
text
,
child:
_SelectableAdapter
(
registrar:
registrar
,
selectedText:
selectedText
,
child:
child
,
),
);
}
}
class
_SelectableAdapter
extends
SingleChildRenderObjectWidget
{
const
_SelectableAdapter
({
required
this
.
registrar
,
required
Widget
child
,
required
this
.
selectedText
,
})
:
super
(
child:
child
);
final
SelectionRegistrar
registrar
;
final
String
selectedText
;
@override
_RenderSelectableAdapter
createRenderObject
(
BuildContext
context
)
{
return
_RenderSelectableAdapter
(
DefaultSelectionStyle
.
of
(
context
).
selectionColor
!,
selectedText
,
registrar
,
);
}
@override
void
updateRenderObject
(
BuildContext
context
,
_RenderSelectableAdapter
renderObject
)
{
renderObject
..
selectionColor
=
DefaultSelectionStyle
.
of
(
context
).
selectionColor
!
..
registrar
=
registrar
;
}
}
class
_RenderSelectableAdapter
extends
RenderProxyBox
with
Selectable
,
SelectionRegistrant
{
String
selectionText
;
_RenderSelectableAdapter
(
Color
selectionColor
,
this
.
selectionText
,
SelectionRegistrar
registrar
,
)
:
_selectionColor
=
selectionColor
,
_geometry
=
ValueNotifier
<
SelectionGeometry
>(
_noSelection
)
{
this
.
registrar
=
registrar
;
_geometry
.
addListener
(
markNeedsPaint
);
}
static
const
SelectionGeometry
_noSelection
=
SelectionGeometry
(
status:
SelectionStatus
.
none
,
hasContent:
true
);
final
ValueNotifier
<
SelectionGeometry
>
_geometry
;
Color
get
selectionColor
=>
_selectionColor
;
late
Color
_selectionColor
;
set
selectionColor
(
Color
value
)
{
if
(
_selectionColor
==
value
)
{
return
;
}
_selectionColor
=
value
;
markNeedsPaint
();
}
// ValueListenable APIs
@override
void
addListener
(
VoidCallback
listener
)
=>
_geometry
.
addListener
(
listener
);
@override
void
removeListener
(
VoidCallback
listener
)
=>
_geometry
.
removeListener
(
listener
);
@override
SelectionGeometry
get
value
=>
_geometry
.
value
;
// Selectable APIs.
@override
List
<
Rect
>
get
boundingBoxes
=>
<
Rect
>[
paintBounds
];
// Adjust this value to enlarge or shrink the selection highlight.
static
const
double
_padding
=
0.0
;
Rect
_getSelectionHighlightRect
()
{
return
Rect
.
fromLTWH
(
0
-
_padding
,
0
-
_padding
,
size
.
width
+
_padding
*
2
,
size
.
height
+
_padding
*
2
);
}
Offset
?
_start
;
Offset
?
_end
;
void
_updateGeometry
()
{
if
(
_start
==
null
||
_end
==
null
)
{
_geometry
.
value
=
_noSelection
;
return
;
}
final
Rect
renderObjectRect
=
Rect
.
fromLTWH
(
0
,
0
,
size
.
width
,
size
.
height
);
final
Rect
selectionRect
=
Rect
.
fromPoints
(
_start
!,
_end
!);
if
(
renderObjectRect
.
intersect
(
selectionRect
).
isEmpty
)
{
_geometry
.
value
=
_noSelection
;
}
else
{
final
Rect
selectionRect
=
_getSelectionHighlightRect
();
final
SelectionPoint
firstSelectionPoint
=
SelectionPoint
(
localPosition:
selectionRect
.
bottomLeft
,
lineHeight:
selectionRect
.
size
.
height
,
handleType:
TextSelectionHandleType
.
left
,
);
final
SelectionPoint
secondSelectionPoint
=
SelectionPoint
(
localPosition:
selectionRect
.
bottomRight
,
lineHeight:
selectionRect
.
size
.
height
,
handleType:
TextSelectionHandleType
.
right
,
);
final
bool
isReversed
;
if
(
_start
!.
dy
>
_end
!.
dy
)
{
isReversed
=
true
;
}
else
if
(
_start
!.
dy
<
_end
!.
dy
)
{
isReversed
=
false
;
}
else
{
isReversed
=
_start
!.
dx
>
_end
!.
dx
;
}
_geometry
.
value
=
SelectionGeometry
(
status:
SelectionStatus
.
uncollapsed
,
hasContent:
true
,
startSelectionPoint:
isReversed
?
secondSelectionPoint
:
firstSelectionPoint
,
endSelectionPoint:
isReversed
?
firstSelectionPoint
:
secondSelectionPoint
,
selectionRects:
<
Rect
>[
selectionRect
],
);
}
}
@override
SelectionResult
dispatchSelectionEvent
(
SelectionEvent
event
)
{
SelectionResult
result
=
SelectionResult
.
none
;
switch
(
event
.
type
)
{
case
SelectionEventType
.
startEdgeUpdate
:
case
SelectionEventType
.
endEdgeUpdate
:
final
Rect
renderObjectRect
=
Rect
.
fromLTWH
(
0
,
0
,
size
.
width
,
size
.
height
);
// Normalize offset in case it is out side of the rect.
final
Offset
point
=
globalToLocal
((
event
as
SelectionEdgeUpdateEvent
).
globalPosition
);
final
Offset
adjustedPoint
=
SelectionUtils
.
adjustDragOffset
(
renderObjectRect
,
point
);
if
(
event
.
type
==
SelectionEventType
.
startEdgeUpdate
)
{
_start
=
adjustedPoint
;
}
else
{
_end
=
adjustedPoint
;
}
result
=
SelectionUtils
.
getResultBasedOnRect
(
renderObjectRect
,
point
);
case
SelectionEventType
.
clear
:
_start
=
_end
=
null
;
case
SelectionEventType
.
selectAll
:
case
SelectionEventType
.
selectWord
:
case
SelectionEventType
.
selectParagraph
:
_start
=
Offset
.
zero
;
_end
=
Offset
.
infinite
;
case
SelectionEventType
.
granularlyExtendSelection
:
result
=
SelectionResult
.
end
;
final
GranularlyExtendSelectionEvent
extendSelectionEvent
=
event
as
GranularlyExtendSelectionEvent
;
// Initialize the offset it there is no ongoing selection.
if
(
_start
==
null
||
_end
==
null
)
{
if
(
extendSelectionEvent
.
forward
)
{
_start
=
_end
=
Offset
.
zero
;
}
else
{
_start
=
_end
=
Offset
.
infinite
;
}
}
// Move the corresponding selection edge.
final
Offset
newOffset
=
extendSelectionEvent
.
forward
?
Offset
.
infinite
:
Offset
.
zero
;
if
(
extendSelectionEvent
.
isEnd
)
{
if
(
newOffset
==
_end
)
{
result
=
extendSelectionEvent
.
forward
?
SelectionResult
.
next
:
SelectionResult
.
previous
;
}
_end
=
newOffset
;
}
else
{
if
(
newOffset
==
_start
)
{
result
=
extendSelectionEvent
.
forward
?
SelectionResult
.
next
:
SelectionResult
.
previous
;
}
_start
=
newOffset
;
}
case
SelectionEventType
.
directionallyExtendSelection
:
result
=
SelectionResult
.
end
;
final
DirectionallyExtendSelectionEvent
extendSelectionEvent
=
event
as
DirectionallyExtendSelectionEvent
;
// Convert to local coordinates.
final
double
horizontalBaseLine
=
globalToLocal
(
Offset
(
event
.
dx
,
0
)).
dx
;
final
Offset
newOffset
;
final
bool
forward
;
switch
(
extendSelectionEvent
.
direction
)
{
case
SelectionExtendDirection
.
backward
:
case
SelectionExtendDirection
.
previousLine
:
forward
=
false
;
// Initialize the offset it there is no ongoing selection.
if
(
_start
==
null
||
_end
==
null
)
{
_start
=
_end
=
Offset
.
infinite
;
}
// Move the corresponding selection edge.
if
(
extendSelectionEvent
.
direction
==
SelectionExtendDirection
.
previousLine
||
horizontalBaseLine
<
0
)
{
newOffset
=
Offset
.
zero
;
}
else
{
newOffset
=
Offset
.
infinite
;
}
case
SelectionExtendDirection
.
nextLine
:
case
SelectionExtendDirection
.
forward
:
forward
=
true
;
// Initialize the offset it there is no ongoing selection.
if
(
_start
==
null
||
_end
==
null
)
{
_start
=
_end
=
Offset
.
zero
;
}
// Move the corresponding selection edge.
if
(
extendSelectionEvent
.
direction
==
SelectionExtendDirection
.
nextLine
||
horizontalBaseLine
>
size
.
width
)
{
newOffset
=
Offset
.
infinite
;
}
else
{
newOffset
=
Offset
.
zero
;
}
}
if
(
extendSelectionEvent
.
isEnd
)
{
if
(
newOffset
==
_end
)
{
result
=
forward
?
SelectionResult
.
next
:
SelectionResult
.
previous
;
}
_end
=
newOffset
;
}
else
{
if
(
newOffset
==
_start
)
{
result
=
forward
?
SelectionResult
.
next
:
SelectionResult
.
previous
;
}
_start
=
newOffset
;
}
}
_updateGeometry
();
return
result
;
}
// This method is called when users want to copy selected content in this
// widget into clipboard.
@override
SelectedContent
?
getSelectedContent
()
{
return
value
.
hasSelection
?
SelectedContent
(
plainText:
selectionText
)
:
null
;
}
LayerLink
?
_startHandle
;
LayerLink
?
_endHandle
;
@override
void
pushHandleLayers
(
LayerLink
?
startHandle
,
LayerLink
?
endHandle
)
{
if
(
_startHandle
==
startHandle
&&
_endHandle
==
endHandle
)
{
return
;
}
_startHandle
=
startHandle
;
_endHandle
=
endHandle
;
markNeedsPaint
();
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
super
.
paint
(
context
,
offset
);
if
(!
_geometry
.
value
.
hasSelection
)
{
return
;
}
// Draw the selection highlight.
final
Paint
selectionPaint
=
Paint
()
..
style
=
PaintingStyle
.
fill
..
color
=
_selectionColor
;
context
.
canvas
.
drawRect
(
_getSelectionHighlightRect
().
shift
(
offset
),
selectionPaint
);
// Push the layer links if any.
if
(
_startHandle
!=
null
)
{
context
.
pushLayer
(
LeaderLayer
(
link:
_startHandle
!,
offset:
offset
+
value
.
startSelectionPoint
!.
localPosition
,
),
(
PaintingContext
context
,
Offset
offset
)
{},
Offset
.
zero
,
);
}
if
(
_endHandle
!=
null
)
{
context
.
pushLayer
(
LeaderLayer
(
link:
_endHandle
!,
offset:
offset
+
value
.
endSelectionPoint
!.
localPosition
,
),
(
PaintingContext
context
,
Offset
offset
)
{},
Offset
.
zero
,
);
}
}
@override
void
dispose
()
{
_geometry
.
dispose
();
super
.
dispose
();
}
}
...
...
gpt_markdown/example/pubspec.lock
View file @
c3dc6e6
...
...
@@ -126,7 +126,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.1.1
0
"
version: "0.1.1
1
"
http:
dependency: transitive
description:
...
...
@@ -373,5 +373,5 @@ packages:
source: hosted
version: "6.5.0"
sdks:
dart: ">=3.
3
.0 <4.0.0"
dart: ">=3.
5
.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
...
...
gpt_markdown/example/pubspec.yaml
View file @
c3dc6e6
...
...
@@ -5,7 +5,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version
:
1.0.0+1
environment
:
sdk
:
'
>=
2.18.6
<4.0.0'
sdk
:
'
>=
3.5.0
<4.0.0'
dependencies
:
flutter
:
sdk
:
flutter
...
...
gpt_markdown/lib/markdown_component.dart
View file @
c3dc6e6
...
...
@@ -7,6 +7,7 @@ import 'package:gpt_markdown/custom_widgets/custom_error_image.dart';
import
'package:gpt_markdown/custom_widgets/custom_rb_cb.dart'
;
import
'package:gpt_markdown/custom_widgets/markdow_config.dart'
;
import
'package:gpt_markdown/custom_widgets/unordered_ordered_list.dart'
;
import
'package:gpt_markdown/theme.dart'
;
import
'md_widget.dart'
;
/// Markdown components
...
...
@@ -413,14 +414,14 @@ class HighlightedText extends InlineMd {
style:
config
.
style
?.
copyWith
(
fontWeight:
FontWeight
.
bold
,
background:
Paint
()
..
color
=
Theme
.
of
(
context
).
colorScheme
.
onInverseSurface
..
color
=
GptMarkdownTheme
.
of
(
context
).
highlightColor
..
strokeCap
=
StrokeCap
.
round
..
strokeJoin
=
StrokeJoin
.
round
,
)
??
TextStyle
(
fontWeight:
FontWeight
.
bold
,
background:
Paint
()
..
color
=
Theme
.
of
(
context
).
colorScheme
.
surfaceContainerHighest
..
color
=
GptMarkdownTheme
.
of
(
context
).
highlightColor
..
strokeCap
=
StrokeCap
.
round
..
strokeJoin
=
StrokeJoin
.
round
,
),
...
...
gpt_markdown/lib/theme.dart
0 → 100644
View file @
c3dc6e6
import
'package:flutter/material.dart'
;
/// Theme defined for `TexMarkdown` widget
class
GptMarkdownThemeData
extends
ThemeExtension
<
GptMarkdownThemeData
>
{
GptMarkdownThemeData
({
required
this
.
highlightColor
,
});
/// Define default attributes.
factory
GptMarkdownThemeData
.
from
(
BuildContext
context
)
{
return
GptMarkdownThemeData
(
highlightColor:
Theme
.
of
(
context
).
colorScheme
.
onInverseSurface
,
);
}
Color
highlightColor
;
@override
GptMarkdownThemeData
copyWith
({
Color
?
highlightColor
})
{
return
GptMarkdownThemeData
(
highlightColor:
highlightColor
??
this
.
highlightColor
,
);
}
@override
GptMarkdownThemeData
lerp
(
GptMarkdownThemeData
?
other
,
double
t
)
{
if
(
other
==
null
)
{
return
this
;
}
return
GptMarkdownThemeData
(
highlightColor:
Color
.
lerp
(
highlightColor
,
other
.
highlightColor
,
t
)
??
highlightColor
,
);
}
}
/// Wrap a `Widget` with `GptMarkdownTheme` to provide `GptMarkdownThemeData` in your intiar app.
class
GptMarkdownTheme
extends
InheritedWidget
{
const
GptMarkdownTheme
({
super
.
key
,
required
this
.
gptThemeData
,
required
super
.
child
,
});
final
GptMarkdownThemeData
gptThemeData
;
static
GptMarkdownThemeData
of
(
BuildContext
context
)
{
final
provider
=
context
.
dependOnInheritedWidgetOfExactType
<
GptMarkdownTheme
>();
if
(
provider
!=
null
)
{
return
provider
.
gptThemeData
;
}
final
themeData
=
Theme
.
of
(
context
).
extension
<
GptMarkdownThemeData
>();
if
(
themeData
!=
null
)
{
return
themeData
;
}
return
GptMarkdownThemeData
.
from
(
context
);
}
@override
bool
updateShouldNotify
(
GptMarkdownTheme
oldWidget
)
{
return
gptThemeData
!=
oldWidget
.
gptThemeData
;
}
}
...
...
gpt_markdown/pubspec.yaml
View file @
c3dc6e6
name
:
gpt_markdown
description
:
"
The
purpose
of
this
package
is
to
render
the
response
of
ChatGPT
into
a
Flutter
app."
version
:
0.1.1
0
version
:
0.1.1
1
homepage
:
https://github.com/saminsohag/flutter_packages/tree/main/gpt_markdown
environment
:
...
...
Please
register
or
login
to post a comment