@@ -7,20 +7,39 @@ import android.os.VibrationAttributes
77import android.os.VibrationEffect
88import android.os.Vibrator
99import android.widget.Toast
10+ import androidx.compose.animation.core.LinearOutSlowInEasing
11+ import androidx.compose.animation.core.animateFloat
12+ import androidx.compose.animation.core.tween
13+ import androidx.compose.animation.core.updateTransition
14+ import androidx.compose.foundation.background
15+ import androidx.compose.foundation.border
1016import androidx.compose.foundation.clickable
17+ import androidx.compose.foundation.layout.Box
1118import androidx.compose.foundation.layout.Column
1219import androidx.compose.foundation.layout.Row
1320import androidx.compose.foundation.layout.fillMaxWidth
21+ import androidx.compose.foundation.layout.heightIn
1422import androidx.compose.foundation.layout.padding
23+ import androidx.compose.foundation.layout.width
1524import androidx.compose.foundation.shape.RoundedCornerShape
25+ import androidx.compose.foundation.text.BasicTextField
26+ import androidx.compose.foundation.text.KeyboardOptions
27+ import androidx.compose.foundation.text.selection.LocalTextSelectionColors
28+ import androidx.compose.foundation.text.selection.TextSelectionColors
29+ import androidx.compose.material3.LocalTextStyle
1630import androidx.compose.material3.MaterialTheme
1731import androidx.compose.material3.Surface
1832import androidx.compose.material3.Text
1933import androidx.compose.runtime.Composable
34+ import androidx.compose.runtime.CompositionLocalProvider
35+ import androidx.compose.runtime.getValue
2036import androidx.compose.runtime.remember
2137import androidx.compose.ui.Alignment
2238import androidx.compose.ui.Modifier
39+ import androidx.compose.ui.draw.shadow
2340import androidx.compose.ui.graphics.Color
41+ import androidx.compose.ui.graphics.SolidColor
42+ import androidx.compose.ui.graphics.graphicsLayer
2443import androidx.compose.ui.text.AnnotatedString
2544import androidx.compose.ui.text.SpanStyle
2645import androidx.compose.ui.text.buildAnnotatedString
@@ -29,8 +48,11 @@ import androidx.compose.ui.text.font.FontStyle
2948import androidx.compose.ui.text.font.FontWeight
3049import androidx.compose.ui.text.style.TextDecoration
3150import androidx.compose.ui.unit.Dp
51+ import androidx.compose.ui.unit.IntOffset
3252import androidx.compose.ui.unit.dp
3353import androidx.compose.ui.unit.sp
54+ import androidx.compose.ui.window.Popup
55+ import androidx.compose.ui.window.PopupProperties
3456import androidx.lifecycle.viewModelScope
3557import com.google.gson.Gson
3658import com.google.gson.JsonParser
@@ -61,6 +83,97 @@ fun clickVibrate(vibrator: Vibrator){
6183 }
6284}
6385
86+ @Composable
87+ fun FreeDropdownMenu (expanded : Boolean ,
88+ onDismissRequest : () -> Unit ,
89+ offset : IntOffset = IntOffset (0,0),
90+ width : Dp = 150.dp,
91+ content : @Composable (() -> Unit )) {
92+ if (expanded) {
93+ Popup (
94+ alignment = Alignment .TopStart ,
95+ offset = offset,
96+ onDismissRequest = onDismissRequest,
97+ properties = PopupProperties (focusable = true ),
98+ ) {
99+ val transition = updateTransition(targetState = expanded, label = " menuTransition" )
100+ val scale by transition.animateFloat(
101+ transitionSpec = { tween(durationMillis = 120 , easing = LinearOutSlowInEasing ) },
102+ label = " scale"
103+ ) { if (it) 1f else 0.8f }
104+
105+ val alpha by transition.animateFloat(
106+ transitionSpec = { tween(durationMillis = 120 ) },
107+ label = " alpha"
108+ ) { if (it) 1f else 0f }
109+ Column (
110+ modifier = Modifier
111+ .graphicsLayer {
112+ scaleX = scale
113+ scaleY = scale
114+ this .alpha = alpha
115+ }
116+ .shadow(
117+ elevation = 8 .dp,
118+ shape = MaterialTheme .shapes.extraSmall
119+ )
120+ .background(
121+ color = MaterialTheme .colorScheme.surfaceContainer,
122+ shape = MaterialTheme .shapes.extraSmall
123+ )
124+ .border(
125+ width = 1 .dp,
126+ color = MaterialTheme .colorScheme.outlineVariant,
127+ shape = MaterialTheme .shapes.extraSmall
128+ )
129+ .width(width)
130+ ) { content() }
131+ }
132+ }
133+ }
134+
135+ @Composable
136+ fun SmallTextField (value : String , onValueChange : (String ) -> Unit , modifier : Modifier = Modifier , placeholder : @Composable (() -> Unit )? = null,keyboardOptions : KeyboardOptions = KeyboardOptions .Default ,showFrame : Boolean =true,showHandle : Boolean =true) {
137+ val selectionColors = TextSelectionColors (
138+ handleColor = if (showHandle){MaterialTheme .colorScheme.primary}else {Color .Transparent },
139+ backgroundColor = MaterialTheme .colorScheme.primary
140+ )
141+ CompositionLocalProvider (LocalTextSelectionColors provides selectionColors) {
142+ BasicTextField (
143+ value = value,
144+ onValueChange = onValueChange,
145+ modifier = modifier
146+ .heightIn(min = 36 .dp),
147+ textStyle = LocalTextStyle .current.copy(
148+ color = MaterialTheme .colorScheme.onSurface
149+ ),
150+ cursorBrush = SolidColor (MaterialTheme .colorScheme.primary),
151+ maxLines = 7 ,
152+ keyboardOptions = keyboardOptions,
153+ decorationBox = { innerTextField ->
154+ Box (
155+ modifier = if (showFrame) {
156+ Modifier
157+ .border(
158+ width = 1 .dp,
159+ color = MaterialTheme .colorScheme.outline,
160+ shape = RoundedCornerShape (18 .dp)
161+ )
162+ .padding(vertical = 5 .dp, horizontal = 10 .dp)
163+ } else {
164+ modifier.padding(vertical = 5 .dp, horizontal = 10 .dp)
165+ }
166+ ) {
167+ if (value.isEmpty() && placeholder != null ) {
168+ placeholder()
169+ }
170+ innerTextField()
171+ }
172+ }
173+ )
174+ }
175+ }
176+
64177class MarkdownParser {
65178 // 代码块
66179 private val codeBlockRegex = """ ```(.*?)\n([\s\S]*?)\s*```""" .toRegex()
@@ -259,7 +372,7 @@ class MarkdownParser {
259372 val cleanText = text.replace(" *" , " \u200B " ).replace(" `" ," \u200B " ).replace(" ~" ," \u200B " )
260373 append(cleanText)
261374
262- // 处理粗体
375+ // 粗体
263376 boldRegex.findAll(text).forEach { result ->
264377 addStyle(
265378 SpanStyle (fontWeight = FontWeight .ExtraBold ),
@@ -268,7 +381,7 @@ class MarkdownParser {
268381 )
269382 }
270383
271- // 处理斜体
384+ // 斜体
272385 italicRegex.findAll(text).forEach { result ->
273386 addStyle(
274387 SpanStyle (fontStyle = FontStyle .Italic ),
@@ -277,7 +390,7 @@ class MarkdownParser {
277390 )
278391 }
279392
280- // 处理删除线
393+ // 删除线
281394 deleteRegex.findAll(text).forEach { result ->
282395 addStyle(
283396 SpanStyle (textDecoration = TextDecoration .LineThrough ),
@@ -286,7 +399,7 @@ class MarkdownParser {
286399 )
287400 }
288401
289- // 处理代码样式
402+ // 代码
290403 codeRegex.findAll(text).forEach { result ->
291404 addStyle(
292405 SpanStyle (
@@ -324,7 +437,7 @@ fun createNewSession(viewModel: ChatViewModel,context: Context,isInNewSessionChe
324437 } else {
325438 viewModel.msgs.clear()
326439 viewModel.addSystemMessage(systemPrompt)
327- if (viewModel.sessions.last().startsWith(" 新对话" ) && viewModel.sessions.last().endsWith(" \u200B " )){
440+ if (viewModel.sessions.isNotEmpty() && viewModel.sessions. last().startsWith(" 新对话" ) && viewModel.sessions.last().endsWith(" \u200B " )){
328441 viewModel.currentSession = viewModel.sessions.last()
329442 }else {
330443 viewModel.currentSession = " 新对话" + System .currentTimeMillis().toString() + " \u200B "
@@ -340,22 +453,21 @@ fun send(
340453) {
341454 viewModel.isLoading = true
342455 cancel = false
343- viewModel.updateAIMessage(" " )
344456 val client = OkHttpClient .Builder ()
345457 .readTimeout(0 , TimeUnit .SECONDS )
346458 .build()
347459
348460 val bodyMap = mutableMapOf (
349461 " model" to model,
350- " messages" to viewModel.withoutReasoning (),
462+ " messages" to viewModel.msgsToSend (),
351463 " stream" to true
352464 )
353465 bodyMap.apply {
354466 if (viewModel.temperature >= 0 ) {
355467 put(" temperature" , viewModel.temperature.toFloat() / 10 )
356468 }
357469 if (viewModel.maxTokensIsNumber()) {
358- put(" max_tokens" , viewModel.maxTokens.toInt ())
470+ put(" max_tokens" , viewModel.maxTokens.toLong ())
359471 }
360472 }
361473 val requestBody = Gson ().toJson(bodyMap)
@@ -367,6 +479,7 @@ fun send(
367479 .addHeader(" Authorization" , " Bearer $api_key " )
368480 .build()
369481
482+ viewModel.updateAIMessage(" " )
370483 viewModel.viewModelScope.launch(Dispatchers .IO ) {
371484 try {
372485 val response = client.newCall(request).execute()
0 commit comments