Attach any file, take shares from other apps, and survive a backwards highlight
Attachments were images only. Now any file can be attached: from the file chooser behind the "+" menu, or from Android's share sheet, which the app is now in. An image still goes to the model as a picture; anything else is stored under its own name (`<hex>-<name>`, cleaned by `safe_file_name`) and the Claude driver ends the message with `Attached file: /abs/path`, since the CLI reads files by path and a model cannot be shown a trace. The user-message field is renamed `images` -> `attachments` on both sides, with a serde alias reading the rows written before. A share arrives before anyone has said which session it is for, so it is held in AppRoot with a banner on the list until a session takes it; an open session takes it at once. Unreadable shares are reported beside the composer, not thrown. The tool card crashed the app when opened on a command holding a quoted glob such as `-path '*/.git/*'`: highlights 1.1.0's shell lexer answers `x '*/a/*'` with a span whose end is before its start, and AnnotatedString refuses the range. Such spans are dropped; the library is the place for the fix. The echo driver gains `/bash <command>` so a card with a given command can be produced on the emulator. ui-sandbox.sh's token salvage read the tokens block's close only at a line start, ran past the compact `),],` the server writes, and copied `setups` into the new config twice, which the server then refused. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
4bc69e8f9c
commit
6180663f14
25 files changed
+672
-165
No files matched your search
@@ -0,0 +1,66 @@
|
||||
package com.example.aiapp
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* Whether [ref] names an image the server stored as one -- `<hex>.<extension>`, with an extension
|
||||
* from the list it writes -- rather than a file kept under its own name. Mirrors the server's
|
||||
* `media` table, which is the one other place the list lives.
|
||||
*/
|
||||
fun isImageRef(ref: String): Boolean = ref.substringAfterLast('.', "") in IMAGE_EXTENSIONS
|
||||
|
||||
private val IMAGE_EXTENSIONS = setOf("png", "jpg", "gif", "webp")
|
||||
|
||||
/**
|
||||
* The name a file was attached under: the ref less the hex the server put before it. The hex has no
|
||||
* dash in it, so the first one is the boundary however many the name has.
|
||||
*/
|
||||
fun attachmentName(ref: String): String = ref.substringAfter('-', ref)
|
||||
|
||||
/**
|
||||
* One attachment on a sent message, drawn as what it is: an image inline, a file as its name. A
|
||||
* file is not fetched -- there is nothing on this phone to open a trace or a log with -- so the
|
||||
* name is the whole of it.
|
||||
*/
|
||||
@Composable
|
||||
fun Attachment(
|
||||
settings: ServerSettings,
|
||||
sessionId: String,
|
||||
ref: String,
|
||||
onOpenImage: (String) -> Unit,
|
||||
) {
|
||||
if (isImageRef(ref)) SessionImage(settings, sessionId, ref, onOpenImage)
|
||||
else
|
||||
FileName(
|
||||
attachmentName(ref),
|
||||
Modifier.clip(MaterialTheme.shapes.extraSmall)
|
||||
.background(rawSurface)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A file's name, one line, in the face names are read in. Overlong names lose their middle: a name
|
||||
* is identified by both ends -- what it is at the front, what kind at the back -- and either
|
||||
* ellipsis alone takes away one of them.
|
||||
*/
|
||||
@Composable
|
||||
fun FileName(name: String, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
name,
|
||||
modifier = modifier,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.MiddleEllipsis,
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user